diff options
Diffstat (limited to 'libs')
132 files changed, 31850 insertions, 0 deletions
diff --git a/libs/libaxolotl/LICENSE b/libs/libaxolotl/LICENSE new file mode 100644 index 0000000000..94a0453222 --- /dev/null +++ b/libs/libaxolotl/LICENSE @@ -0,0 +1,621 @@ + 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 diff --git a/libs/libaxolotl/README.md b/libs/libaxolotl/README.md new file mode 100644 index 0000000000..09a954f434 --- /dev/null +++ b/libs/libaxolotl/README.md @@ -0,0 +1,205 @@ +# Overview + +This is a ratcheting forward secrecy protocol that works in synchronous and asynchronous messaging +environments. See the [Java library](https://github.com/whispersystems/libaxolotl-java) for more details. + +# Building libaxolotl-c + +## Development host setup + +### Build dependencies + +* [CMake](http://www.cmake.org/) 2.8.4 or higher +* [Check *1](http://check.sourceforge.net/) +* [OpenSSL *1](https://www.openssl.org/) 1.0 or higher +* [LCOV *2](http://ltp.sourceforge.net/coverage/lcov.php) + +Most of these dependencies are required just for the unit test suite and +development of the library itself. When integrating into actual applications, +you should not need anything beyond CMake. Alternatively, you may integrate +the code using a build system of your choice. +Items marked with *1 are required for tests, with *2 are additionally required for code coverage. + +### Setting up a fresh source tree + + $ cd /path/to/libaxolotl-c + $ mkdir build + $ cd build + $ cmake -DCMAKE_BUILD_TYPE=Debug .. + $ make + +### Running the unit tests + + $ cd /path/to/libaxolotl-c/build + $ cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=1 .. + $ ctest + +### Creating the code coverage report + + $ cd /path/to/libaxolotl-c/build + $ cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=1 -DCOVERAGE=1 .. + $ make coverage + +The generated code coverage report can be found in: +`/path/to/libaxolotl-c/build/coverage` + +### Eclipse project setup + +CMake provides a tutorial on Eclipse project setup here: +http://www.cmake.org/Wiki/CMake:Eclipse_UNIX_Tutorial + +It is recommended to follow the more manual "Option 2," since the Eclipse +project generator built into CMake tends to be outdated and leads you toward +a very awkward and occasionally broken project configuration. + +### Protocol Buffers compiler + +This project uses serialization code based on [Protocol Buffers](https://code.google.com/p/protobuf/). +Since the official library does not support C, the [protobuf-c](https://github.com/protobuf-c/protobuf-c) +generator is used instead. For the sake of convenience, the generated code and its dependencies are +included in the source tree. The generated code can be regenerated at any time by installing the two +mentioned packages and running "make" in the "protobuf/" subdirectory. + +## Target platforms + +CMake toolchain files have been included from the following sources: + +* [iOS](https://code.google.com/p/ios-cmake/) +* [BlackBerry 10](https://github.com/blackberry/OGRE/blob/master/src/CMake/toolchain/blackberry.toolchain.cmake) + +# Using libaxolotl-c + +## Library initialization + +Before using the library, a libaxolotl-c client needs to initialize a global +context. This global context is used to provide callbacks for implementations +of functions used across the library that need client-specific implementations. +Refer to "axolotl.h" for detailed documentation on these functions, and the unit +tests for example implementations. + + axolotl_context *global_context; + axolotl_context_create(&global_context, user_data); + axolotl_context_set_crypto_provider(global_context, &provider); + axolotl_context_set_locking_functions(global_context, lock_function, unlock_function); + +## Client install time + +At install time, a libaxolotl-c client needs to generate its identity keys, +registration id, and prekeys. + + ratchet_identity_key_pair *identity_key_pair; + uint32_t registration_id; + axolotl_key_helper_pre_key_list_node *pre_keys_head; + session_pre_key *last_resort_key; + session_signed_pre_key *signed_pre_key; + + axolotl_key_helper_generate_identity_key_pair(&identity_key_pair, global_context); + axolotl_key_helper_generate_registration_id(®istration_id, 0, global_context); + axolotl_key_helper_generate_pre_keys(&pre_keys_head, start_id, 100, global_context); + axolotl_key_helper_generate_last_resort_pre_key(&last_resort_key, global_context); + axolotl_key_helper_generate_signed_pre_key(&signed_pre_key, identity_key_pair, 5, timestamp, global_context); + + /* Store identity_key_pair somewhere durable and safe. */ + /* Store registration_id somewhere durable and safe. */ + + /* Store pre keys in the pre key store. */ + /* Store signed pre key in the signed pre key store. */ + +The above example is simplified for the sake of clarity. All of these functions return errors +on failure, and those errors should be checked for in real usage. + +There are also iteration and serialization methods for the above types that should +be used as appropriate. + +## Building a session + +A libaxolotl-c client needs to implement four data store callback interfaces: +`axolotl_identity_key_store`, `axolotl_pre_key_store`, +`axolotl_signed_pre_key_store`, and `axolotl_session_store`. +These will manage loading and storing of identity, prekeys, signed prekeys, +and session state. + +These callback interfaces are designed such that implementations should treat +all data flowing through them as opaque binary blobs. Anything necessary for +referencing that data will be provided as separate function arguments to those +callbacks. If it is ever necessary for clients to directly access stored data +in terms of library data structures, they should use the accessor functions +declared in "axolotl.h" for these data stores. + +Once the callbacks for these data stores are implemented, building a session +is fairly straightforward: + + /* Create the data store context, and add all the callbacks to it */ + axolotl_store_context *store_context; + axolotl_store_context_create(&store_context, context); + axolotl_store_context_set_session_store(store_context, &session_store); + axolotl_store_context_set_pre_key_store(store_context, &pre_key_store); + axolotl_store_context_set_signed_pre_key_store(store_context, &signed_pre_key_store); + axolotl_store_context_set_identity_key_store(store_context, &identity_key_store); + + /* Instantiate a session_builder for a recipient address. */ + axolotl_address address = { + "+14159998888", 12, 1 + }; + session_builder *builder; + session_builder_create(&builder, store_context, &address, global_context); + + /* Build a session with a pre key retrieved from the server. */ + session_builder_process_pre_key_bundle(builder, retrieved_pre_key); + + /* Create the session cipher and encrypt the message */ + session_cipher *cipher; + session_cipher_create(&cipher, store_context, &address, global_context); + + ciphertext_message *encrypted_message; + session_cipher_encrypt(cipher, message, message_len, &encrypted_message); + + /* Get the serialized content and deliver it */ + axolotl_buffer *serialized = ciphertext_message_get_serialized(encrypted_message); + + deliver(axolotl_buffer_data(serialized), axolotl_buffer_len(serialized)); + + /* Cleanup */ + AXOLOTL_UNREF(encrypted_message); + session_cipher_free(cipher); + session_builder_free(builder); + axolotl_store_context_destroy(store_context); + +The above example is simplified for the sake of clarity. All of these functions return errors +on failure, and those errors should be checked for in real usage. + +## Memory management notes + +For every custom data type that the libaxolotl-c library can allocate and +return, a corresponding way of deallocating an instance of that data type +is provided. + +The more basic and higher level data types provide a type-specific free or +destroy function. These types include `axolotl_context`, +`axolotl_store_context`, `axolotl_buffer`, `axolotl_buffer_list`, +`axolotl_int_list`, `axolotl_key_helper_pre_key_list_node`, `session_builder`, +`session_cipher`, `group_session_builder`, `group_cipher`, and +`fingerprint_generator`. + +Most of the other data types, including everything internal, use a reference +counting mechanism. If you are going to hold onto a reference to one of these +types, use the `AXOLOTL_REF(x)` macro to increment its count. If you are done +with a reference, use `AXOLOTL_UNREF(x)` to decrement its count. When the count +reaches 0, the type's destructor function is called. + +# Legal things +## Cryptography Notice + +This distribution includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software. +BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted. +See <http://www.wassenaar.org/> for more information. + +The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has classified this software as Export Commodity Control Number (ECCN) 5D002.C.1, which includes information security software using or performing cryptographic functions with asymmetric algorithms. +The form and manner of this distribution makes it eligible for export under the License Exception ENC Technology Software Unrestricted (TSU) exception (see the BIS Export Administration Regulations, Section 740.13) for both object code and source code. + +## License + +Copyright 2015 Open Whisper Systems + +Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html + diff --git a/libs/libaxolotl/libaxolotl.vcxproj b/libs/libaxolotl/libaxolotl.vcxproj new file mode 100644 index 0000000000..aeb45eb40b --- /dev/null +++ b/libs/libaxolotl/libaxolotl.vcxproj @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>libaxolotl</ProjectName>
+ <ProjectGuid>{620E0BE7-3763-4F35-9DBD-4770104E269C}</ProjectGuid>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(ProjectDir)..\..\build\vc.common\lib.props" />
+ </ImportGroup>
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <PreprocessorDefinitions>_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>.\;src\curve25519\ed25519;src\curve25519\ed25519\nacl_includes;src\curve25519\ed25519\additions\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4005;4127;4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="src\protobuf-c\*.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\curve25519\*.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\curve25519\ed25519\*.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\curve25519\ed25519\nacl_sha512\*.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\curve25519\ed25519\additions\*.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ </ItemGroup>
+</Project>
diff --git a/libs/libaxolotl/libaxolotl.vcxproj.filters b/libs/libaxolotl/libaxolotl.vcxproj.filters new file mode 100644 index 0000000000..de5ad9f66c --- /dev/null +++ b/libs/libaxolotl/libaxolotl.vcxproj.filters @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(ProjectDir)..\..\build\vc.common\common.filters" />
+</Project>
\ No newline at end of file diff --git a/libs/libaxolotl/src/CMakeLists.txt b/libs/libaxolotl/src/CMakeLists.txt new file mode 100644 index 0000000000..3900713add --- /dev/null +++ b/libs/libaxolotl/src/CMakeLists.txt @@ -0,0 +1,90 @@ +include_directories( + . + curve25519/ed25519/nacl_includes + curve25519/ed25519/additions + curve25519/ed25519/sha512 + curve25519/ed25519 + curve25519 +) + +set(protobuf_SRCS + LocalStorageProtocol.pb-c.c + WhisperTextProtocol.pb-c.c + FingerprintProtocol.pb-c.c +) + +set(axolotl_SRCS + vpool.c + vpool.h + axolotl.c + axolotl.h + axolotl_types.h + axolotl_internal.h + curve.c + curve.h + hkdf.c + hkdf.h + ratchet.c + ratchet.h + protocol.c + protocol.h + session_state.c + session_state.h + session_record.c + session_record.h + session_pre_key.c + session_pre_key.h + session_builder.c + session_builder.h + session_cipher.c + session_cipher.h + key_helper.c + key_helper.h + sender_key.c + sender_key.h + sender_key_state.c + sender_key_state.h + sender_key_record.c + sender_key_record.h + group_session_builder.c + group_session_builder.h + group_cipher.c + group_cipher.h + fingerprint.c + fingerprint.h +) + +add_subdirectory(curve25519) +add_subdirectory(protobuf-c) + +add_library(axolotl-c + ${protobuf_SRCS} + ${axolotl_SRCS} + $<TARGET_OBJECTS:curve25519> + $<TARGET_OBJECTS:protobuf-c> +) + +INSTALL( + FILES + axolotl.h + axolotl_types.h + curve.h + hkdf.h + ratchet.h + protocol.h + session_state.h + session_record.h + session_pre_key.h + session_builder.h + session_cipher.h + key_helper.h + sender_key.h + sender_key_state.h + sender_key_record.h + group_session_builder.h + group_cipher.h + fingerprint.h + DESTINATION include/axolotl +) + +INSTALL(TARGETS axolotl-c DESTINATION lib) diff --git a/libs/libaxolotl/src/FingerprintProtocol.pb-c.c b/libs/libaxolotl/src/FingerprintProtocol.pb-c.c new file mode 100644 index 0000000000..f5e62e0301 --- /dev/null +++ b/libs/libaxolotl/src/FingerprintProtocol.pb-c.c @@ -0,0 +1,210 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: FingerprintProtocol.proto */ + +/* Do not generate deprecated warnings for self */ +#ifndef PROTOBUF_C__NO_DEPRECATED +#define PROTOBUF_C__NO_DEPRECATED +#endif + +#include "FingerprintProtocol.pb-c.h" +void textsecure__fingerprint_data__init + (Textsecure__FingerprintData *message) +{ + static Textsecure__FingerprintData init_value = TEXTSECURE__FINGERPRINT_DATA__INIT; + *message = init_value; +} +size_t textsecure__fingerprint_data__get_packed_size + (const Textsecure__FingerprintData *message) +{ + assert(message->base.descriptor == &textsecure__fingerprint_data__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t textsecure__fingerprint_data__pack + (const Textsecure__FingerprintData *message, + uint8_t *out) +{ + assert(message->base.descriptor == &textsecure__fingerprint_data__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t textsecure__fingerprint_data__pack_to_buffer + (const Textsecure__FingerprintData *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &textsecure__fingerprint_data__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Textsecure__FingerprintData * + textsecure__fingerprint_data__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Textsecure__FingerprintData *) + protobuf_c_message_unpack (&textsecure__fingerprint_data__descriptor, + allocator, len, data); +} +void textsecure__fingerprint_data__free_unpacked + (Textsecure__FingerprintData *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &textsecure__fingerprint_data__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void textsecure__combined_fingerprint__init + (Textsecure__CombinedFingerprint *message) +{ + static Textsecure__CombinedFingerprint init_value = TEXTSECURE__COMBINED_FINGERPRINT__INIT; + *message = init_value; +} +size_t textsecure__combined_fingerprint__get_packed_size + (const Textsecure__CombinedFingerprint *message) +{ + assert(message->base.descriptor == &textsecure__combined_fingerprint__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t textsecure__combined_fingerprint__pack + (const Textsecure__CombinedFingerprint *message, + uint8_t *out) +{ + assert(message->base.descriptor == &textsecure__combined_fingerprint__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t textsecure__combined_fingerprint__pack_to_buffer + (const Textsecure__CombinedFingerprint *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &textsecure__combined_fingerprint__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Textsecure__CombinedFingerprint * + textsecure__combined_fingerprint__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Textsecure__CombinedFingerprint *) + protobuf_c_message_unpack (&textsecure__combined_fingerprint__descriptor, + allocator, len, data); +} +void textsecure__combined_fingerprint__free_unpacked + (Textsecure__CombinedFingerprint *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &textsecure__combined_fingerprint__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +static const ProtobufCFieldDescriptor textsecure__fingerprint_data__field_descriptors[2] = +{ + { + "publicKey", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__FingerprintData, has_publickey), + offsetof(Textsecure__FingerprintData, publickey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "identifier", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__FingerprintData, has_identifier), + offsetof(Textsecure__FingerprintData, identifier), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__fingerprint_data__field_indices_by_name[] = { + 1, /* field[1] = identifier */ + 0, /* field[0] = publicKey */ +}; +static const ProtobufCIntRange textsecure__fingerprint_data__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor textsecure__fingerprint_data__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.FingerprintData", + "FingerprintData", + "Textsecure__FingerprintData", + "textsecure", + sizeof(Textsecure__FingerprintData), + 2, + textsecure__fingerprint_data__field_descriptors, + textsecure__fingerprint_data__field_indices_by_name, + 1, textsecure__fingerprint_data__number_ranges, + (ProtobufCMessageInit) textsecure__fingerprint_data__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__combined_fingerprint__field_descriptors[3] = +{ + { + "version", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__CombinedFingerprint, has_version), + offsetof(Textsecure__CombinedFingerprint, version), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "localFingerprint", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Textsecure__CombinedFingerprint, localfingerprint), + &textsecure__fingerprint_data__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "remoteFingerprint", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Textsecure__CombinedFingerprint, remotefingerprint), + &textsecure__fingerprint_data__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__combined_fingerprint__field_indices_by_name[] = { + 1, /* field[1] = localFingerprint */ + 2, /* field[2] = remoteFingerprint */ + 0, /* field[0] = version */ +}; +static const ProtobufCIntRange textsecure__combined_fingerprint__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 3 } +}; +const ProtobufCMessageDescriptor textsecure__combined_fingerprint__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.CombinedFingerprint", + "CombinedFingerprint", + "Textsecure__CombinedFingerprint", + "textsecure", + sizeof(Textsecure__CombinedFingerprint), + 3, + textsecure__combined_fingerprint__field_descriptors, + textsecure__combined_fingerprint__field_indices_by_name, + 1, textsecure__combined_fingerprint__number_ranges, + (ProtobufCMessageInit) textsecure__combined_fingerprint__init, + NULL,NULL,NULL /* reserved[123] */ +}; diff --git a/libs/libaxolotl/src/FingerprintProtocol.pb-c.h b/libs/libaxolotl/src/FingerprintProtocol.pb-c.h new file mode 100644 index 0000000000..8bfce6eaeb --- /dev/null +++ b/libs/libaxolotl/src/FingerprintProtocol.pb-c.h @@ -0,0 +1,111 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: FingerprintProtocol.proto */ + +#ifndef PROTOBUF_C_FingerprintProtocol_2eproto__INCLUDED +#define PROTOBUF_C_FingerprintProtocol_2eproto__INCLUDED + +#include "protobuf-c/protobuf-c.h" + +PROTOBUF_C__BEGIN_DECLS + +#if PROTOBUF_C_VERSION_NUMBER < 1000000 +# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. +#elif 1001001 < PROTOBUF_C_MIN_COMPILER_VERSION +# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. +#endif + + +typedef struct _Textsecure__FingerprintData Textsecure__FingerprintData; +typedef struct _Textsecure__CombinedFingerprint Textsecure__CombinedFingerprint; + + +/* --- enums --- */ + + +/* --- messages --- */ + +struct _Textsecure__FingerprintData +{ + ProtobufCMessage base; + protobuf_c_boolean has_publickey; + ProtobufCBinaryData publickey; + protobuf_c_boolean has_identifier; + ProtobufCBinaryData identifier; +}; +#define TEXTSECURE__FINGERPRINT_DATA__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__fingerprint_data__descriptor) \ + , 0,{0,NULL}, 0,{0,NULL} } + + +struct _Textsecure__CombinedFingerprint +{ + ProtobufCMessage base; + protobuf_c_boolean has_version; + uint32_t version; + Textsecure__FingerprintData *localfingerprint; + Textsecure__FingerprintData *remotefingerprint; +}; +#define TEXTSECURE__COMBINED_FINGERPRINT__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__combined_fingerprint__descriptor) \ + , 0,0, NULL, NULL } + + +/* Textsecure__FingerprintData methods */ +void textsecure__fingerprint_data__init + (Textsecure__FingerprintData *message); +size_t textsecure__fingerprint_data__get_packed_size + (const Textsecure__FingerprintData *message); +size_t textsecure__fingerprint_data__pack + (const Textsecure__FingerprintData *message, + uint8_t *out); +size_t textsecure__fingerprint_data__pack_to_buffer + (const Textsecure__FingerprintData *message, + ProtobufCBuffer *buffer); +Textsecure__FingerprintData * + textsecure__fingerprint_data__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void textsecure__fingerprint_data__free_unpacked + (Textsecure__FingerprintData *message, + ProtobufCAllocator *allocator); +/* Textsecure__CombinedFingerprint methods */ +void textsecure__combined_fingerprint__init + (Textsecure__CombinedFingerprint *message); +size_t textsecure__combined_fingerprint__get_packed_size + (const Textsecure__CombinedFingerprint *message); +size_t textsecure__combined_fingerprint__pack + (const Textsecure__CombinedFingerprint *message, + uint8_t *out); +size_t textsecure__combined_fingerprint__pack_to_buffer + (const Textsecure__CombinedFingerprint *message, + ProtobufCBuffer *buffer); +Textsecure__CombinedFingerprint * + textsecure__combined_fingerprint__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void textsecure__combined_fingerprint__free_unpacked + (Textsecure__CombinedFingerprint *message, + ProtobufCAllocator *allocator); +/* --- per-message closures --- */ + +typedef void (*Textsecure__FingerprintData_Closure) + (const Textsecure__FingerprintData *message, + void *closure_data); +typedef void (*Textsecure__CombinedFingerprint_Closure) + (const Textsecure__CombinedFingerprint *message, + void *closure_data); + +/* --- services --- */ + + +/* --- descriptors --- */ + +extern const ProtobufCMessageDescriptor textsecure__fingerprint_data__descriptor; +extern const ProtobufCMessageDescriptor textsecure__combined_fingerprint__descriptor; + +PROTOBUF_C__END_DECLS + + +#endif /* PROTOBUF_C_FingerprintProtocol_2eproto__INCLUDED */ diff --git a/libs/libaxolotl/src/LocalStorageProtocol.pb-c.c b/libs/libaxolotl/src/LocalStorageProtocol.pb-c.c new file mode 100644 index 0000000000..afe017187a --- /dev/null +++ b/libs/libaxolotl/src/LocalStorageProtocol.pb-c.c @@ -0,0 +1,1462 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: LocalStorageProtocol.proto */ + +/* Do not generate deprecated warnings for self */ +#ifndef PROTOBUF_C__NO_DEPRECATED +#define PROTOBUF_C__NO_DEPRECATED +#endif + +#include "LocalStorageProtocol.pb-c.h" +void textsecure__session_structure__chain__chain_key__init + (Textsecure__SessionStructure__Chain__ChainKey *message) +{ + static Textsecure__SessionStructure__Chain__ChainKey init_value = TEXTSECURE__SESSION_STRUCTURE__CHAIN__CHAIN_KEY__INIT; + *message = init_value; +} +void textsecure__session_structure__chain__message_key__init + (Textsecure__SessionStructure__Chain__MessageKey *message) +{ + static Textsecure__SessionStructure__Chain__MessageKey init_value = TEXTSECURE__SESSION_STRUCTURE__CHAIN__MESSAGE_KEY__INIT; + *message = init_value; +} +void textsecure__session_structure__chain__init + (Textsecure__SessionStructure__Chain *message) +{ + static Textsecure__SessionStructure__Chain init_value = TEXTSECURE__SESSION_STRUCTURE__CHAIN__INIT; + *message = init_value; +} +void textsecure__session_structure__pending_key_exchange__init + (Textsecure__SessionStructure__PendingKeyExchange *message) +{ + static Textsecure__SessionStructure__PendingKeyExchange init_value = TEXTSECURE__SESSION_STRUCTURE__PENDING_KEY_EXCHANGE__INIT; + *message = init_value; +} +void textsecure__session_structure__pending_pre_key__init + (Textsecure__SessionStructure__PendingPreKey *message) +{ + static Textsecure__SessionStructure__PendingPreKey init_value = TEXTSECURE__SESSION_STRUCTURE__PENDING_PRE_KEY__INIT; + *message = init_value; +} +void textsecure__session_structure__init + (Textsecure__SessionStructure *message) +{ + static Textsecure__SessionStructure init_value = TEXTSECURE__SESSION_STRUCTURE__INIT; + *message = init_value; +} +size_t textsecure__session_structure__get_packed_size + (const Textsecure__SessionStructure *message) +{ + assert(message->base.descriptor == &textsecure__session_structure__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t textsecure__session_structure__pack + (const Textsecure__SessionStructure *message, + uint8_t *out) +{ + assert(message->base.descriptor == &textsecure__session_structure__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t textsecure__session_structure__pack_to_buffer + (const Textsecure__SessionStructure *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &textsecure__session_structure__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Textsecure__SessionStructure * + textsecure__session_structure__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Textsecure__SessionStructure *) + protobuf_c_message_unpack (&textsecure__session_structure__descriptor, + allocator, len, data); +} +void textsecure__session_structure__free_unpacked + (Textsecure__SessionStructure *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &textsecure__session_structure__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void textsecure__record_structure__init + (Textsecure__RecordStructure *message) +{ + static Textsecure__RecordStructure init_value = TEXTSECURE__RECORD_STRUCTURE__INIT; + *message = init_value; +} +size_t textsecure__record_structure__get_packed_size + (const Textsecure__RecordStructure *message) +{ + assert(message->base.descriptor == &textsecure__record_structure__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t textsecure__record_structure__pack + (const Textsecure__RecordStructure *message, + uint8_t *out) +{ + assert(message->base.descriptor == &textsecure__record_structure__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t textsecure__record_structure__pack_to_buffer + (const Textsecure__RecordStructure *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &textsecure__record_structure__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Textsecure__RecordStructure * + textsecure__record_structure__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Textsecure__RecordStructure *) + protobuf_c_message_unpack (&textsecure__record_structure__descriptor, + allocator, len, data); +} +void textsecure__record_structure__free_unpacked + (Textsecure__RecordStructure *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &textsecure__record_structure__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void textsecure__pre_key_record_structure__init + (Textsecure__PreKeyRecordStructure *message) +{ + static Textsecure__PreKeyRecordStructure init_value = TEXTSECURE__PRE_KEY_RECORD_STRUCTURE__INIT; + *message = init_value; +} +size_t textsecure__pre_key_record_structure__get_packed_size + (const Textsecure__PreKeyRecordStructure *message) +{ + assert(message->base.descriptor == &textsecure__pre_key_record_structure__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t textsecure__pre_key_record_structure__pack + (const Textsecure__PreKeyRecordStructure *message, + uint8_t *out) +{ + assert(message->base.descriptor == &textsecure__pre_key_record_structure__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t textsecure__pre_key_record_structure__pack_to_buffer + (const Textsecure__PreKeyRecordStructure *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &textsecure__pre_key_record_structure__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Textsecure__PreKeyRecordStructure * + textsecure__pre_key_record_structure__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Textsecure__PreKeyRecordStructure *) + protobuf_c_message_unpack (&textsecure__pre_key_record_structure__descriptor, + allocator, len, data); +} +void textsecure__pre_key_record_structure__free_unpacked + (Textsecure__PreKeyRecordStructure *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &textsecure__pre_key_record_structure__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void textsecure__signed_pre_key_record_structure__init + (Textsecure__SignedPreKeyRecordStructure *message) +{ + static Textsecure__SignedPreKeyRecordStructure init_value = TEXTSECURE__SIGNED_PRE_KEY_RECORD_STRUCTURE__INIT; + *message = init_value; +} +size_t textsecure__signed_pre_key_record_structure__get_packed_size + (const Textsecure__SignedPreKeyRecordStructure *message) +{ + assert(message->base.descriptor == &textsecure__signed_pre_key_record_structure__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t textsecure__signed_pre_key_record_structure__pack + (const Textsecure__SignedPreKeyRecordStructure *message, + uint8_t *out) +{ + assert(message->base.descriptor == &textsecure__signed_pre_key_record_structure__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t textsecure__signed_pre_key_record_structure__pack_to_buffer + (const Textsecure__SignedPreKeyRecordStructure *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &textsecure__signed_pre_key_record_structure__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Textsecure__SignedPreKeyRecordStructure * + textsecure__signed_pre_key_record_structure__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Textsecure__SignedPreKeyRecordStructure *) + protobuf_c_message_unpack (&textsecure__signed_pre_key_record_structure__descriptor, + allocator, len, data); +} +void textsecure__signed_pre_key_record_structure__free_unpacked + (Textsecure__SignedPreKeyRecordStructure *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &textsecure__signed_pre_key_record_structure__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void textsecure__identity_key_pair_structure__init + (Textsecure__IdentityKeyPairStructure *message) +{ + static Textsecure__IdentityKeyPairStructure init_value = TEXTSECURE__IDENTITY_KEY_PAIR_STRUCTURE__INIT; + *message = init_value; +} +size_t textsecure__identity_key_pair_structure__get_packed_size + (const Textsecure__IdentityKeyPairStructure *message) +{ + assert(message->base.descriptor == &textsecure__identity_key_pair_structure__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t textsecure__identity_key_pair_structure__pack + (const Textsecure__IdentityKeyPairStructure *message, + uint8_t *out) +{ + assert(message->base.descriptor == &textsecure__identity_key_pair_structure__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t textsecure__identity_key_pair_structure__pack_to_buffer + (const Textsecure__IdentityKeyPairStructure *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &textsecure__identity_key_pair_structure__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Textsecure__IdentityKeyPairStructure * + textsecure__identity_key_pair_structure__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Textsecure__IdentityKeyPairStructure *) + protobuf_c_message_unpack (&textsecure__identity_key_pair_structure__descriptor, + allocator, len, data); +} +void textsecure__identity_key_pair_structure__free_unpacked + (Textsecure__IdentityKeyPairStructure *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &textsecure__identity_key_pair_structure__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void textsecure__sender_key_state_structure__sender_chain_key__init + (Textsecure__SenderKeyStateStructure__SenderChainKey *message) +{ + static Textsecure__SenderKeyStateStructure__SenderChainKey init_value = TEXTSECURE__SENDER_KEY_STATE_STRUCTURE__SENDER_CHAIN_KEY__INIT; + *message = init_value; +} +void textsecure__sender_key_state_structure__sender_message_key__init + (Textsecure__SenderKeyStateStructure__SenderMessageKey *message) +{ + static Textsecure__SenderKeyStateStructure__SenderMessageKey init_value = TEXTSECURE__SENDER_KEY_STATE_STRUCTURE__SENDER_MESSAGE_KEY__INIT; + *message = init_value; +} +void textsecure__sender_key_state_structure__sender_signing_key__init + (Textsecure__SenderKeyStateStructure__SenderSigningKey *message) +{ + static Textsecure__SenderKeyStateStructure__SenderSigningKey init_value = TEXTSECURE__SENDER_KEY_STATE_STRUCTURE__SENDER_SIGNING_KEY__INIT; + *message = init_value; +} +void textsecure__sender_key_state_structure__init + (Textsecure__SenderKeyStateStructure *message) +{ + static Textsecure__SenderKeyStateStructure init_value = TEXTSECURE__SENDER_KEY_STATE_STRUCTURE__INIT; + *message = init_value; +} +size_t textsecure__sender_key_state_structure__get_packed_size + (const Textsecure__SenderKeyStateStructure *message) +{ + assert(message->base.descriptor == &textsecure__sender_key_state_structure__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t textsecure__sender_key_state_structure__pack + (const Textsecure__SenderKeyStateStructure *message, + uint8_t *out) +{ + assert(message->base.descriptor == &textsecure__sender_key_state_structure__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t textsecure__sender_key_state_structure__pack_to_buffer + (const Textsecure__SenderKeyStateStructure *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &textsecure__sender_key_state_structure__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Textsecure__SenderKeyStateStructure * + textsecure__sender_key_state_structure__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Textsecure__SenderKeyStateStructure *) + protobuf_c_message_unpack (&textsecure__sender_key_state_structure__descriptor, + allocator, len, data); +} +void textsecure__sender_key_state_structure__free_unpacked + (Textsecure__SenderKeyStateStructure *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &textsecure__sender_key_state_structure__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void textsecure__sender_key_record_structure__init + (Textsecure__SenderKeyRecordStructure *message) +{ + static Textsecure__SenderKeyRecordStructure init_value = TEXTSECURE__SENDER_KEY_RECORD_STRUCTURE__INIT; + *message = init_value; +} +size_t textsecure__sender_key_record_structure__get_packed_size + (const Textsecure__SenderKeyRecordStructure *message) +{ + assert(message->base.descriptor == &textsecure__sender_key_record_structure__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t textsecure__sender_key_record_structure__pack + (const Textsecure__SenderKeyRecordStructure *message, + uint8_t *out) +{ + assert(message->base.descriptor == &textsecure__sender_key_record_structure__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t textsecure__sender_key_record_structure__pack_to_buffer + (const Textsecure__SenderKeyRecordStructure *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &textsecure__sender_key_record_structure__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Textsecure__SenderKeyRecordStructure * + textsecure__sender_key_record_structure__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Textsecure__SenderKeyRecordStructure *) + protobuf_c_message_unpack (&textsecure__sender_key_record_structure__descriptor, + allocator, len, data); +} +void textsecure__sender_key_record_structure__free_unpacked + (Textsecure__SenderKeyRecordStructure *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &textsecure__sender_key_record_structure__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +static const ProtobufCFieldDescriptor textsecure__session_structure__chain__chain_key__field_descriptors[2] = +{ + { + "index", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SessionStructure__Chain__ChainKey, has_index), + offsetof(Textsecure__SessionStructure__Chain__ChainKey, index), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "key", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure__Chain__ChainKey, has_key), + offsetof(Textsecure__SessionStructure__Chain__ChainKey, key), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__session_structure__chain__chain_key__field_indices_by_name[] = { + 0, /* field[0] = index */ + 1, /* field[1] = key */ +}; +static const ProtobufCIntRange textsecure__session_structure__chain__chain_key__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor textsecure__session_structure__chain__chain_key__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.SessionStructure.Chain.ChainKey", + "ChainKey", + "Textsecure__SessionStructure__Chain__ChainKey", + "textsecure", + sizeof(Textsecure__SessionStructure__Chain__ChainKey), + 2, + textsecure__session_structure__chain__chain_key__field_descriptors, + textsecure__session_structure__chain__chain_key__field_indices_by_name, + 1, textsecure__session_structure__chain__chain_key__number_ranges, + (ProtobufCMessageInit) textsecure__session_structure__chain__chain_key__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__session_structure__chain__message_key__field_descriptors[4] = +{ + { + "index", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SessionStructure__Chain__MessageKey, has_index), + offsetof(Textsecure__SessionStructure__Chain__MessageKey, index), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cipherKey", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure__Chain__MessageKey, has_cipherkey), + offsetof(Textsecure__SessionStructure__Chain__MessageKey, cipherkey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "macKey", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure__Chain__MessageKey, has_mackey), + offsetof(Textsecure__SessionStructure__Chain__MessageKey, mackey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "iv", + 4, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure__Chain__MessageKey, has_iv), + offsetof(Textsecure__SessionStructure__Chain__MessageKey, iv), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__session_structure__chain__message_key__field_indices_by_name[] = { + 1, /* field[1] = cipherKey */ + 0, /* field[0] = index */ + 3, /* field[3] = iv */ + 2, /* field[2] = macKey */ +}; +static const ProtobufCIntRange textsecure__session_structure__chain__message_key__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 4 } +}; +const ProtobufCMessageDescriptor textsecure__session_structure__chain__message_key__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.SessionStructure.Chain.MessageKey", + "MessageKey", + "Textsecure__SessionStructure__Chain__MessageKey", + "textsecure", + sizeof(Textsecure__SessionStructure__Chain__MessageKey), + 4, + textsecure__session_structure__chain__message_key__field_descriptors, + textsecure__session_structure__chain__message_key__field_indices_by_name, + 1, textsecure__session_structure__chain__message_key__number_ranges, + (ProtobufCMessageInit) textsecure__session_structure__chain__message_key__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__session_structure__chain__field_descriptors[4] = +{ + { + "senderRatchetKey", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure__Chain, has_senderratchetkey), + offsetof(Textsecure__SessionStructure__Chain, senderratchetkey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "senderRatchetKeyPrivate", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure__Chain, has_senderratchetkeyprivate), + offsetof(Textsecure__SessionStructure__Chain, senderratchetkeyprivate), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "chainKey", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Textsecure__SessionStructure__Chain, chainkey), + &textsecure__session_structure__chain__chain_key__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "messageKeys", + 4, + PROTOBUF_C_LABEL_REPEATED, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(Textsecure__SessionStructure__Chain, n_messagekeys), + offsetof(Textsecure__SessionStructure__Chain, messagekeys), + &textsecure__session_structure__chain__message_key__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__session_structure__chain__field_indices_by_name[] = { + 2, /* field[2] = chainKey */ + 3, /* field[3] = messageKeys */ + 0, /* field[0] = senderRatchetKey */ + 1, /* field[1] = senderRatchetKeyPrivate */ +}; +static const ProtobufCIntRange textsecure__session_structure__chain__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 4 } +}; +const ProtobufCMessageDescriptor textsecure__session_structure__chain__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.SessionStructure.Chain", + "Chain", + "Textsecure__SessionStructure__Chain", + "textsecure", + sizeof(Textsecure__SessionStructure__Chain), + 4, + textsecure__session_structure__chain__field_descriptors, + textsecure__session_structure__chain__field_indices_by_name, + 1, textsecure__session_structure__chain__number_ranges, + (ProtobufCMessageInit) textsecure__session_structure__chain__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__session_structure__pending_key_exchange__field_descriptors[7] = +{ + { + "sequence", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SessionStructure__PendingKeyExchange, has_sequence), + offsetof(Textsecure__SessionStructure__PendingKeyExchange, sequence), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "localBaseKey", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure__PendingKeyExchange, has_localbasekey), + offsetof(Textsecure__SessionStructure__PendingKeyExchange, localbasekey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "localBaseKeyPrivate", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure__PendingKeyExchange, has_localbasekeyprivate), + offsetof(Textsecure__SessionStructure__PendingKeyExchange, localbasekeyprivate), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "localRatchetKey", + 4, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure__PendingKeyExchange, has_localratchetkey), + offsetof(Textsecure__SessionStructure__PendingKeyExchange, localratchetkey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "localRatchetKeyPrivate", + 5, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure__PendingKeyExchange, has_localratchetkeyprivate), + offsetof(Textsecure__SessionStructure__PendingKeyExchange, localratchetkeyprivate), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "localIdentityKey", + 7, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure__PendingKeyExchange, has_localidentitykey), + offsetof(Textsecure__SessionStructure__PendingKeyExchange, localidentitykey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "localIdentityKeyPrivate", + 8, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure__PendingKeyExchange, has_localidentitykeyprivate), + offsetof(Textsecure__SessionStructure__PendingKeyExchange, localidentitykeyprivate), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__session_structure__pending_key_exchange__field_indices_by_name[] = { + 1, /* field[1] = localBaseKey */ + 2, /* field[2] = localBaseKeyPrivate */ + 5, /* field[5] = localIdentityKey */ + 6, /* field[6] = localIdentityKeyPrivate */ + 3, /* field[3] = localRatchetKey */ + 4, /* field[4] = localRatchetKeyPrivate */ + 0, /* field[0] = sequence */ +}; +static const ProtobufCIntRange textsecure__session_structure__pending_key_exchange__number_ranges[2 + 1] = +{ + { 1, 0 }, + { 7, 5 }, + { 0, 7 } +}; +const ProtobufCMessageDescriptor textsecure__session_structure__pending_key_exchange__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.SessionStructure.PendingKeyExchange", + "PendingKeyExchange", + "Textsecure__SessionStructure__PendingKeyExchange", + "textsecure", + sizeof(Textsecure__SessionStructure__PendingKeyExchange), + 7, + textsecure__session_structure__pending_key_exchange__field_descriptors, + textsecure__session_structure__pending_key_exchange__field_indices_by_name, + 2, textsecure__session_structure__pending_key_exchange__number_ranges, + (ProtobufCMessageInit) textsecure__session_structure__pending_key_exchange__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__session_structure__pending_pre_key__field_descriptors[3] = +{ + { + "preKeyId", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SessionStructure__PendingPreKey, has_prekeyid), + offsetof(Textsecure__SessionStructure__PendingPreKey, prekeyid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "baseKey", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure__PendingPreKey, has_basekey), + offsetof(Textsecure__SessionStructure__PendingPreKey, basekey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "signedPreKeyId", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_INT32, + offsetof(Textsecure__SessionStructure__PendingPreKey, has_signedprekeyid), + offsetof(Textsecure__SessionStructure__PendingPreKey, signedprekeyid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__session_structure__pending_pre_key__field_indices_by_name[] = { + 1, /* field[1] = baseKey */ + 0, /* field[0] = preKeyId */ + 2, /* field[2] = signedPreKeyId */ +}; +static const ProtobufCIntRange textsecure__session_structure__pending_pre_key__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 3 } +}; +const ProtobufCMessageDescriptor textsecure__session_structure__pending_pre_key__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.SessionStructure.PendingPreKey", + "PendingPreKey", + "Textsecure__SessionStructure__PendingPreKey", + "textsecure", + sizeof(Textsecure__SessionStructure__PendingPreKey), + 3, + textsecure__session_structure__pending_pre_key__field_descriptors, + textsecure__session_structure__pending_pre_key__field_indices_by_name, + 1, textsecure__session_structure__pending_pre_key__number_ranges, + (ProtobufCMessageInit) textsecure__session_structure__pending_pre_key__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__session_structure__field_descriptors[13] = +{ + { + "sessionVersion", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SessionStructure, has_sessionversion), + offsetof(Textsecure__SessionStructure, sessionversion), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "localIdentityPublic", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure, has_localidentitypublic), + offsetof(Textsecure__SessionStructure, localidentitypublic), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "remoteIdentityPublic", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure, has_remoteidentitypublic), + offsetof(Textsecure__SessionStructure, remoteidentitypublic), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "rootKey", + 4, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure, has_rootkey), + offsetof(Textsecure__SessionStructure, rootkey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "previousCounter", + 5, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SessionStructure, has_previouscounter), + offsetof(Textsecure__SessionStructure, previouscounter), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "senderChain", + 6, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Textsecure__SessionStructure, senderchain), + &textsecure__session_structure__chain__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "receiverChains", + 7, + PROTOBUF_C_LABEL_REPEATED, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(Textsecure__SessionStructure, n_receiverchains), + offsetof(Textsecure__SessionStructure, receiverchains), + &textsecure__session_structure__chain__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "pendingKeyExchange", + 8, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Textsecure__SessionStructure, pendingkeyexchange), + &textsecure__session_structure__pending_key_exchange__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "pendingPreKey", + 9, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Textsecure__SessionStructure, pendingprekey), + &textsecure__session_structure__pending_pre_key__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "remoteRegistrationId", + 10, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SessionStructure, has_remoteregistrationid), + offsetof(Textsecure__SessionStructure, remoteregistrationid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "localRegistrationId", + 11, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SessionStructure, has_localregistrationid), + offsetof(Textsecure__SessionStructure, localregistrationid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "needsRefresh", + 12, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BOOL, + offsetof(Textsecure__SessionStructure, has_needsrefresh), + offsetof(Textsecure__SessionStructure, needsrefresh), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "aliceBaseKey", + 13, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SessionStructure, has_alicebasekey), + offsetof(Textsecure__SessionStructure, alicebasekey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__session_structure__field_indices_by_name[] = { + 12, /* field[12] = aliceBaseKey */ + 1, /* field[1] = localIdentityPublic */ + 10, /* field[10] = localRegistrationId */ + 11, /* field[11] = needsRefresh */ + 7, /* field[7] = pendingKeyExchange */ + 8, /* field[8] = pendingPreKey */ + 4, /* field[4] = previousCounter */ + 6, /* field[6] = receiverChains */ + 2, /* field[2] = remoteIdentityPublic */ + 9, /* field[9] = remoteRegistrationId */ + 3, /* field[3] = rootKey */ + 5, /* field[5] = senderChain */ + 0, /* field[0] = sessionVersion */ +}; +static const ProtobufCIntRange textsecure__session_structure__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 13 } +}; +const ProtobufCMessageDescriptor textsecure__session_structure__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.SessionStructure", + "SessionStructure", + "Textsecure__SessionStructure", + "textsecure", + sizeof(Textsecure__SessionStructure), + 13, + textsecure__session_structure__field_descriptors, + textsecure__session_structure__field_indices_by_name, + 1, textsecure__session_structure__number_ranges, + (ProtobufCMessageInit) textsecure__session_structure__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__record_structure__field_descriptors[2] = +{ + { + "currentSession", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Textsecure__RecordStructure, currentsession), + &textsecure__session_structure__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "previousSessions", + 2, + PROTOBUF_C_LABEL_REPEATED, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(Textsecure__RecordStructure, n_previoussessions), + offsetof(Textsecure__RecordStructure, previoussessions), + &textsecure__session_structure__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__record_structure__field_indices_by_name[] = { + 0, /* field[0] = currentSession */ + 1, /* field[1] = previousSessions */ +}; +static const ProtobufCIntRange textsecure__record_structure__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor textsecure__record_structure__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.RecordStructure", + "RecordStructure", + "Textsecure__RecordStructure", + "textsecure", + sizeof(Textsecure__RecordStructure), + 2, + textsecure__record_structure__field_descriptors, + textsecure__record_structure__field_indices_by_name, + 1, textsecure__record_structure__number_ranges, + (ProtobufCMessageInit) textsecure__record_structure__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__pre_key_record_structure__field_descriptors[3] = +{ + { + "id", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__PreKeyRecordStructure, has_id), + offsetof(Textsecure__PreKeyRecordStructure, id), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "publicKey", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__PreKeyRecordStructure, has_publickey), + offsetof(Textsecure__PreKeyRecordStructure, publickey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "privateKey", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__PreKeyRecordStructure, has_privatekey), + offsetof(Textsecure__PreKeyRecordStructure, privatekey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__pre_key_record_structure__field_indices_by_name[] = { + 0, /* field[0] = id */ + 2, /* field[2] = privateKey */ + 1, /* field[1] = publicKey */ +}; +static const ProtobufCIntRange textsecure__pre_key_record_structure__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 3 } +}; +const ProtobufCMessageDescriptor textsecure__pre_key_record_structure__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.PreKeyRecordStructure", + "PreKeyRecordStructure", + "Textsecure__PreKeyRecordStructure", + "textsecure", + sizeof(Textsecure__PreKeyRecordStructure), + 3, + textsecure__pre_key_record_structure__field_descriptors, + textsecure__pre_key_record_structure__field_indices_by_name, + 1, textsecure__pre_key_record_structure__number_ranges, + (ProtobufCMessageInit) textsecure__pre_key_record_structure__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__signed_pre_key_record_structure__field_descriptors[5] = +{ + { + "id", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SignedPreKeyRecordStructure, has_id), + offsetof(Textsecure__SignedPreKeyRecordStructure, id), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "publicKey", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SignedPreKeyRecordStructure, has_publickey), + offsetof(Textsecure__SignedPreKeyRecordStructure, publickey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "privateKey", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SignedPreKeyRecordStructure, has_privatekey), + offsetof(Textsecure__SignedPreKeyRecordStructure, privatekey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "signature", + 4, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SignedPreKeyRecordStructure, has_signature), + offsetof(Textsecure__SignedPreKeyRecordStructure, signature), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "timestamp", + 5, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_FIXED64, + offsetof(Textsecure__SignedPreKeyRecordStructure, has_timestamp), + offsetof(Textsecure__SignedPreKeyRecordStructure, timestamp), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__signed_pre_key_record_structure__field_indices_by_name[] = { + 0, /* field[0] = id */ + 2, /* field[2] = privateKey */ + 1, /* field[1] = publicKey */ + 3, /* field[3] = signature */ + 4, /* field[4] = timestamp */ +}; +static const ProtobufCIntRange textsecure__signed_pre_key_record_structure__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 5 } +}; +const ProtobufCMessageDescriptor textsecure__signed_pre_key_record_structure__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.SignedPreKeyRecordStructure", + "SignedPreKeyRecordStructure", + "Textsecure__SignedPreKeyRecordStructure", + "textsecure", + sizeof(Textsecure__SignedPreKeyRecordStructure), + 5, + textsecure__signed_pre_key_record_structure__field_descriptors, + textsecure__signed_pre_key_record_structure__field_indices_by_name, + 1, textsecure__signed_pre_key_record_structure__number_ranges, + (ProtobufCMessageInit) textsecure__signed_pre_key_record_structure__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__identity_key_pair_structure__field_descriptors[2] = +{ + { + "publicKey", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__IdentityKeyPairStructure, has_publickey), + offsetof(Textsecure__IdentityKeyPairStructure, publickey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "privateKey", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__IdentityKeyPairStructure, has_privatekey), + offsetof(Textsecure__IdentityKeyPairStructure, privatekey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__identity_key_pair_structure__field_indices_by_name[] = { + 1, /* field[1] = privateKey */ + 0, /* field[0] = publicKey */ +}; +static const ProtobufCIntRange textsecure__identity_key_pair_structure__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor textsecure__identity_key_pair_structure__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.IdentityKeyPairStructure", + "IdentityKeyPairStructure", + "Textsecure__IdentityKeyPairStructure", + "textsecure", + sizeof(Textsecure__IdentityKeyPairStructure), + 2, + textsecure__identity_key_pair_structure__field_descriptors, + textsecure__identity_key_pair_structure__field_indices_by_name, + 1, textsecure__identity_key_pair_structure__number_ranges, + (ProtobufCMessageInit) textsecure__identity_key_pair_structure__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__sender_key_state_structure__sender_chain_key__field_descriptors[2] = +{ + { + "iteration", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SenderKeyStateStructure__SenderChainKey, has_iteration), + offsetof(Textsecure__SenderKeyStateStructure__SenderChainKey, iteration), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "seed", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SenderKeyStateStructure__SenderChainKey, has_seed), + offsetof(Textsecure__SenderKeyStateStructure__SenderChainKey, seed), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__sender_key_state_structure__sender_chain_key__field_indices_by_name[] = { + 0, /* field[0] = iteration */ + 1, /* field[1] = seed */ +}; +static const ProtobufCIntRange textsecure__sender_key_state_structure__sender_chain_key__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor textsecure__sender_key_state_structure__sender_chain_key__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.SenderKeyStateStructure.SenderChainKey", + "SenderChainKey", + "Textsecure__SenderKeyStateStructure__SenderChainKey", + "textsecure", + sizeof(Textsecure__SenderKeyStateStructure__SenderChainKey), + 2, + textsecure__sender_key_state_structure__sender_chain_key__field_descriptors, + textsecure__sender_key_state_structure__sender_chain_key__field_indices_by_name, + 1, textsecure__sender_key_state_structure__sender_chain_key__number_ranges, + (ProtobufCMessageInit) textsecure__sender_key_state_structure__sender_chain_key__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__sender_key_state_structure__sender_message_key__field_descriptors[2] = +{ + { + "iteration", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SenderKeyStateStructure__SenderMessageKey, has_iteration), + offsetof(Textsecure__SenderKeyStateStructure__SenderMessageKey, iteration), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "seed", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SenderKeyStateStructure__SenderMessageKey, has_seed), + offsetof(Textsecure__SenderKeyStateStructure__SenderMessageKey, seed), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__sender_key_state_structure__sender_message_key__field_indices_by_name[] = { + 0, /* field[0] = iteration */ + 1, /* field[1] = seed */ +}; +static const ProtobufCIntRange textsecure__sender_key_state_structure__sender_message_key__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor textsecure__sender_key_state_structure__sender_message_key__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.SenderKeyStateStructure.SenderMessageKey", + "SenderMessageKey", + "Textsecure__SenderKeyStateStructure__SenderMessageKey", + "textsecure", + sizeof(Textsecure__SenderKeyStateStructure__SenderMessageKey), + 2, + textsecure__sender_key_state_structure__sender_message_key__field_descriptors, + textsecure__sender_key_state_structure__sender_message_key__field_indices_by_name, + 1, textsecure__sender_key_state_structure__sender_message_key__number_ranges, + (ProtobufCMessageInit) textsecure__sender_key_state_structure__sender_message_key__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__sender_key_state_structure__sender_signing_key__field_descriptors[2] = +{ + { + "public", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SenderKeyStateStructure__SenderSigningKey, has_public_), + offsetof(Textsecure__SenderKeyStateStructure__SenderSigningKey, public_), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "private", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SenderKeyStateStructure__SenderSigningKey, has_private_), + offsetof(Textsecure__SenderKeyStateStructure__SenderSigningKey, private_), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__sender_key_state_structure__sender_signing_key__field_indices_by_name[] = { + 1, /* field[1] = private */ + 0, /* field[0] = public */ +}; +static const ProtobufCIntRange textsecure__sender_key_state_structure__sender_signing_key__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor textsecure__sender_key_state_structure__sender_signing_key__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.SenderKeyStateStructure.SenderSigningKey", + "SenderSigningKey", + "Textsecure__SenderKeyStateStructure__SenderSigningKey", + "textsecure", + sizeof(Textsecure__SenderKeyStateStructure__SenderSigningKey), + 2, + textsecure__sender_key_state_structure__sender_signing_key__field_descriptors, + textsecure__sender_key_state_structure__sender_signing_key__field_indices_by_name, + 1, textsecure__sender_key_state_structure__sender_signing_key__number_ranges, + (ProtobufCMessageInit) textsecure__sender_key_state_structure__sender_signing_key__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__sender_key_state_structure__field_descriptors[4] = +{ + { + "senderKeyId", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SenderKeyStateStructure, has_senderkeyid), + offsetof(Textsecure__SenderKeyStateStructure, senderkeyid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "senderChainKey", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Textsecure__SenderKeyStateStructure, senderchainkey), + &textsecure__sender_key_state_structure__sender_chain_key__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "senderSigningKey", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Textsecure__SenderKeyStateStructure, sendersigningkey), + &textsecure__sender_key_state_structure__sender_signing_key__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "senderMessageKeys", + 4, + PROTOBUF_C_LABEL_REPEATED, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(Textsecure__SenderKeyStateStructure, n_sendermessagekeys), + offsetof(Textsecure__SenderKeyStateStructure, sendermessagekeys), + &textsecure__sender_key_state_structure__sender_message_key__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__sender_key_state_structure__field_indices_by_name[] = { + 1, /* field[1] = senderChainKey */ + 0, /* field[0] = senderKeyId */ + 3, /* field[3] = senderMessageKeys */ + 2, /* field[2] = senderSigningKey */ +}; +static const ProtobufCIntRange textsecure__sender_key_state_structure__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 4 } +}; +const ProtobufCMessageDescriptor textsecure__sender_key_state_structure__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.SenderKeyStateStructure", + "SenderKeyStateStructure", + "Textsecure__SenderKeyStateStructure", + "textsecure", + sizeof(Textsecure__SenderKeyStateStructure), + 4, + textsecure__sender_key_state_structure__field_descriptors, + textsecure__sender_key_state_structure__field_indices_by_name, + 1, textsecure__sender_key_state_structure__number_ranges, + (ProtobufCMessageInit) textsecure__sender_key_state_structure__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__sender_key_record_structure__field_descriptors[1] = +{ + { + "senderKeyStates", + 1, + PROTOBUF_C_LABEL_REPEATED, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(Textsecure__SenderKeyRecordStructure, n_senderkeystates), + offsetof(Textsecure__SenderKeyRecordStructure, senderkeystates), + &textsecure__sender_key_state_structure__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__sender_key_record_structure__field_indices_by_name[] = { + 0, /* field[0] = senderKeyStates */ +}; +static const ProtobufCIntRange textsecure__sender_key_record_structure__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor textsecure__sender_key_record_structure__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.SenderKeyRecordStructure", + "SenderKeyRecordStructure", + "Textsecure__SenderKeyRecordStructure", + "textsecure", + sizeof(Textsecure__SenderKeyRecordStructure), + 1, + textsecure__sender_key_record_structure__field_descriptors, + textsecure__sender_key_record_structure__field_indices_by_name, + 1, textsecure__sender_key_record_structure__number_ranges, + (ProtobufCMessageInit) textsecure__sender_key_record_structure__init, + NULL,NULL,NULL /* reserved[123] */ +}; diff --git a/libs/libaxolotl/src/LocalStorageProtocol.pb-c.h b/libs/libaxolotl/src/LocalStorageProtocol.pb-c.h new file mode 100644 index 0000000000..86e657e51a --- /dev/null +++ b/libs/libaxolotl/src/LocalStorageProtocol.pb-c.h @@ -0,0 +1,509 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: LocalStorageProtocol.proto */ + +#ifndef PROTOBUF_C_LocalStorageProtocol_2eproto__INCLUDED +#define PROTOBUF_C_LocalStorageProtocol_2eproto__INCLUDED + +#include "protobuf-c/protobuf-c.h" + +PROTOBUF_C__BEGIN_DECLS + +#if PROTOBUF_C_VERSION_NUMBER < 1000000 +# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. +#elif 1001001 < PROTOBUF_C_MIN_COMPILER_VERSION +# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. +#endif + + +typedef struct _Textsecure__SessionStructure Textsecure__SessionStructure; +typedef struct _Textsecure__SessionStructure__Chain Textsecure__SessionStructure__Chain; +typedef struct _Textsecure__SessionStructure__Chain__ChainKey Textsecure__SessionStructure__Chain__ChainKey; +typedef struct _Textsecure__SessionStructure__Chain__MessageKey Textsecure__SessionStructure__Chain__MessageKey; +typedef struct _Textsecure__SessionStructure__PendingKeyExchange Textsecure__SessionStructure__PendingKeyExchange; +typedef struct _Textsecure__SessionStructure__PendingPreKey Textsecure__SessionStructure__PendingPreKey; +typedef struct _Textsecure__RecordStructure Textsecure__RecordStructure; +typedef struct _Textsecure__PreKeyRecordStructure Textsecure__PreKeyRecordStructure; +typedef struct _Textsecure__SignedPreKeyRecordStructure Textsecure__SignedPreKeyRecordStructure; +typedef struct _Textsecure__IdentityKeyPairStructure Textsecure__IdentityKeyPairStructure; +typedef struct _Textsecure__SenderKeyStateStructure Textsecure__SenderKeyStateStructure; +typedef struct _Textsecure__SenderKeyStateStructure__SenderChainKey Textsecure__SenderKeyStateStructure__SenderChainKey; +typedef struct _Textsecure__SenderKeyStateStructure__SenderMessageKey Textsecure__SenderKeyStateStructure__SenderMessageKey; +typedef struct _Textsecure__SenderKeyStateStructure__SenderSigningKey Textsecure__SenderKeyStateStructure__SenderSigningKey; +typedef struct _Textsecure__SenderKeyRecordStructure Textsecure__SenderKeyRecordStructure; + + +/* --- enums --- */ + + +/* --- messages --- */ + +struct _Textsecure__SessionStructure__Chain__ChainKey +{ + ProtobufCMessage base; + protobuf_c_boolean has_index; + uint32_t index; + protobuf_c_boolean has_key; + ProtobufCBinaryData key; +}; +#define TEXTSECURE__SESSION_STRUCTURE__CHAIN__CHAIN_KEY__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__session_structure__chain__chain_key__descriptor) \ + , 0,0, 0,{0,NULL} } + + +struct _Textsecure__SessionStructure__Chain__MessageKey +{ + ProtobufCMessage base; + protobuf_c_boolean has_index; + uint32_t index; + protobuf_c_boolean has_cipherkey; + ProtobufCBinaryData cipherkey; + protobuf_c_boolean has_mackey; + ProtobufCBinaryData mackey; + protobuf_c_boolean has_iv; + ProtobufCBinaryData iv; +}; +#define TEXTSECURE__SESSION_STRUCTURE__CHAIN__MESSAGE_KEY__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__session_structure__chain__message_key__descriptor) \ + , 0,0, 0,{0,NULL}, 0,{0,NULL}, 0,{0,NULL} } + + +struct _Textsecure__SessionStructure__Chain +{ + ProtobufCMessage base; + protobuf_c_boolean has_senderratchetkey; + ProtobufCBinaryData senderratchetkey; + protobuf_c_boolean has_senderratchetkeyprivate; + ProtobufCBinaryData senderratchetkeyprivate; + Textsecure__SessionStructure__Chain__ChainKey *chainkey; + size_t n_messagekeys; + Textsecure__SessionStructure__Chain__MessageKey **messagekeys; +}; +#define TEXTSECURE__SESSION_STRUCTURE__CHAIN__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__session_structure__chain__descriptor) \ + , 0,{0,NULL}, 0,{0,NULL}, NULL, 0,NULL } + + +struct _Textsecure__SessionStructure__PendingKeyExchange +{ + ProtobufCMessage base; + protobuf_c_boolean has_sequence; + uint32_t sequence; + protobuf_c_boolean has_localbasekey; + ProtobufCBinaryData localbasekey; + protobuf_c_boolean has_localbasekeyprivate; + ProtobufCBinaryData localbasekeyprivate; + protobuf_c_boolean has_localratchetkey; + ProtobufCBinaryData localratchetkey; + protobuf_c_boolean has_localratchetkeyprivate; + ProtobufCBinaryData localratchetkeyprivate; + protobuf_c_boolean has_localidentitykey; + ProtobufCBinaryData localidentitykey; + protobuf_c_boolean has_localidentitykeyprivate; + ProtobufCBinaryData localidentitykeyprivate; +}; +#define TEXTSECURE__SESSION_STRUCTURE__PENDING_KEY_EXCHANGE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__session_structure__pending_key_exchange__descriptor) \ + , 0,0, 0,{0,NULL}, 0,{0,NULL}, 0,{0,NULL}, 0,{0,NULL}, 0,{0,NULL}, 0,{0,NULL} } + + +struct _Textsecure__SessionStructure__PendingPreKey +{ + ProtobufCMessage base; + protobuf_c_boolean has_prekeyid; + uint32_t prekeyid; + protobuf_c_boolean has_signedprekeyid; + int32_t signedprekeyid; + protobuf_c_boolean has_basekey; + ProtobufCBinaryData basekey; +}; +#define TEXTSECURE__SESSION_STRUCTURE__PENDING_PRE_KEY__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__session_structure__pending_pre_key__descriptor) \ + , 0,0, 0,0, 0,{0,NULL} } + + +struct _Textsecure__SessionStructure +{ + ProtobufCMessage base; + protobuf_c_boolean has_sessionversion; + uint32_t sessionversion; + protobuf_c_boolean has_localidentitypublic; + ProtobufCBinaryData localidentitypublic; + protobuf_c_boolean has_remoteidentitypublic; + ProtobufCBinaryData remoteidentitypublic; + protobuf_c_boolean has_rootkey; + ProtobufCBinaryData rootkey; + protobuf_c_boolean has_previouscounter; + uint32_t previouscounter; + Textsecure__SessionStructure__Chain *senderchain; + size_t n_receiverchains; + Textsecure__SessionStructure__Chain **receiverchains; + Textsecure__SessionStructure__PendingKeyExchange *pendingkeyexchange; + Textsecure__SessionStructure__PendingPreKey *pendingprekey; + protobuf_c_boolean has_remoteregistrationid; + uint32_t remoteregistrationid; + protobuf_c_boolean has_localregistrationid; + uint32_t localregistrationid; + protobuf_c_boolean has_needsrefresh; + protobuf_c_boolean needsrefresh; + protobuf_c_boolean has_alicebasekey; + ProtobufCBinaryData alicebasekey; +}; +#define TEXTSECURE__SESSION_STRUCTURE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__session_structure__descriptor) \ + , 0,0, 0,{0,NULL}, 0,{0,NULL}, 0,{0,NULL}, 0,0, NULL, 0,NULL, NULL, NULL, 0,0, 0,0, 0,0, 0,{0,NULL} } + + +struct _Textsecure__RecordStructure +{ + ProtobufCMessage base; + Textsecure__SessionStructure *currentsession; + size_t n_previoussessions; + Textsecure__SessionStructure **previoussessions; +}; +#define TEXTSECURE__RECORD_STRUCTURE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__record_structure__descriptor) \ + , NULL, 0,NULL } + + +struct _Textsecure__PreKeyRecordStructure +{ + ProtobufCMessage base; + protobuf_c_boolean has_id; + uint32_t id; + protobuf_c_boolean has_publickey; + ProtobufCBinaryData publickey; + protobuf_c_boolean has_privatekey; + ProtobufCBinaryData privatekey; +}; +#define TEXTSECURE__PRE_KEY_RECORD_STRUCTURE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__pre_key_record_structure__descriptor) \ + , 0,0, 0,{0,NULL}, 0,{0,NULL} } + + +struct _Textsecure__SignedPreKeyRecordStructure +{ + ProtobufCMessage base; + protobuf_c_boolean has_id; + uint32_t id; + protobuf_c_boolean has_publickey; + ProtobufCBinaryData publickey; + protobuf_c_boolean has_privatekey; + ProtobufCBinaryData privatekey; + protobuf_c_boolean has_signature; + ProtobufCBinaryData signature; + protobuf_c_boolean has_timestamp; + uint64_t timestamp; +}; +#define TEXTSECURE__SIGNED_PRE_KEY_RECORD_STRUCTURE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__signed_pre_key_record_structure__descriptor) \ + , 0,0, 0,{0,NULL}, 0,{0,NULL}, 0,{0,NULL}, 0,0 } + + +struct _Textsecure__IdentityKeyPairStructure +{ + ProtobufCMessage base; + protobuf_c_boolean has_publickey; + ProtobufCBinaryData publickey; + protobuf_c_boolean has_privatekey; + ProtobufCBinaryData privatekey; +}; +#define TEXTSECURE__IDENTITY_KEY_PAIR_STRUCTURE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__identity_key_pair_structure__descriptor) \ + , 0,{0,NULL}, 0,{0,NULL} } + + +struct _Textsecure__SenderKeyStateStructure__SenderChainKey +{ + ProtobufCMessage base; + protobuf_c_boolean has_iteration; + uint32_t iteration; + protobuf_c_boolean has_seed; + ProtobufCBinaryData seed; +}; +#define TEXTSECURE__SENDER_KEY_STATE_STRUCTURE__SENDER_CHAIN_KEY__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__sender_key_state_structure__sender_chain_key__descriptor) \ + , 0,0, 0,{0,NULL} } + + +struct _Textsecure__SenderKeyStateStructure__SenderMessageKey +{ + ProtobufCMessage base; + protobuf_c_boolean has_iteration; + uint32_t iteration; + protobuf_c_boolean has_seed; + ProtobufCBinaryData seed; +}; +#define TEXTSECURE__SENDER_KEY_STATE_STRUCTURE__SENDER_MESSAGE_KEY__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__sender_key_state_structure__sender_message_key__descriptor) \ + , 0,0, 0,{0,NULL} } + + +struct _Textsecure__SenderKeyStateStructure__SenderSigningKey +{ + ProtobufCMessage base; + protobuf_c_boolean has_public_; + ProtobufCBinaryData public_; + protobuf_c_boolean has_private_; + ProtobufCBinaryData private_; +}; +#define TEXTSECURE__SENDER_KEY_STATE_STRUCTURE__SENDER_SIGNING_KEY__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__sender_key_state_structure__sender_signing_key__descriptor) \ + , 0,{0,NULL}, 0,{0,NULL} } + + +struct _Textsecure__SenderKeyStateStructure +{ + ProtobufCMessage base; + protobuf_c_boolean has_senderkeyid; + uint32_t senderkeyid; + Textsecure__SenderKeyStateStructure__SenderChainKey *senderchainkey; + Textsecure__SenderKeyStateStructure__SenderSigningKey *sendersigningkey; + size_t n_sendermessagekeys; + Textsecure__SenderKeyStateStructure__SenderMessageKey **sendermessagekeys; +}; +#define TEXTSECURE__SENDER_KEY_STATE_STRUCTURE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__sender_key_state_structure__descriptor) \ + , 0,0, NULL, NULL, 0,NULL } + + +struct _Textsecure__SenderKeyRecordStructure +{ + ProtobufCMessage base; + size_t n_senderkeystates; + Textsecure__SenderKeyStateStructure **senderkeystates; +}; +#define TEXTSECURE__SENDER_KEY_RECORD_STRUCTURE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__sender_key_record_structure__descriptor) \ + , 0,NULL } + + +/* Textsecure__SessionStructure__Chain__ChainKey methods */ +void textsecure__session_structure__chain__chain_key__init + (Textsecure__SessionStructure__Chain__ChainKey *message); +/* Textsecure__SessionStructure__Chain__MessageKey methods */ +void textsecure__session_structure__chain__message_key__init + (Textsecure__SessionStructure__Chain__MessageKey *message); +/* Textsecure__SessionStructure__Chain methods */ +void textsecure__session_structure__chain__init + (Textsecure__SessionStructure__Chain *message); +/* Textsecure__SessionStructure__PendingKeyExchange methods */ +void textsecure__session_structure__pending_key_exchange__init + (Textsecure__SessionStructure__PendingKeyExchange *message); +/* Textsecure__SessionStructure__PendingPreKey methods */ +void textsecure__session_structure__pending_pre_key__init + (Textsecure__SessionStructure__PendingPreKey *message); +/* Textsecure__SessionStructure methods */ +void textsecure__session_structure__init + (Textsecure__SessionStructure *message); +size_t textsecure__session_structure__get_packed_size + (const Textsecure__SessionStructure *message); +size_t textsecure__session_structure__pack + (const Textsecure__SessionStructure *message, + uint8_t *out); +size_t textsecure__session_structure__pack_to_buffer + (const Textsecure__SessionStructure *message, + ProtobufCBuffer *buffer); +Textsecure__SessionStructure * + textsecure__session_structure__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void textsecure__session_structure__free_unpacked + (Textsecure__SessionStructure *message, + ProtobufCAllocator *allocator); +/* Textsecure__RecordStructure methods */ +void textsecure__record_structure__init + (Textsecure__RecordStructure *message); +size_t textsecure__record_structure__get_packed_size + (const Textsecure__RecordStructure *message); +size_t textsecure__record_structure__pack + (const Textsecure__RecordStructure *message, + uint8_t *out); +size_t textsecure__record_structure__pack_to_buffer + (const Textsecure__RecordStructure *message, + ProtobufCBuffer *buffer); +Textsecure__RecordStructure * + textsecure__record_structure__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void textsecure__record_structure__free_unpacked + (Textsecure__RecordStructure *message, + ProtobufCAllocator *allocator); +/* Textsecure__PreKeyRecordStructure methods */ +void textsecure__pre_key_record_structure__init + (Textsecure__PreKeyRecordStructure *message); +size_t textsecure__pre_key_record_structure__get_packed_size + (const Textsecure__PreKeyRecordStructure *message); +size_t textsecure__pre_key_record_structure__pack + (const Textsecure__PreKeyRecordStructure *message, + uint8_t *out); +size_t textsecure__pre_key_record_structure__pack_to_buffer + (const Textsecure__PreKeyRecordStructure *message, + ProtobufCBuffer *buffer); +Textsecure__PreKeyRecordStructure * + textsecure__pre_key_record_structure__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void textsecure__pre_key_record_structure__free_unpacked + (Textsecure__PreKeyRecordStructure *message, + ProtobufCAllocator *allocator); +/* Textsecure__SignedPreKeyRecordStructure methods */ +void textsecure__signed_pre_key_record_structure__init + (Textsecure__SignedPreKeyRecordStructure *message); +size_t textsecure__signed_pre_key_record_structure__get_packed_size + (const Textsecure__SignedPreKeyRecordStructure *message); +size_t textsecure__signed_pre_key_record_structure__pack + (const Textsecure__SignedPreKeyRecordStructure *message, + uint8_t *out); +size_t textsecure__signed_pre_key_record_structure__pack_to_buffer + (const Textsecure__SignedPreKeyRecordStructure *message, + ProtobufCBuffer *buffer); +Textsecure__SignedPreKeyRecordStructure * + textsecure__signed_pre_key_record_structure__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void textsecure__signed_pre_key_record_structure__free_unpacked + (Textsecure__SignedPreKeyRecordStructure *message, + ProtobufCAllocator *allocator); +/* Textsecure__IdentityKeyPairStructure methods */ +void textsecure__identity_key_pair_structure__init + (Textsecure__IdentityKeyPairStructure *message); +size_t textsecure__identity_key_pair_structure__get_packed_size + (const Textsecure__IdentityKeyPairStructure *message); +size_t textsecure__identity_key_pair_structure__pack + (const Textsecure__IdentityKeyPairStructure *message, + uint8_t *out); +size_t textsecure__identity_key_pair_structure__pack_to_buffer + (const Textsecure__IdentityKeyPairStructure *message, + ProtobufCBuffer *buffer); +Textsecure__IdentityKeyPairStructure * + textsecure__identity_key_pair_structure__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void textsecure__identity_key_pair_structure__free_unpacked + (Textsecure__IdentityKeyPairStructure *message, + ProtobufCAllocator *allocator); +/* Textsecure__SenderKeyStateStructure__SenderChainKey methods */ +void textsecure__sender_key_state_structure__sender_chain_key__init + (Textsecure__SenderKeyStateStructure__SenderChainKey *message); +/* Textsecure__SenderKeyStateStructure__SenderMessageKey methods */ +void textsecure__sender_key_state_structure__sender_message_key__init + (Textsecure__SenderKeyStateStructure__SenderMessageKey *message); +/* Textsecure__SenderKeyStateStructure__SenderSigningKey methods */ +void textsecure__sender_key_state_structure__sender_signing_key__init + (Textsecure__SenderKeyStateStructure__SenderSigningKey *message); +/* Textsecure__SenderKeyStateStructure methods */ +void textsecure__sender_key_state_structure__init + (Textsecure__SenderKeyStateStructure *message); +size_t textsecure__sender_key_state_structure__get_packed_size + (const Textsecure__SenderKeyStateStructure *message); +size_t textsecure__sender_key_state_structure__pack + (const Textsecure__SenderKeyStateStructure *message, + uint8_t *out); +size_t textsecure__sender_key_state_structure__pack_to_buffer + (const Textsecure__SenderKeyStateStructure *message, + ProtobufCBuffer *buffer); +Textsecure__SenderKeyStateStructure * + textsecure__sender_key_state_structure__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void textsecure__sender_key_state_structure__free_unpacked + (Textsecure__SenderKeyStateStructure *message, + ProtobufCAllocator *allocator); +/* Textsecure__SenderKeyRecordStructure methods */ +void textsecure__sender_key_record_structure__init + (Textsecure__SenderKeyRecordStructure *message); +size_t textsecure__sender_key_record_structure__get_packed_size + (const Textsecure__SenderKeyRecordStructure *message); +size_t textsecure__sender_key_record_structure__pack + (const Textsecure__SenderKeyRecordStructure *message, + uint8_t *out); +size_t textsecure__sender_key_record_structure__pack_to_buffer + (const Textsecure__SenderKeyRecordStructure *message, + ProtobufCBuffer *buffer); +Textsecure__SenderKeyRecordStructure * + textsecure__sender_key_record_structure__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void textsecure__sender_key_record_structure__free_unpacked + (Textsecure__SenderKeyRecordStructure *message, + ProtobufCAllocator *allocator); +/* --- per-message closures --- */ + +typedef void (*Textsecure__SessionStructure__Chain__ChainKey_Closure) + (const Textsecure__SessionStructure__Chain__ChainKey *message, + void *closure_data); +typedef void (*Textsecure__SessionStructure__Chain__MessageKey_Closure) + (const Textsecure__SessionStructure__Chain__MessageKey *message, + void *closure_data); +typedef void (*Textsecure__SessionStructure__Chain_Closure) + (const Textsecure__SessionStructure__Chain *message, + void *closure_data); +typedef void (*Textsecure__SessionStructure__PendingKeyExchange_Closure) + (const Textsecure__SessionStructure__PendingKeyExchange *message, + void *closure_data); +typedef void (*Textsecure__SessionStructure__PendingPreKey_Closure) + (const Textsecure__SessionStructure__PendingPreKey *message, + void *closure_data); +typedef void (*Textsecure__SessionStructure_Closure) + (const Textsecure__SessionStructure *message, + void *closure_data); +typedef void (*Textsecure__RecordStructure_Closure) + (const Textsecure__RecordStructure *message, + void *closure_data); +typedef void (*Textsecure__PreKeyRecordStructure_Closure) + (const Textsecure__PreKeyRecordStructure *message, + void *closure_data); +typedef void (*Textsecure__SignedPreKeyRecordStructure_Closure) + (const Textsecure__SignedPreKeyRecordStructure *message, + void *closure_data); +typedef void (*Textsecure__IdentityKeyPairStructure_Closure) + (const Textsecure__IdentityKeyPairStructure *message, + void *closure_data); +typedef void (*Textsecure__SenderKeyStateStructure__SenderChainKey_Closure) + (const Textsecure__SenderKeyStateStructure__SenderChainKey *message, + void *closure_data); +typedef void (*Textsecure__SenderKeyStateStructure__SenderMessageKey_Closure) + (const Textsecure__SenderKeyStateStructure__SenderMessageKey *message, + void *closure_data); +typedef void (*Textsecure__SenderKeyStateStructure__SenderSigningKey_Closure) + (const Textsecure__SenderKeyStateStructure__SenderSigningKey *message, + void *closure_data); +typedef void (*Textsecure__SenderKeyStateStructure_Closure) + (const Textsecure__SenderKeyStateStructure *message, + void *closure_data); +typedef void (*Textsecure__SenderKeyRecordStructure_Closure) + (const Textsecure__SenderKeyRecordStructure *message, + void *closure_data); + +/* --- services --- */ + + +/* --- descriptors --- */ + +extern const ProtobufCMessageDescriptor textsecure__session_structure__descriptor; +extern const ProtobufCMessageDescriptor textsecure__session_structure__chain__descriptor; +extern const ProtobufCMessageDescriptor textsecure__session_structure__chain__chain_key__descriptor; +extern const ProtobufCMessageDescriptor textsecure__session_structure__chain__message_key__descriptor; +extern const ProtobufCMessageDescriptor textsecure__session_structure__pending_key_exchange__descriptor; +extern const ProtobufCMessageDescriptor textsecure__session_structure__pending_pre_key__descriptor; +extern const ProtobufCMessageDescriptor textsecure__record_structure__descriptor; +extern const ProtobufCMessageDescriptor textsecure__pre_key_record_structure__descriptor; +extern const ProtobufCMessageDescriptor textsecure__signed_pre_key_record_structure__descriptor; +extern const ProtobufCMessageDescriptor textsecure__identity_key_pair_structure__descriptor; +extern const ProtobufCMessageDescriptor textsecure__sender_key_state_structure__descriptor; +extern const ProtobufCMessageDescriptor textsecure__sender_key_state_structure__sender_chain_key__descriptor; +extern const ProtobufCMessageDescriptor textsecure__sender_key_state_structure__sender_message_key__descriptor; +extern const ProtobufCMessageDescriptor textsecure__sender_key_state_structure__sender_signing_key__descriptor; +extern const ProtobufCMessageDescriptor textsecure__sender_key_record_structure__descriptor; + +PROTOBUF_C__END_DECLS + + +#endif /* PROTOBUF_C_LocalStorageProtocol_2eproto__INCLUDED */ diff --git a/libs/libaxolotl/src/WhisperTextProtocol.pb-c.c b/libs/libaxolotl/src/WhisperTextProtocol.pb-c.c new file mode 100644 index 0000000000..12c756a047 --- /dev/null +++ b/libs/libaxolotl/src/WhisperTextProtocol.pb-c.c @@ -0,0 +1,635 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: WhisperTextProtocol.proto */ + +/* Do not generate deprecated warnings for self */ +#ifndef PROTOBUF_C__NO_DEPRECATED +#define PROTOBUF_C__NO_DEPRECATED +#endif + +#include "WhisperTextProtocol.pb-c.h" +void textsecure__whisper_message__init + (Textsecure__WhisperMessage *message) +{ + static Textsecure__WhisperMessage init_value = TEXTSECURE__WHISPER_MESSAGE__INIT; + *message = init_value; +} +size_t textsecure__whisper_message__get_packed_size + (const Textsecure__WhisperMessage *message) +{ + assert(message->base.descriptor == &textsecure__whisper_message__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t textsecure__whisper_message__pack + (const Textsecure__WhisperMessage *message, + uint8_t *out) +{ + assert(message->base.descriptor == &textsecure__whisper_message__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t textsecure__whisper_message__pack_to_buffer + (const Textsecure__WhisperMessage *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &textsecure__whisper_message__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Textsecure__WhisperMessage * + textsecure__whisper_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Textsecure__WhisperMessage *) + protobuf_c_message_unpack (&textsecure__whisper_message__descriptor, + allocator, len, data); +} +void textsecure__whisper_message__free_unpacked + (Textsecure__WhisperMessage *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &textsecure__whisper_message__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void textsecure__pre_key_whisper_message__init + (Textsecure__PreKeyWhisperMessage *message) +{ + static Textsecure__PreKeyWhisperMessage init_value = TEXTSECURE__PRE_KEY_WHISPER_MESSAGE__INIT; + *message = init_value; +} +size_t textsecure__pre_key_whisper_message__get_packed_size + (const Textsecure__PreKeyWhisperMessage *message) +{ + assert(message->base.descriptor == &textsecure__pre_key_whisper_message__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t textsecure__pre_key_whisper_message__pack + (const Textsecure__PreKeyWhisperMessage *message, + uint8_t *out) +{ + assert(message->base.descriptor == &textsecure__pre_key_whisper_message__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t textsecure__pre_key_whisper_message__pack_to_buffer + (const Textsecure__PreKeyWhisperMessage *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &textsecure__pre_key_whisper_message__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Textsecure__PreKeyWhisperMessage * + textsecure__pre_key_whisper_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Textsecure__PreKeyWhisperMessage *) + protobuf_c_message_unpack (&textsecure__pre_key_whisper_message__descriptor, + allocator, len, data); +} +void textsecure__pre_key_whisper_message__free_unpacked + (Textsecure__PreKeyWhisperMessage *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &textsecure__pre_key_whisper_message__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void textsecure__key_exchange_message__init + (Textsecure__KeyExchangeMessage *message) +{ + static Textsecure__KeyExchangeMessage init_value = TEXTSECURE__KEY_EXCHANGE_MESSAGE__INIT; + *message = init_value; +} +size_t textsecure__key_exchange_message__get_packed_size + (const Textsecure__KeyExchangeMessage *message) +{ + assert(message->base.descriptor == &textsecure__key_exchange_message__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t textsecure__key_exchange_message__pack + (const Textsecure__KeyExchangeMessage *message, + uint8_t *out) +{ + assert(message->base.descriptor == &textsecure__key_exchange_message__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t textsecure__key_exchange_message__pack_to_buffer + (const Textsecure__KeyExchangeMessage *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &textsecure__key_exchange_message__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Textsecure__KeyExchangeMessage * + textsecure__key_exchange_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Textsecure__KeyExchangeMessage *) + protobuf_c_message_unpack (&textsecure__key_exchange_message__descriptor, + allocator, len, data); +} +void textsecure__key_exchange_message__free_unpacked + (Textsecure__KeyExchangeMessage *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &textsecure__key_exchange_message__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void textsecure__sender_key_message__init + (Textsecure__SenderKeyMessage *message) +{ + static Textsecure__SenderKeyMessage init_value = TEXTSECURE__SENDER_KEY_MESSAGE__INIT; + *message = init_value; +} +size_t textsecure__sender_key_message__get_packed_size + (const Textsecure__SenderKeyMessage *message) +{ + assert(message->base.descriptor == &textsecure__sender_key_message__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t textsecure__sender_key_message__pack + (const Textsecure__SenderKeyMessage *message, + uint8_t *out) +{ + assert(message->base.descriptor == &textsecure__sender_key_message__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t textsecure__sender_key_message__pack_to_buffer + (const Textsecure__SenderKeyMessage *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &textsecure__sender_key_message__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Textsecure__SenderKeyMessage * + textsecure__sender_key_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Textsecure__SenderKeyMessage *) + protobuf_c_message_unpack (&textsecure__sender_key_message__descriptor, + allocator, len, data); +} +void textsecure__sender_key_message__free_unpacked + (Textsecure__SenderKeyMessage *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &textsecure__sender_key_message__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void textsecure__sender_key_distribution_message__init + (Textsecure__SenderKeyDistributionMessage *message) +{ + static Textsecure__SenderKeyDistributionMessage init_value = TEXTSECURE__SENDER_KEY_DISTRIBUTION_MESSAGE__INIT; + *message = init_value; +} +size_t textsecure__sender_key_distribution_message__get_packed_size + (const Textsecure__SenderKeyDistributionMessage *message) +{ + assert(message->base.descriptor == &textsecure__sender_key_distribution_message__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t textsecure__sender_key_distribution_message__pack + (const Textsecure__SenderKeyDistributionMessage *message, + uint8_t *out) +{ + assert(message->base.descriptor == &textsecure__sender_key_distribution_message__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t textsecure__sender_key_distribution_message__pack_to_buffer + (const Textsecure__SenderKeyDistributionMessage *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &textsecure__sender_key_distribution_message__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Textsecure__SenderKeyDistributionMessage * + textsecure__sender_key_distribution_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Textsecure__SenderKeyDistributionMessage *) + protobuf_c_message_unpack (&textsecure__sender_key_distribution_message__descriptor, + allocator, len, data); +} +void textsecure__sender_key_distribution_message__free_unpacked + (Textsecure__SenderKeyDistributionMessage *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &textsecure__sender_key_distribution_message__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +static const ProtobufCFieldDescriptor textsecure__whisper_message__field_descriptors[4] = +{ + { + "ratchetKey", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__WhisperMessage, has_ratchetkey), + offsetof(Textsecure__WhisperMessage, ratchetkey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "counter", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__WhisperMessage, has_counter), + offsetof(Textsecure__WhisperMessage, counter), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "previousCounter", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__WhisperMessage, has_previouscounter), + offsetof(Textsecure__WhisperMessage, previouscounter), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "ciphertext", + 4, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__WhisperMessage, has_ciphertext), + offsetof(Textsecure__WhisperMessage, ciphertext), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__whisper_message__field_indices_by_name[] = { + 3, /* field[3] = ciphertext */ + 1, /* field[1] = counter */ + 2, /* field[2] = previousCounter */ + 0, /* field[0] = ratchetKey */ +}; +static const ProtobufCIntRange textsecure__whisper_message__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 4 } +}; +const ProtobufCMessageDescriptor textsecure__whisper_message__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.WhisperMessage", + "WhisperMessage", + "Textsecure__WhisperMessage", + "textsecure", + sizeof(Textsecure__WhisperMessage), + 4, + textsecure__whisper_message__field_descriptors, + textsecure__whisper_message__field_indices_by_name, + 1, textsecure__whisper_message__number_ranges, + (ProtobufCMessageInit) textsecure__whisper_message__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__pre_key_whisper_message__field_descriptors[6] = +{ + { + "preKeyId", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__PreKeyWhisperMessage, has_prekeyid), + offsetof(Textsecure__PreKeyWhisperMessage, prekeyid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "baseKey", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__PreKeyWhisperMessage, has_basekey), + offsetof(Textsecure__PreKeyWhisperMessage, basekey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "identityKey", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__PreKeyWhisperMessage, has_identitykey), + offsetof(Textsecure__PreKeyWhisperMessage, identitykey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "message", + 4, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__PreKeyWhisperMessage, has_message), + offsetof(Textsecure__PreKeyWhisperMessage, message), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "registrationId", + 5, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__PreKeyWhisperMessage, has_registrationid), + offsetof(Textsecure__PreKeyWhisperMessage, registrationid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "signedPreKeyId", + 6, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__PreKeyWhisperMessage, has_signedprekeyid), + offsetof(Textsecure__PreKeyWhisperMessage, signedprekeyid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__pre_key_whisper_message__field_indices_by_name[] = { + 1, /* field[1] = baseKey */ + 2, /* field[2] = identityKey */ + 3, /* field[3] = message */ + 0, /* field[0] = preKeyId */ + 4, /* field[4] = registrationId */ + 5, /* field[5] = signedPreKeyId */ +}; +static const ProtobufCIntRange textsecure__pre_key_whisper_message__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 6 } +}; +const ProtobufCMessageDescriptor textsecure__pre_key_whisper_message__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.PreKeyWhisperMessage", + "PreKeyWhisperMessage", + "Textsecure__PreKeyWhisperMessage", + "textsecure", + sizeof(Textsecure__PreKeyWhisperMessage), + 6, + textsecure__pre_key_whisper_message__field_descriptors, + textsecure__pre_key_whisper_message__field_indices_by_name, + 1, textsecure__pre_key_whisper_message__number_ranges, + (ProtobufCMessageInit) textsecure__pre_key_whisper_message__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__key_exchange_message__field_descriptors[5] = +{ + { + "id", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__KeyExchangeMessage, has_id), + offsetof(Textsecure__KeyExchangeMessage, id), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "baseKey", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__KeyExchangeMessage, has_basekey), + offsetof(Textsecure__KeyExchangeMessage, basekey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "ratchetKey", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__KeyExchangeMessage, has_ratchetkey), + offsetof(Textsecure__KeyExchangeMessage, ratchetkey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "identityKey", + 4, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__KeyExchangeMessage, has_identitykey), + offsetof(Textsecure__KeyExchangeMessage, identitykey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "baseKeySignature", + 5, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__KeyExchangeMessage, has_basekeysignature), + offsetof(Textsecure__KeyExchangeMessage, basekeysignature), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__key_exchange_message__field_indices_by_name[] = { + 1, /* field[1] = baseKey */ + 4, /* field[4] = baseKeySignature */ + 0, /* field[0] = id */ + 3, /* field[3] = identityKey */ + 2, /* field[2] = ratchetKey */ +}; +static const ProtobufCIntRange textsecure__key_exchange_message__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 5 } +}; +const ProtobufCMessageDescriptor textsecure__key_exchange_message__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.KeyExchangeMessage", + "KeyExchangeMessage", + "Textsecure__KeyExchangeMessage", + "textsecure", + sizeof(Textsecure__KeyExchangeMessage), + 5, + textsecure__key_exchange_message__field_descriptors, + textsecure__key_exchange_message__field_indices_by_name, + 1, textsecure__key_exchange_message__number_ranges, + (ProtobufCMessageInit) textsecure__key_exchange_message__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__sender_key_message__field_descriptors[3] = +{ + { + "id", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SenderKeyMessage, has_id), + offsetof(Textsecure__SenderKeyMessage, id), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "iteration", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SenderKeyMessage, has_iteration), + offsetof(Textsecure__SenderKeyMessage, iteration), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "ciphertext", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SenderKeyMessage, has_ciphertext), + offsetof(Textsecure__SenderKeyMessage, ciphertext), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__sender_key_message__field_indices_by_name[] = { + 2, /* field[2] = ciphertext */ + 0, /* field[0] = id */ + 1, /* field[1] = iteration */ +}; +static const ProtobufCIntRange textsecure__sender_key_message__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 3 } +}; +const ProtobufCMessageDescriptor textsecure__sender_key_message__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.SenderKeyMessage", + "SenderKeyMessage", + "Textsecure__SenderKeyMessage", + "textsecure", + sizeof(Textsecure__SenderKeyMessage), + 3, + textsecure__sender_key_message__field_descriptors, + textsecure__sender_key_message__field_indices_by_name, + 1, textsecure__sender_key_message__number_ranges, + (ProtobufCMessageInit) textsecure__sender_key_message__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor textsecure__sender_key_distribution_message__field_descriptors[4] = +{ + { + "id", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SenderKeyDistributionMessage, has_id), + offsetof(Textsecure__SenderKeyDistributionMessage, id), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "iteration", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Textsecure__SenderKeyDistributionMessage, has_iteration), + offsetof(Textsecure__SenderKeyDistributionMessage, iteration), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "chainKey", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SenderKeyDistributionMessage, has_chainkey), + offsetof(Textsecure__SenderKeyDistributionMessage, chainkey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "signingKey", + 4, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Textsecure__SenderKeyDistributionMessage, has_signingkey), + offsetof(Textsecure__SenderKeyDistributionMessage, signingkey), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned textsecure__sender_key_distribution_message__field_indices_by_name[] = { + 2, /* field[2] = chainKey */ + 0, /* field[0] = id */ + 1, /* field[1] = iteration */ + 3, /* field[3] = signingKey */ +}; +static const ProtobufCIntRange textsecure__sender_key_distribution_message__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 4 } +}; +const ProtobufCMessageDescriptor textsecure__sender_key_distribution_message__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "textsecure.SenderKeyDistributionMessage", + "SenderKeyDistributionMessage", + "Textsecure__SenderKeyDistributionMessage", + "textsecure", + sizeof(Textsecure__SenderKeyDistributionMessage), + 4, + textsecure__sender_key_distribution_message__field_descriptors, + textsecure__sender_key_distribution_message__field_indices_by_name, + 1, textsecure__sender_key_distribution_message__number_ranges, + (ProtobufCMessageInit) textsecure__sender_key_distribution_message__init, + NULL,NULL,NULL /* reserved[123] */ +}; diff --git a/libs/libaxolotl/src/WhisperTextProtocol.pb-c.h b/libs/libaxolotl/src/WhisperTextProtocol.pb-c.h new file mode 100644 index 0000000000..909a7f59f3 --- /dev/null +++ b/libs/libaxolotl/src/WhisperTextProtocol.pb-c.h @@ -0,0 +1,249 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: WhisperTextProtocol.proto */ + +#ifndef PROTOBUF_C_WhisperTextProtocol_2eproto__INCLUDED +#define PROTOBUF_C_WhisperTextProtocol_2eproto__INCLUDED + +#include "protobuf-c/protobuf-c.h" + +PROTOBUF_C__BEGIN_DECLS + +#if PROTOBUF_C_VERSION_NUMBER < 1000000 +# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. +#elif 1001001 < PROTOBUF_C_MIN_COMPILER_VERSION +# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. +#endif + + +typedef struct _Textsecure__WhisperMessage Textsecure__WhisperMessage; +typedef struct _Textsecure__PreKeyWhisperMessage Textsecure__PreKeyWhisperMessage; +typedef struct _Textsecure__KeyExchangeMessage Textsecure__KeyExchangeMessage; +typedef struct _Textsecure__SenderKeyMessage Textsecure__SenderKeyMessage; +typedef struct _Textsecure__SenderKeyDistributionMessage Textsecure__SenderKeyDistributionMessage; + + +/* --- enums --- */ + + +/* --- messages --- */ + +struct _Textsecure__WhisperMessage +{ + ProtobufCMessage base; + protobuf_c_boolean has_ratchetkey; + ProtobufCBinaryData ratchetkey; + protobuf_c_boolean has_counter; + uint32_t counter; + protobuf_c_boolean has_previouscounter; + uint32_t previouscounter; + protobuf_c_boolean has_ciphertext; + ProtobufCBinaryData ciphertext; +}; +#define TEXTSECURE__WHISPER_MESSAGE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__whisper_message__descriptor) \ + , 0,{0,NULL}, 0,0, 0,0, 0,{0,NULL} } + + +struct _Textsecure__PreKeyWhisperMessage +{ + ProtobufCMessage base; + protobuf_c_boolean has_registrationid; + uint32_t registrationid; + protobuf_c_boolean has_prekeyid; + uint32_t prekeyid; + protobuf_c_boolean has_signedprekeyid; + uint32_t signedprekeyid; + protobuf_c_boolean has_basekey; + ProtobufCBinaryData basekey; + protobuf_c_boolean has_identitykey; + ProtobufCBinaryData identitykey; + /* + * WhisperMessage + */ + protobuf_c_boolean has_message; + ProtobufCBinaryData message; +}; +#define TEXTSECURE__PRE_KEY_WHISPER_MESSAGE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__pre_key_whisper_message__descriptor) \ + , 0,0, 0,0, 0,0, 0,{0,NULL}, 0,{0,NULL}, 0,{0,NULL} } + + +struct _Textsecure__KeyExchangeMessage +{ + ProtobufCMessage base; + protobuf_c_boolean has_id; + uint32_t id; + protobuf_c_boolean has_basekey; + ProtobufCBinaryData basekey; + protobuf_c_boolean has_ratchetkey; + ProtobufCBinaryData ratchetkey; + protobuf_c_boolean has_identitykey; + ProtobufCBinaryData identitykey; + protobuf_c_boolean has_basekeysignature; + ProtobufCBinaryData basekeysignature; +}; +#define TEXTSECURE__KEY_EXCHANGE_MESSAGE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__key_exchange_message__descriptor) \ + , 0,0, 0,{0,NULL}, 0,{0,NULL}, 0,{0,NULL}, 0,{0,NULL} } + + +struct _Textsecure__SenderKeyMessage +{ + ProtobufCMessage base; + protobuf_c_boolean has_id; + uint32_t id; + protobuf_c_boolean has_iteration; + uint32_t iteration; + protobuf_c_boolean has_ciphertext; + ProtobufCBinaryData ciphertext; +}; +#define TEXTSECURE__SENDER_KEY_MESSAGE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__sender_key_message__descriptor) \ + , 0,0, 0,0, 0,{0,NULL} } + + +struct _Textsecure__SenderKeyDistributionMessage +{ + ProtobufCMessage base; + protobuf_c_boolean has_id; + uint32_t id; + protobuf_c_boolean has_iteration; + uint32_t iteration; + protobuf_c_boolean has_chainkey; + ProtobufCBinaryData chainkey; + protobuf_c_boolean has_signingkey; + ProtobufCBinaryData signingkey; +}; +#define TEXTSECURE__SENDER_KEY_DISTRIBUTION_MESSAGE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&textsecure__sender_key_distribution_message__descriptor) \ + , 0,0, 0,0, 0,{0,NULL}, 0,{0,NULL} } + + +/* Textsecure__WhisperMessage methods */ +void textsecure__whisper_message__init + (Textsecure__WhisperMessage *message); +size_t textsecure__whisper_message__get_packed_size + (const Textsecure__WhisperMessage *message); +size_t textsecure__whisper_message__pack + (const Textsecure__WhisperMessage *message, + uint8_t *out); +size_t textsecure__whisper_message__pack_to_buffer + (const Textsecure__WhisperMessage *message, + ProtobufCBuffer *buffer); +Textsecure__WhisperMessage * + textsecure__whisper_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void textsecure__whisper_message__free_unpacked + (Textsecure__WhisperMessage *message, + ProtobufCAllocator *allocator); +/* Textsecure__PreKeyWhisperMessage methods */ +void textsecure__pre_key_whisper_message__init + (Textsecure__PreKeyWhisperMessage *message); +size_t textsecure__pre_key_whisper_message__get_packed_size + (const Textsecure__PreKeyWhisperMessage *message); +size_t textsecure__pre_key_whisper_message__pack + (const Textsecure__PreKeyWhisperMessage *message, + uint8_t *out); +size_t textsecure__pre_key_whisper_message__pack_to_buffer + (const Textsecure__PreKeyWhisperMessage *message, + ProtobufCBuffer *buffer); +Textsecure__PreKeyWhisperMessage * + textsecure__pre_key_whisper_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void textsecure__pre_key_whisper_message__free_unpacked + (Textsecure__PreKeyWhisperMessage *message, + ProtobufCAllocator *allocator); +/* Textsecure__KeyExchangeMessage methods */ +void textsecure__key_exchange_message__init + (Textsecure__KeyExchangeMessage *message); +size_t textsecure__key_exchange_message__get_packed_size + (const Textsecure__KeyExchangeMessage *message); +size_t textsecure__key_exchange_message__pack + (const Textsecure__KeyExchangeMessage *message, + uint8_t *out); +size_t textsecure__key_exchange_message__pack_to_buffer + (const Textsecure__KeyExchangeMessage *message, + ProtobufCBuffer *buffer); +Textsecure__KeyExchangeMessage * + textsecure__key_exchange_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void textsecure__key_exchange_message__free_unpacked + (Textsecure__KeyExchangeMessage *message, + ProtobufCAllocator *allocator); +/* Textsecure__SenderKeyMessage methods */ +void textsecure__sender_key_message__init + (Textsecure__SenderKeyMessage *message); +size_t textsecure__sender_key_message__get_packed_size + (const Textsecure__SenderKeyMessage *message); +size_t textsecure__sender_key_message__pack + (const Textsecure__SenderKeyMessage *message, + uint8_t *out); +size_t textsecure__sender_key_message__pack_to_buffer + (const Textsecure__SenderKeyMessage *message, + ProtobufCBuffer *buffer); +Textsecure__SenderKeyMessage * + textsecure__sender_key_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void textsecure__sender_key_message__free_unpacked + (Textsecure__SenderKeyMessage *message, + ProtobufCAllocator *allocator); +/* Textsecure__SenderKeyDistributionMessage methods */ +void textsecure__sender_key_distribution_message__init + (Textsecure__SenderKeyDistributionMessage *message); +size_t textsecure__sender_key_distribution_message__get_packed_size + (const Textsecure__SenderKeyDistributionMessage *message); +size_t textsecure__sender_key_distribution_message__pack + (const Textsecure__SenderKeyDistributionMessage *message, + uint8_t *out); +size_t textsecure__sender_key_distribution_message__pack_to_buffer + (const Textsecure__SenderKeyDistributionMessage *message, + ProtobufCBuffer *buffer); +Textsecure__SenderKeyDistributionMessage * + textsecure__sender_key_distribution_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void textsecure__sender_key_distribution_message__free_unpacked + (Textsecure__SenderKeyDistributionMessage *message, + ProtobufCAllocator *allocator); +/* --- per-message closures --- */ + +typedef void (*Textsecure__WhisperMessage_Closure) + (const Textsecure__WhisperMessage *message, + void *closure_data); +typedef void (*Textsecure__PreKeyWhisperMessage_Closure) + (const Textsecure__PreKeyWhisperMessage *message, + void *closure_data); +typedef void (*Textsecure__KeyExchangeMessage_Closure) + (const Textsecure__KeyExchangeMessage *message, + void *closure_data); +typedef void (*Textsecure__SenderKeyMessage_Closure) + (const Textsecure__SenderKeyMessage *message, + void *closure_data); +typedef void (*Textsecure__SenderKeyDistributionMessage_Closure) + (const Textsecure__SenderKeyDistributionMessage *message, + void *closure_data); + +/* --- services --- */ + + +/* --- descriptors --- */ + +extern const ProtobufCMessageDescriptor textsecure__whisper_message__descriptor; +extern const ProtobufCMessageDescriptor textsecure__pre_key_whisper_message__descriptor; +extern const ProtobufCMessageDescriptor textsecure__key_exchange_message__descriptor; +extern const ProtobufCMessageDescriptor textsecure__sender_key_message__descriptor; +extern const ProtobufCMessageDescriptor textsecure__sender_key_distribution_message__descriptor; + +PROTOBUF_C__END_DECLS + + +#endif /* PROTOBUF_C_WhisperTextProtocol_2eproto__INCLUDED */ diff --git a/libs/libaxolotl/src/axolotl.c b/libs/libaxolotl/src/axolotl.c new file mode 100644 index 0000000000..7db1cbfc88 --- /dev/null +++ b/libs/libaxolotl/src/axolotl.c @@ -0,0 +1,1071 @@ +#include "axolotl.h" +#include "axolotl_internal.h" + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <stdarg.h> +#include <assert.h> +#include "utlist.h" +#include "utarray.h" + +#ifdef _WINDOWS +#include "Windows.h" +#include "WinBase.h" +#endif + +#ifdef DEBUG_REFCOUNT +int type_ref_count = 0; +int type_unref_count = 0; +#endif + +struct axolotl_store_context { + axolotl_context *global_context; + axolotl_session_store session_store; + axolotl_pre_key_store pre_key_store; + axolotl_signed_pre_key_store signed_pre_key_store; + axolotl_identity_key_store identity_key_store; + axolotl_sender_key_store sender_key_store; +}; + +void axolotl_type_init(axolotl_type_base *instance, + void (*destroy_func)(axolotl_type_base *instance)) +{ + instance->ref_count = 1; + instance->destroy = destroy_func; +#ifdef DEBUG_REFCOUNT + type_ref_count++; +#endif +} + +void axolotl_type_ref(axolotl_type_base *instance) +{ +#ifdef DEBUG_REFCOUNT + type_ref_count++; +#endif + assert(instance); + assert(instance->ref_count > 0); + instance->ref_count++; +} + +void axolotl_type_unref(axolotl_type_base *instance) +{ + if(instance) { +#ifdef DEBUG_REFCOUNT + type_unref_count++; +#endif + assert(instance->ref_count > 0); + if(instance->ref_count > 1) { + instance->ref_count--; + } + else { + instance->destroy(instance); + } + } +} + +#ifdef DEBUG_REFCOUNT +int axolotl_type_ref_count(axolotl_type_base *instance) +{ + return instance->ref_count; +} +#endif + +/*------------------------------------------------------------------------*/ + +axolotl_buffer *axolotl_buffer_alloc(size_t len) +{ + axolotl_buffer *buffer; + if(len > (SIZE_MAX - sizeof(struct axolotl_buffer)) / sizeof(uint8_t)) { + return 0; + } + + buffer = malloc(sizeof(struct axolotl_buffer) + (sizeof(uint8_t) * len)); + if(buffer) { + buffer->len = len; + } + return buffer; +} + +axolotl_buffer *axolotl_buffer_create(const uint8_t *data, size_t len) +{ + axolotl_buffer *buffer = axolotl_buffer_alloc(len); + if(!buffer) { + return 0; + } + + memcpy(buffer->data, data, len); + return buffer; +} + +axolotl_buffer *axolotl_buffer_copy(const axolotl_buffer *buffer) +{ + return axolotl_buffer_create(buffer->data, buffer->len); +} + +axolotl_buffer *axolotl_buffer_append(axolotl_buffer *buffer, const uint8_t *data, size_t len) +{ + size_t previous_size = buffer->len; + size_t previous_alloc = sizeof(struct axolotl_buffer) + (sizeof(uint8_t) * previous_size); + axolotl_buffer *tmp_buffer = realloc(buffer, previous_alloc + (sizeof(uint8_t) * len)); + if(!tmp_buffer) { + return 0; + } + + memcpy(tmp_buffer->data + previous_size, data, len); + tmp_buffer->len = previous_size + len; + return tmp_buffer; +} + +uint8_t *axolotl_buffer_data(axolotl_buffer *buffer) +{ + return buffer->data; +} + +size_t axolotl_buffer_len(axolotl_buffer *buffer) +{ + return buffer->len; +} + +int axolotl_buffer_compare(axolotl_buffer *buffer1, axolotl_buffer *buffer2) +{ + if(buffer1 == buffer2) { + return 0; + } + else if(buffer1 == 0 && buffer2 != 0) { + return -1; + } + else if(buffer1 != 0 && buffer2 == 0) { + return 1; + } + else { + if(buffer1->len < buffer2->len) { + return -1; + } + else if(buffer1->len > buffer2->len) { + return 1; + } + else { + return axolotl_constant_memcmp(buffer1->data, buffer2->data, buffer1->len); + } + } +} + +void axolotl_buffer_free(axolotl_buffer *buffer) +{ + if(buffer) { + free(buffer); + } +} + +void axolotl_buffer_bzero_free(axolotl_buffer *buffer) +{ + if(buffer) { + axolotl_explicit_bzero(buffer->data, buffer->len); + free(buffer); + } +} + +/*------------------------------------------------------------------------*/ + +typedef struct axolotl_buffer_list_node +{ + axolotl_buffer *buffer; + struct axolotl_buffer_list_node *next; +} axolotl_buffer_list_node; + +struct axolotl_buffer_list +{ + int size; + axolotl_buffer_list_node *head; +}; + +struct axolotl_int_list +{ + UT_array *values; +}; + +axolotl_buffer_list *axolotl_buffer_list_alloc() +{ + axolotl_buffer_list *list = malloc(sizeof(axolotl_buffer_list)); + if(list) { + memset(list, 0, sizeof(axolotl_buffer_list)); + } + return list; +} + +int axolotl_buffer_list_push(axolotl_buffer_list *list, axolotl_buffer *buffer) +{ + axolotl_buffer_list_node *node = 0; + + assert(list); + assert(buffer); + + node = malloc(sizeof(axolotl_buffer_list_node)); + + if(!node) { + return AX_ERR_NOMEM; + } + + node->buffer = buffer; + LL_PREPEND(list->head, node); + list->size++; + return 0; +} + +int axolotl_buffer_list_size(axolotl_buffer_list *list) +{ + assert(list); + return list->size; +} + +void axolotl_buffer_list_free(axolotl_buffer_list *list) +{ + axolotl_buffer_list_node *cur_node; + axolotl_buffer_list_node *tmp_node; + + assert(list); + + LL_FOREACH_SAFE(list->head, cur_node, tmp_node) { + LL_DELETE(list->head, cur_node); + if(cur_node->buffer) { + axolotl_buffer_free(cur_node->buffer); + } + free(cur_node); + } + free(list); +} +axolotl_int_list *axolotl_int_list_alloc(); + +/*------------------------------------------------------------------------*/ + +axolotl_int_list *axolotl_int_list_alloc() +{ + axolotl_int_list *list = malloc(sizeof(axolotl_int_list)); + if(!list) { + return 0; + } + memset(list, 0, sizeof(axolotl_int_list)); + utarray_new(list->values, &ut_int_icd); + return list; +} + +void axolotl_int_list_push_back(axolotl_int_list *list, int value) +{ + assert(list); + utarray_push_back(list->values, &value); +} + +unsigned int axolotl_int_list_size(axolotl_int_list *list) +{ + assert(list); + return utarray_len(list->values); +} + +int axolotl_int_list_at(axolotl_int_list *list, unsigned int index) +{ + int *value = 0; + + assert(list); + assert(index >= 0 && index < utarray_len(list->values)); + + value = (int *)utarray_eltptr(list->values, index); + + assert(value); + + return *value; +} + +void axolotl_int_list_free(axolotl_int_list *list) +{ + if(list) { + utarray_free(list->values); + free(list); + } +} + +/*------------------------------------------------------------------------*/ + +int axolotl_context_create(axolotl_context **context, void *user_data) +{ + *context = malloc(sizeof(axolotl_context)); + if(!(*context)) { + return AX_ERR_NOMEM; + } + memset(*context, 0, sizeof(axolotl_context)); + (*context)->user_data = user_data; +#ifdef DEBUG_REFCOUNT + type_ref_count = 0; + type_unref_count = 0; +#endif + return 0; +} + +int axolotl_context_set_crypto_provider(axolotl_context *context, const axolotl_crypto_provider *crypto_provider) +{ + assert(context); + if(!crypto_provider + || !crypto_provider->hmac_sha256_init_func + || !crypto_provider->hmac_sha256_update_func + || !crypto_provider->hmac_sha256_final_func + || !crypto_provider->hmac_sha256_cleanup_func) { + return AX_ERR_INVAL; + } + memcpy(&(context->crypto_provider), crypto_provider, sizeof(axolotl_crypto_provider)); + return 0; +} + +int axolotl_context_set_locking_functions(axolotl_context *context, + void (*lock)(void *user_data), void (*unlock)(void *user_data)) +{ + assert(context); + if((lock && !unlock) || (!lock && unlock)) { + return AX_ERR_INVAL; + } + + context->lock = lock; + context->unlock = unlock; + return 0; +} + +int axolotl_context_set_log_function(axolotl_context *context, + void (*log)(int level, const char *message, size_t len, void *user_data)) +{ + assert(context); + context->log = log; + return 0; +} + +void axolotl_context_destroy(axolotl_context *context) +{ +#ifdef DEBUG_REFCOUNT + fprintf(stderr, "Global REF count: %d\n", type_ref_count); + fprintf(stderr, "Global UNREF count: %d\n", type_unref_count); +#endif + if(context) { + free(context); + } +} + +/*------------------------------------------------------------------------*/ + +int axolotl_crypto_random(axolotl_context *context, uint8_t *data, size_t len) +{ + assert(context); + assert(context->crypto_provider.random_func); + return context->crypto_provider.random_func(data, len, context->crypto_provider.user_data); +} + +int axolotl_hmac_sha256_init(axolotl_context *context, void **hmac_context, const uint8_t *key, size_t key_len) +{ + assert(context); + assert(context->crypto_provider.hmac_sha256_init_func); + return context->crypto_provider.hmac_sha256_init_func(hmac_context, key, key_len, context->crypto_provider.user_data); +} + +int axolotl_hmac_sha256_update(axolotl_context *context, void *hmac_context, const uint8_t *data, size_t data_len) +{ + assert(context); + assert(context->crypto_provider.hmac_sha256_update_func); + return context->crypto_provider.hmac_sha256_update_func(hmac_context, data, data_len, context->crypto_provider.user_data); +} + +int axolotl_hmac_sha256_final(axolotl_context *context, void *hmac_context, axolotl_buffer **output) +{ + assert(context); + assert(context->crypto_provider.hmac_sha256_final_func); + return context->crypto_provider.hmac_sha256_final_func(hmac_context, output, context->crypto_provider.user_data); +} + +void axolotl_hmac_sha256_cleanup(axolotl_context *context, void *hmac_context) +{ + assert(context); + assert(context->crypto_provider.hmac_sha256_cleanup_func); + context->crypto_provider.hmac_sha256_cleanup_func(hmac_context, context->crypto_provider.user_data); +} + +int axolotl_sha512_digest(axolotl_context *context, axolotl_buffer **output, const uint8_t *data, size_t data_len) +{ + assert(context); + assert(context->crypto_provider.sha512_digest_func); + return context->crypto_provider.sha512_digest_func(output, data, data_len, context->crypto_provider.user_data); +} + +int axolotl_encrypt(axolotl_context *context, + axolotl_buffer **output, + int cipher, + const uint8_t *key, size_t key_len, + const uint8_t *iv, size_t iv_len, + const uint8_t *plaintext, size_t plaintext_len) +{ + assert(context); + assert(context->crypto_provider.encrypt_func); + return context->crypto_provider.encrypt_func( + output, cipher, key, key_len, iv, iv_len, + plaintext, plaintext_len, + context->crypto_provider.user_data); +} + +int axolotl_decrypt(axolotl_context *context, + axolotl_buffer **output, + int cipher, + const uint8_t *key, size_t key_len, + const uint8_t *iv, size_t iv_len, + const uint8_t *ciphertext, size_t ciphertext_len) +{ + assert(context); + assert(context->crypto_provider.decrypt_func); + return context->crypto_provider.decrypt_func( + output, cipher, key, key_len, iv, iv_len, + ciphertext, ciphertext_len, + context->crypto_provider.user_data); +} + +void axolotl_lock(axolotl_context *context) +{ + if(context->lock) { + context->lock(context->user_data); + } +} + +void axolotl_unlock(axolotl_context *context) +{ + if(context->unlock) { + context->unlock(context->user_data); + } +} + +void axolotl_log(axolotl_context *context, int level, const char *format, ...) +{ + char buf[256]; + int n; + if(context->log) { + va_list args; + va_start(args, format); + n = vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + if(n > 0) { + context->log(level, buf, strlen(buf), context->user_data); + } + } +} + +void axolotl_explicit_bzero(void *v, size_t n) +{ +#ifdef HAVE_SECUREZEROMEMORY + SecureZeroMemory(v, n); +#elif HAVE_MEMSET_S + memset_s(v, n, 0, n); +#else + volatile unsigned char *p = v; + while(n--) *p++ = 0; +#endif +} + +int axolotl_constant_memcmp(const void *s1, const void *s2, size_t n) +{ + size_t i; + const unsigned char *c1 = (const unsigned char *) s1; + const unsigned char *c2 = (const unsigned char *) s2; + unsigned char result = 0; + + for (i = 0; i < n; i++) { + result |= c1[i] ^ c2[i]; + } + + return result; +} + +void axolotl_str_serialize_protobuf(ProtobufCBinaryData *buffer, const char *str) +{ + assert(buffer); + assert(str); + buffer->data = (uint8_t *)str; + buffer->len = strlen(str); +} + +char *axolotl_str_deserialize_protobuf(ProtobufCBinaryData *buffer) +{ + char *str = 0; + assert(buffer); + + str = malloc(buffer->len + 1); + if(!str) { + return 0; + } + + memcpy(str, buffer->data, buffer->len); + str[buffer->len] = '\0'; + + return str; +} + +/*------------------------------------------------------------------------*/ + +int axolotl_store_context_create(axolotl_store_context **context, axolotl_context *global_context) +{ + assert(global_context); + *context = malloc(sizeof(axolotl_store_context)); + if(!(*context)) { + return AX_ERR_NOMEM; + } + memset(*context, 0, sizeof(axolotl_store_context)); + (*context)->global_context = global_context; + return 0; +} + +int axolotl_store_context_set_session_store(axolotl_store_context *context, const axolotl_session_store *store) +{ + if(!store) { + return AX_ERR_INVAL; + } + memcpy(&(context->session_store), store, sizeof(axolotl_session_store)); + return 0; +} + +int axolotl_store_context_set_pre_key_store(axolotl_store_context *context, const axolotl_pre_key_store *store) +{ + if(!store) { + return AX_ERR_INVAL; + } + memcpy(&(context->pre_key_store), store, sizeof(axolotl_pre_key_store)); + return 0; +} + +int axolotl_store_context_set_signed_pre_key_store(axolotl_store_context *context, const axolotl_signed_pre_key_store *store) +{ + if(!store) { + return AX_ERR_INVAL; + } + memcpy(&(context->signed_pre_key_store), store, sizeof(axolotl_signed_pre_key_store)); + return 0; +} + +int axolotl_store_context_set_identity_key_store(axolotl_store_context *context, const axolotl_identity_key_store *store) +{ + if(!store) { + return AX_ERR_INVAL; + } + memcpy(&(context->identity_key_store), store, sizeof(axolotl_identity_key_store)); + return 0; +} + +int axolotl_store_context_set_sender_key_store(axolotl_store_context *context, const axolotl_sender_key_store *store) +{ + if(!store) { + return AX_ERR_INVAL; + } + memcpy(&(context->sender_key_store), store, sizeof(axolotl_sender_key_store)); + return 0; +} + +void axolotl_store_context_destroy(axolotl_store_context *context) +{ + if(context) { + if(context->session_store.destroy_func) { + context->session_store.destroy_func(context->session_store.user_data); + } + if(context->pre_key_store.destroy_func) { + context->pre_key_store.destroy_func(context->pre_key_store.user_data); + } + if(context->signed_pre_key_store.destroy_func) { + context->signed_pre_key_store.destroy_func(context->signed_pre_key_store.user_data); + } + if(context->identity_key_store.destroy_func) { + context->identity_key_store.destroy_func(context->identity_key_store.user_data); + } + if(context->sender_key_store.destroy_func) { + context->sender_key_store.destroy_func(context->sender_key_store.user_data); + } + free(context); + } +} + +/*------------------------------------------------------------------------*/ + +int axolotl_session_load_session(axolotl_store_context *context, session_record **record, const axolotl_address *address) +{ + int result = 0; + axolotl_buffer *buffer = 0; + session_record *result_record = 0; + + assert(context); + assert(context->session_store.load_session_func); + + result = context->session_store.load_session_func( + &buffer, address, + context->session_store.user_data); + if(result < 0) { + goto complete; + } + + if(result == 0) { + if(buffer) { + result = AX_ERR_UNKNOWN; + goto complete; + } + result = session_record_create(&result_record, 0, context->global_context); + } + else if(result == 1) { + if(!buffer) { + result = -1; + goto complete; + } + result = session_record_deserialize(&result_record, + axolotl_buffer_data(buffer), axolotl_buffer_len(buffer), context->global_context); + } + else { + result = AX_ERR_UNKNOWN; + } + +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + if(result >= 0) { + *record = result_record; + } + return result; +} + +int axolotl_session_get_sub_device_sessions(axolotl_store_context *context, axolotl_int_list **sessions, const char *name, size_t name_len) +{ + assert(context); + assert(context->session_store.get_sub_device_sessions_func); + + return context->session_store.get_sub_device_sessions_func( + sessions, name, name_len, + context->session_store.user_data); +} + +int axolotl_session_store_session(axolotl_store_context *context, const axolotl_address *address, session_record *record) +{ + int result = 0; + axolotl_buffer *buffer = 0; + + assert(context); + assert(context->session_store.store_session_func); + assert(record); + + result = session_record_serialize(&buffer, record); + if(result < 0) { + goto complete; + } + + result = context->session_store.store_session_func( + address, + axolotl_buffer_data(buffer), axolotl_buffer_len(buffer), + context->session_store.user_data); + +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + + return result; +} + +int axolotl_session_contains_session(axolotl_store_context *context, const axolotl_address *address) +{ + assert(context); + assert(context->session_store.contains_session_func); + + return context->session_store.contains_session_func( + address, + context->session_store.user_data); +} + +int axolotl_session_delete_session(axolotl_store_context *context, const axolotl_address *address) +{ + assert(context); + assert(context->session_store.delete_session_func); + + return context->session_store.delete_session_func( + address, + context->session_store.user_data); +} + +int axolotl_session_delete_all_sessions(axolotl_store_context *context, const char *name, size_t name_len) +{ + assert(context); + assert(context->session_store.delete_all_sessions_func); + + return context->session_store.delete_all_sessions_func( + name, name_len, + context->session_store.user_data); +} + +/*------------------------------------------------------------------------*/ + +int axolotl_pre_key_load_key(axolotl_store_context *context, session_pre_key **pre_key, uint32_t pre_key_id) +{ + int result = 0; + axolotl_buffer *buffer = 0; + session_pre_key *result_key = 0; + + assert(context); + assert(context->pre_key_store.load_pre_key); + + result = context->pre_key_store.load_pre_key( + &buffer, pre_key_id, + context->pre_key_store.user_data); + if(result < 0) { + goto complete; + } + + result = session_pre_key_deserialize(&result_key, + axolotl_buffer_data(buffer), axolotl_buffer_len(buffer), context->global_context); + +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + if(result >= 0) { + *pre_key = result_key; + } + return result; +} + +int axolotl_pre_key_store_key(axolotl_store_context *context, session_pre_key *pre_key) +{ + int result = 0; + axolotl_buffer *buffer = 0; + uint32_t id = 0; + + assert(context); + assert(context->pre_key_store.store_pre_key); + assert(pre_key); + + id = session_pre_key_get_id(pre_key); + + result = session_pre_key_serialize(&buffer, pre_key); + if(result < 0) { + goto complete; + } + + result = context->pre_key_store.store_pre_key( + id, + axolotl_buffer_data(buffer), axolotl_buffer_len(buffer), + context->pre_key_store.user_data); + +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + + return result; +} + +int axolotl_pre_key_contains_key(axolotl_store_context *context, uint32_t pre_key_id) +{ + int result = 0; + + assert(context); + assert(context->pre_key_store.contains_pre_key); + + result = context->pre_key_store.contains_pre_key( + pre_key_id, context->pre_key_store.user_data); + + return result; +} + +int axolotl_pre_key_remove_key(axolotl_store_context *context, uint32_t pre_key_id) +{ + int result = 0; + + assert(context); + assert(context->pre_key_store.remove_pre_key); + + result = context->pre_key_store.remove_pre_key( + pre_key_id, context->pre_key_store.user_data); + + return result; +} + +/*------------------------------------------------------------------------*/ + +int axolotl_signed_pre_key_load_key(axolotl_store_context *context, session_signed_pre_key **pre_key, uint32_t signed_pre_key_id) +{ + int result = 0; + axolotl_buffer *buffer = 0; + session_signed_pre_key *result_key = 0; + + assert(context); + assert(context->signed_pre_key_store.load_signed_pre_key); + + result = context->signed_pre_key_store.load_signed_pre_key( + &buffer, signed_pre_key_id, + context->signed_pre_key_store.user_data); + if(result < 0) { + goto complete; + } + + result = session_signed_pre_key_deserialize(&result_key, + axolotl_buffer_data(buffer), axolotl_buffer_len(buffer), context->global_context); + +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + if(result >= 0) { + *pre_key = result_key; + } + return result; +} + +int axolotl_signed_pre_key_store_key(axolotl_store_context *context, session_signed_pre_key *pre_key) +{ + int result = 0; + axolotl_buffer *buffer = 0; + uint32_t id = 0; + + assert(context); + assert(context->signed_pre_key_store.store_signed_pre_key); + assert(pre_key); + + id = session_signed_pre_key_get_id(pre_key); + + result = session_signed_pre_key_serialize(&buffer, pre_key); + if(result < 0) { + goto complete; + } + + result = context->signed_pre_key_store.store_signed_pre_key( + id, + axolotl_buffer_data(buffer), axolotl_buffer_len(buffer), + context->signed_pre_key_store.user_data); + +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + + return result; +} + +int axolotl_signed_pre_key_contains_key(axolotl_store_context *context, uint32_t signed_pre_key_id) +{ + int result = 0; + + assert(context); + assert(context->signed_pre_key_store.contains_signed_pre_key); + + result = context->signed_pre_key_store.contains_signed_pre_key( + signed_pre_key_id, context->signed_pre_key_store.user_data); + + return result; +} + +int axolotl_signed_pre_key_remove_key(axolotl_store_context *context, uint32_t signed_pre_key_id) +{ + int result = 0; + + assert(context); + assert(context->signed_pre_key_store.remove_signed_pre_key); + + result = context->signed_pre_key_store.remove_signed_pre_key( + signed_pre_key_id, context->signed_pre_key_store.user_data); + + return result; +} + +/*------------------------------------------------------------------------*/ + +int axolotl_identity_get_key_pair(axolotl_store_context *context, ratchet_identity_key_pair **key_pair) +{ + int result = 0; + axolotl_buffer *public_buf = 0; + axolotl_buffer *private_buf = 0; + ec_public_key *public_key = 0; + ec_private_key *private_key = 0; + ratchet_identity_key_pair *result_key = 0; + + assert(context); + assert(context->identity_key_store.get_identity_key_pair); + + result = context->identity_key_store.get_identity_key_pair( + &public_buf, &private_buf, + context->identity_key_store.user_data); + if(result < 0) { + goto complete; + } + + result = curve_decode_point(&public_key, public_buf->data, public_buf->len, context->global_context); + if(result < 0) { + goto complete; + } + + result = curve_decode_private_point(&private_key, private_buf->data, private_buf->len, context->global_context); + if(result < 0) { + goto complete; + } + + result = ratchet_identity_key_pair_create(&result_key, public_key, private_key); + if(result < 0) { + goto complete; + } + +complete: + if(public_buf) { + axolotl_buffer_free(public_buf); + } + if(private_buf) { + axolotl_buffer_free(private_buf); + } + if(public_key) { + AXOLOTL_UNREF(public_key); + } + if(private_key) { + AXOLOTL_UNREF(private_key); + } + if(result >= 0) { + *key_pair = result_key; + } + return result; +} + +int axolotl_identity_get_local_registration_id(axolotl_store_context *context, uint32_t *registration_id) +{ + int result = 0; + + assert(context); + assert(context->identity_key_store.get_local_registration_id); + + result = context->identity_key_store.get_local_registration_id( + context->identity_key_store.user_data, registration_id); + + return result; +} + +int axolotl_identity_save_identity(axolotl_store_context *context, const char *name, size_t name_len, ec_public_key *identity_key) +{ + int result = 0; + axolotl_buffer *buffer = 0; + + assert(context); + assert(context->identity_key_store.save_identity); + + if(identity_key) { + result = ec_public_key_serialize(&buffer, identity_key); + if(result < 0) { + goto complete; + } + + result = context->identity_key_store.save_identity( + name, name_len, + axolotl_buffer_data(buffer), + axolotl_buffer_len(buffer), + context->identity_key_store.user_data); + } + else { + result = context->identity_key_store.save_identity( + name, name_len, 0, 0, + context->identity_key_store.user_data); + } + +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + + return result; +} + +int axolotl_identity_is_trusted_identity(axolotl_store_context *context, const char *name, size_t name_len, ec_public_key *identity_key) +{ + int result = 0; + axolotl_buffer *buffer = 0; + + assert(context); + assert(context->identity_key_store.is_trusted_identity); + + result = ec_public_key_serialize(&buffer, identity_key); + if(result < 0) { + goto complete; + } + + result = context->identity_key_store.is_trusted_identity( + name, name_len, + axolotl_buffer_data(buffer), + axolotl_buffer_len(buffer), + context->identity_key_store.user_data); +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + + return result; +} + +int axolotl_sender_key_store_key(axolotl_store_context *context, const axolotl_sender_key_name *sender_key_name, sender_key_record *record) +{ + int result = 0; + axolotl_buffer *buffer = 0; + + assert(context); + assert(context->sender_key_store.store_sender_key); + assert(record); + + result = sender_key_record_serialize(&buffer, record); + if(result < 0) { + goto complete; + } + + result = context->sender_key_store.store_sender_key( + sender_key_name, + axolotl_buffer_data(buffer), axolotl_buffer_len(buffer), + context->sender_key_store.user_data); + +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + + return result; +} + +int axolotl_sender_key_load_key(axolotl_store_context *context, sender_key_record **record, const axolotl_sender_key_name *sender_key_name) +{ + int result = 0; + axolotl_buffer *buffer = 0; + sender_key_record *result_record = 0; + + assert(context); + assert(context->sender_key_store.load_sender_key); + + result = context->sender_key_store.load_sender_key( + &buffer, sender_key_name, + context->sender_key_store.user_data); + if(result < 0) { + goto complete; + } + + if(result == 0) { + if(buffer) { + result = AX_ERR_UNKNOWN; + goto complete; + } + result = sender_key_record_create(&result_record, context->global_context); + } + else if(result == 1) { + if(!buffer) { + result = -1; + goto complete; + } + result = sender_key_record_deserialize(&result_record, + axolotl_buffer_data(buffer), axolotl_buffer_len(buffer), context->global_context); + } + else { + result = AX_ERR_UNKNOWN; + } + +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + if(result >= 0) { + *record = result_record; + } + return result; +} diff --git a/libs/libaxolotl/src/axolotl.h b/libs/libaxolotl/src/axolotl.h new file mode 100644 index 0000000000..7fe03691d2 --- /dev/null +++ b/libs/libaxolotl/src/axolotl.h @@ -0,0 +1,731 @@ +#ifndef AXOLOTL_H +#define AXOLOTL_H + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +#include "axolotl_types.h" +#include "ratchet.h" +#include "curve.h" +#include "session_record.h" +#include "session_pre_key.h" +#include "sender_key_record.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define AX_SUCCESS 0 + +/* Standard error codes with values that match errno.h equivalents */ +#define AX_ERR_NOMEM -12 /* Not enough space */ +#define AX_ERR_INVAL -22 /* Invalid argument */ + +/* Custom error codes for error conditions specific to the library */ +#define AX_ERR_UNKNOWN -1000 +#define AX_ERR_DUPLICATE_MESSAGE -1001 +#define AX_ERR_INVALID_KEY -1002 +#define AX_ERR_INVALID_KEY_ID -1003 +#define AX_ERR_INVALID_MAC -1004 +#define AX_ERR_INVALID_MESSAGE -1005 +#define AX_ERR_INVALID_VERSION -1006 +#define AX_ERR_LEGACY_MESSAGE -1007 +#define AX_ERR_NO_SESSION -1008 +#define AX_ERR_STALE_KEY_EXCHANGE -1009 +#define AX_ERR_UNTRUSTED_IDENTITY -1010 +#define AX_ERR_INVALID_PROTO_BUF -1100 +#define AX_ERR_FP_VERSION_MISMATCH -1200 +#define AX_ERR_FP_IDENT_MISMATCH -1201 + +/* + * Minimum negative error code value that this library may use. + * When implementing library callback functions, using values + * less than this constant will ensure that application-specific + * errors can be distinguished from library errors. + */ +#define AX_ERR_MINIMUM -9999 + +/* Log levels */ +#define AX_LOG_ERROR 0 +#define AX_LOG_WARNING 1 +#define AX_LOG_NOTICE 2 +#define AX_LOG_INFO 3 +#define AX_LOG_DEBUG 4 + +/* Mode settings for the crypto callbacks */ +#define AX_CIPHER_AES_CTR_NOPADDING 1 +#define AX_CIPHER_AES_CBC_PKCS5 2 + +void axolotl_type_ref(axolotl_type_base *instance); +void axolotl_type_unref(axolotl_type_base *instance); + +#ifdef DEBUG_REFCOUNT +int axolotl_type_ref_count(axolotl_type_base *instance); +#define AXOLOTL_REF(instance) do { \ + axolotl_type_ref((axolotl_type_base *)instance); \ + fprintf(stderr, "REF: " #instance " = %d\n", axolotl_type_ref_count((axolotl_type_base *)instance)); \ + } while (0) +#define AXOLOTL_UNREF(instance) do { \ + fprintf(stderr, "UNREF: " #instance " = %d\n", axolotl_type_ref_count((axolotl_type_base *)instance)); \ + axolotl_type_unref((axolotl_type_base *)instance); \ + instance = 0; \ + } while(0) +#else +#define AXOLOTL_REF(instance) axolotl_type_ref((axolotl_type_base *)instance) +#define AXOLOTL_UNREF(instance) do { axolotl_type_unref((axolotl_type_base *)instance); instance = 0; } while(0) +#endif + +/** + * Allocate a new buffer to store data of the provided length. + * + * @param len length of the buffer to allocate + * @return pointer to the allocated buffer, or 0 on failure + */ +axolotl_buffer *axolotl_buffer_alloc(size_t len); + +/** + * Create a new buffer and copy the provided data into it. + * + * @param data pointer to the start of the data + * @param len length of the data + * @return pointer to the allocated buffer, or 0 on failure + */ +axolotl_buffer *axolotl_buffer_create(const uint8_t *data, size_t len); + +/** + * Create a copy of an existing buffer. + * + * @param buffer the existing buffer to copy + * @return pointer to the updated buffer, or 0 on failure + */ +axolotl_buffer *axolotl_buffer_copy(const axolotl_buffer *buffer); + +/** + * Append the provided data to an existing buffer. + * Note: The underlying buffer is only expanded by an amount sufficient + * to hold the data being appended. There is no additional reserved space + * to reduce the need for memory allocations. + * + * @param buffer the existing buffer to append to + * @param data pointer to the start of the data + * @param len length of the data + * @return pointer to the updated buffer, or 0 on failure + */ +axolotl_buffer *axolotl_buffer_append(axolotl_buffer *buffer, const uint8_t *data, size_t len); + +/** + * Gets the data pointer for the buffer. + * This can be used to read and write data stored in the buffer. + * + * @param buffer pointer to the buffer instance + * @return data pointer + */ +uint8_t *axolotl_buffer_data(axolotl_buffer *buffer); + +/** + * Gets the length of the data stored within the buffer. + * + * @param buffer pointer to the buffer instance + * @return data length + */ +size_t axolotl_buffer_len(axolotl_buffer *buffer); + +/** + * Compare two buffers. + * + * @param buffer1 first buffer to compare + * @param buffer2 second buffer to compare + * @return 0 if the two buffers are equal, negative or positive otherwise + */ +int axolotl_buffer_compare(axolotl_buffer *buffer1, axolotl_buffer *buffer2); + +/** + * Free the data buffer. + * + * @param buffer pointer to the buffer instance to free + */ +void axolotl_buffer_free(axolotl_buffer *buffer); + +/** + * Zero and free the data buffer. + * This function should be used when the buffer contains sensitive + * data, to make sure the memory is cleared before being freed. + * + * @param buffer pointer to the buffer instance to free + */ +void axolotl_buffer_bzero_free(axolotl_buffer *buffer); + +/** + * Allocate a new buffer list. + * + * @return pointer to the allocated buffer, or 0 on failure + */ +axolotl_buffer_list *axolotl_buffer_list_alloc(); + +/** + * Push the provided buffer onto the head of the list. + * + * @param list the buffer list + * @param buffer the buffer to push + * @return 0 on success, or negative on failure + */ +int axolotl_buffer_list_push(axolotl_buffer_list *list, axolotl_buffer *buffer); + +/** + * Gets the size of the buffer list. + * + * @param list the buffer list + * @return the size of the list + */ +int axolotl_buffer_list_size(axolotl_buffer_list *list); + +/** + * Free the buffer list, including all the buffers added to it. + * + * @param list the buffer list + */ +void axolotl_buffer_list_free(axolotl_buffer_list *list); + +/** + * Allocate a new int list + * + * @return pointer to the allocated buffer, or 0 on failure + */ +axolotl_int_list *axolotl_int_list_alloc(); + +/** + * Push a new value onto the end of the list + * + * @param list the list + * @param value the value to push + */ +void axolotl_int_list_push_back(axolotl_int_list *list, int value); + +/** + * Gets the size of the list. + * + * @param list the list + * @return the size of the list + */ +unsigned int axolotl_int_list_size(axolotl_int_list *list); + +/** + * Gets the value of the element at a particular index in the list + * + * @param list the list + * @param index the index within the list + * @return the value + */ +int axolotl_int_list_at(axolotl_int_list *list, unsigned int index); + +/** + * Free the int list + * @param list the list to free + */ +void axolotl_int_list_free(axolotl_int_list *list); + +typedef struct axolotl_crypto_provider { + /** + * Callback for a secure random number generator. + * This function shall fill the provided buffer with random bytes. + * + * @param data pointer to the output buffer + * @param len size of the output buffer + * @return 0 on success, negative on failure + */ + int (*random_func)(uint8_t *data, size_t len, void *user_data); + + /** + * Callback for an HMAC-SHA256 implementation. + * This function shall initialize an HMAC context with the provided key. + * + * @param hmac_context private HMAC context pointer + * @param key pointer to the key + * @param key_len length of the key + * @return 0 on success, negative on failure + */ + int (*hmac_sha256_init_func)(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data); + + /** + * Callback for an HMAC-SHA256 implementation. + * This function shall update the HMAC context with the provided data + * + * @param hmac_context private HMAC context pointer + * @param data pointer to the data + * @param data_len length of the data + * @return 0 on success, negative on failure + */ + int (*hmac_sha256_update_func)(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data); + + /** + * Callback for an HMAC-SHA256 implementation. + * This function shall finalize an HMAC calculation and populate the output + * buffer with the result. + * + * @param hmac_context private HMAC context pointer + * @param output buffer to be allocated and populated with the result + * @return 0 on success, negative on failure + */ + int (*hmac_sha256_final_func)(void *hmac_context, axolotl_buffer **output, void *user_data); + + /** + * Callback for an HMAC-SHA256 implementation. + * This function shall free the private context allocated in + * hmac_sha256_init_func. + * + * @param hmac_context private HMAC context pointer + */ + void (*hmac_sha256_cleanup_func)(void *hmac_context, void *user_data); + + /** + * Callback for a SHA512 message digest implementation. + * This function is currently only used by the fingerprint generator. + * + * @param output buffer to be allocated and populated with the ciphertext + * @param data pointer to the data + * @param data_len length of the data + * @return 0 on success, negative on failure + */ + int (*sha512_digest_func)(axolotl_buffer **output, const uint8_t *data, size_t data_len, void *user_data); + + /** + * Callback for an AES encryption implementation. + * + * @param output buffer to be allocated and populated with the ciphertext + * @param cipher specific cipher variant to use, either AX_CIPHER_AES_CTR_NOPADDING or AX_CIPHER_AES_CBC_PKCS5 + * @param key the encryption key + * @param key_len length of the encryption key + * @param iv the initialization vector + * @param iv_len length of the initialization vector + * @param plaintext the plaintext to encrypt + * @param plaintext_len length of the plaintext + * @return 0 on success, negative on failure + */ + int (*encrypt_func)(axolotl_buffer **output, + int cipher, + const uint8_t *key, size_t key_len, + const uint8_t *iv, size_t iv_len, + const uint8_t *plaintext, size_t plaintext_len, + void *user_data); + + /** + * Callback for an AES decryption implementation. + * + * @param output buffer to be allocated and populated with the plaintext + * @param cipher specific cipher variant to use, either AX_CIPHER_AES_CTR_NOPADDING or AX_CIPHER_AES_CBC_PKCS5 + * @param key the encryption key + * @param key_len length of the encryption key + * @param iv the initialization vector + * @param iv_len length of the initialization vector + * @param ciphertext the ciphertext to decrypt + * @param ciphertext_len length of the ciphertext + * @return 0 on success, negative on failure + */ + int (*decrypt_func)(axolotl_buffer **output, + int cipher, + const uint8_t *key, size_t key_len, + const uint8_t *iv, size_t iv_len, + const uint8_t *ciphertext, size_t ciphertext_len, + void *user_data); + + /** User data pointer */ + void *user_data; +} axolotl_crypto_provider; + +typedef struct axolotl_session_store { + /** + * Returns a copy of the serialized session record corresponding to the + * provided recipient ID + device ID tuple. + * + * @param record pointer to a freshly allocated buffer containing the + * serialized session record. Unset if no record was found. + * The axolotl library is responsible for freeing this buffer. + * @param address the address of the remote client + * @return 1 if the session was loaded, 0 if the session was not found, negative on failure + */ + int (*load_session_func)(axolotl_buffer **record, const axolotl_address *address, void *user_data); + + /** + * Returns all known devices with active sessions for a recipient + * + * @param pointer to an array that will be allocated and populated with the result + * @param name the name of the remote client + * @param name_len the length of the name + * @return size of the sessions array, or negative on failure + */ + int (*get_sub_device_sessions_func)(axolotl_int_list **sessions, const char *name, size_t name_len, void *user_data); + + /** + * Commit to storage the session record for a given + * recipient ID + device ID tuple. + * + * @param address the address of the remote client + * @param record pointer to a buffer containing the serialized session + * record for the remote client + * @param record_len length of the serialized session record + * @return 0 on success, negative on failure + */ + int (*store_session_func)(const axolotl_address *address, uint8_t *record, size_t record_len, void *user_data); + + /** + * Determine whether there is a committed session record for a + * recipient ID + device ID tuple. + * + * @param address the address of the remote client + * @return 1 if a session record exists, 0 otherwise. + */ + int (*contains_session_func)(const axolotl_address *address, void *user_data); + + /** + * Remove a session record for a recipient ID + device ID tuple. + * + * @param address the address of the remote client + * @return 1 if a session was deleted, 0 if a session was not deleted, negative on error + */ + int (*delete_session_func)(const axolotl_address *address, void *user_data); + + /** + * Remove the session records corresponding to all devices of a recipient ID. + * + * @param name the name of the remote client + * @param name_len the length of the name + * @return the number of deleted sessions on success, negative on failure + */ + int (*delete_all_sessions_func)(const char *name, size_t name_len, void *user_data); + + /** + * Function called to perform cleanup when the data store context is being + * destroyed. + */ + void (*destroy_func)(void *user_data); + + /** User data pointer */ + void *user_data; +} axolotl_session_store; + +typedef struct axolotl_pre_key_store { + /** + * Load a local serialized PreKey record. + * + * @param record pointer to a newly allocated buffer containing the record, + * if found. Unset if no record was found. + * The axolotl library is responsible for freeing this buffer. + * @param pre_key_id the ID of the local serialized PreKey record + * @retval AX_SUCCESS if the key was found + * @retval AX_ERR_INVALID_KEY_ID if the key could not be found + */ + int (*load_pre_key)(axolotl_buffer **record, uint32_t pre_key_id, void *user_data); + + /** + * Store a local serialized PreKey record. + * + * @param pre_key_id the ID of the PreKey record to store. + * @param record pointer to a buffer containing the serialized record + * @param record_len length of the serialized record + * @return 0 on success, negative on failure + */ + int (*store_pre_key)(uint32_t pre_key_id, uint8_t *record, size_t record_len, void *user_data); + + /** + * Determine whether there is a committed PreKey record matching the + * provided ID. + * + * @param pre_key_id A PreKey record ID. + * @return 1 if the store has a record for the PreKey ID, 0 otherwise + */ + int (*contains_pre_key)(uint32_t pre_key_id, void *user_data); + + /** + * Delete a PreKey record from local storage. + * + * @param pre_key_id The ID of the PreKey record to remove. + * @return 0 on success, negative on failure + */ + int (*remove_pre_key)(uint32_t pre_key_id, void *user_data); + + /** + * Function called to perform cleanup when the data store context is being + * destroyed. + */ + void (*destroy_func)(void *user_data); + + /** User data pointer */ + void *user_data; +} axolotl_pre_key_store; + +typedef struct axolotl_signed_pre_key_store { + /** + * Load a local serialized signed PreKey record. + * + * @param record pointer to a newly allocated buffer containing the record, + * if found. Unset if no record was found. + * The axolotl library is responsible for freeing this buffer. + * @param signed_pre_key_id the ID of the local signed PreKey record + * @retval AX_SUCCESS if the key was found + * @retval AX_ERR_INVALID_KEY_ID if the key could not be found + */ + int (*load_signed_pre_key)(axolotl_buffer **record, uint32_t signed_pre_key_id, void *user_data); + + /** + * Store a local serialized signed PreKey record. + * + * @param signed_pre_key_id the ID of the signed PreKey record to store + * @param record pointer to a buffer containing the serialized record + * @param record_len length of the serialized record + * @return 0 on success, negative on failure + */ + int (*store_signed_pre_key)(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *user_data); + + /** + * Determine whether there is a committed signed PreKey record matching + * the provided ID. + * + * @param signed_pre_key_id A signed PreKey record ID. + * @return 1 if the store has a record for the signed PreKey ID, 0 otherwise + */ + int (*contains_signed_pre_key)(uint32_t signed_pre_key_id, void *user_data); + + /** + * Delete a SignedPreKeyRecord from local storage. + * + * @param signed_pre_key_id The ID of the signed PreKey record to remove. + * @return 0 on success, negative on failure + */ + int (*remove_signed_pre_key)(uint32_t signed_pre_key_id, void *user_data); + + /** + * Function called to perform cleanup when the data store context is being + * destroyed. + */ + void (*destroy_func)(void *user_data); + + /** User data pointer */ + void *user_data; +} axolotl_signed_pre_key_store; + +typedef struct axolotl_identity_key_store { + /** + * Get the local client's identity key pair. + * + * @param public_data pointer to a newly allocated buffer containing the + * public key, if found. Unset if no record was found. + * The axolotl library is responsible for freeing this buffer. + * @param private_data pointer to a newly allocated buffer containing the + * private key, if found. Unset if no record was found. + * The axolotl library is responsible for freeing this buffer. + * @return 0 on success, negative on failure + */ + int (*get_identity_key_pair)(axolotl_buffer **public_data, axolotl_buffer **private_data, void *user_data); + + /** + * Return the local client's registration ID. + * + * Clients should maintain a registration ID, a random number + * between 1 and 16380 that's generated once at install time. + * + * @param registration_id pointer to be set to the local client's + * registration ID, if it was successfully retrieved. + * @return 0 on success, negative on failure + */ + int (*get_local_registration_id)(void *user_data, uint32_t *registration_id); + + /** + * Save a remote client's identity key + * <p> + * Store a remote client's identity key as trusted. + * The value of key_data may be null. In this case remove the key data + * from the identity store, but retain any metadata that may be kept + * alongside it. + * + * @param name the name of the remote client + * @param name_len the length of the name + * @param key_data Pointer to the remote client's identity key, may be null + * @param key_len Length of the remote client's identity key + * @return 0 on success, negative on failure + */ + int (*save_identity)(const char *name, size_t name_len, uint8_t *key_data, size_t key_len, void *user_data); + + /** + * Verify a remote client's identity key. + * + * Determine whether a remote client's identity is trusted. Convention is + * that the TextSecure protocol is 'trust on first use.' This means that + * an identity key is considered 'trusted' if there is no entry for the recipient + * in the local store, or if it matches the saved key for a recipient in the local + * store. Only if it mismatches an entry in the local store is it considered + * 'untrusted.' + * + * @param name the name of the remote client + * @param name_len the length of the name + * @param identityKey The identity key to verify. + * @param key_data Pointer to the identity key to verify + * @param key_len Length of the identity key to verify + * @return 1 if trusted, 0 if untrusted, negative on failure + */ + int (*is_trusted_identity)(const char *name, size_t name_len, uint8_t *key_data, size_t key_len, void *user_data); + + /** + * Function called to perform cleanup when the data store context is being + * destroyed. + */ + void (*destroy_func)(void *user_data); + + /** User data pointer */ + void *user_data; +} axolotl_identity_key_store; + +typedef struct axolotl_sender_key_store { + /** + * Store a serialized sender key record for a given + * (groupId + senderId + deviceId) tuple. + * + * @param sender_key_name the (groupId + senderId + deviceId) tuple + * @param record pointer to a buffer containing the serialized record + * @param record_len length of the serialized record + * @return 0 on success, negative on failure + */ + int (*store_sender_key)(const axolotl_sender_key_name *sender_key_name, uint8_t *record, size_t record_len, void *user_data); + + /** + * Returns a copy of the sender key record corresponding to the + * (groupId + senderId + deviceId) tuple. + * + * @param record pointer to a newly allocated buffer containing the record, + * if found. Unset if no record was found. + * The axolotl library is responsible for freeing this buffer. + * @param sender_key_name the (groupId + senderId + deviceId) tuple + * @return 1 if the record was loaded, 0 if the record was not found, negative on failure + */ + int (*load_sender_key)(axolotl_buffer **record, const axolotl_sender_key_name *sender_key_name, void *user_data); + + /** + * Function called to perform cleanup when the data store context is being + * destroyed. + */ + void (*destroy_func)(void *user_data); + + /** User data pointer */ + void *user_data; +} axolotl_sender_key_store; + +/** + * Create a new instance of the global library context. + */ +int axolotl_context_create(axolotl_context **context, void *user_data); + +/** + * Set the crypto provider to be used by the AXOLOTL library. + * + * @param crypto_provider Populated structure of crypto provider function + * pointers. The contents of this structure are copied, so the caller + * does not need to maintain its instance. + * @return 0 on success, negative on failure + */ +int axolotl_context_set_crypto_provider(axolotl_context *context, const axolotl_crypto_provider *crypto_provider); + +/** + * Set the locking functions to be used by the AXOLOTL library for + * synchronization. + * + * Note: These functions must allow recursive locking (e.g. PTHREAD_MUTEX_RECURSIVE) + * + * @param lock function to lock a mutex + * @param unlock function to unlock a mutex + * @return 0 on success, negative on failure + */ +int axolotl_context_set_locking_functions(axolotl_context *context, + void (*lock)(void *user_data), void (*unlock)(void *user_data)); + +/** + * Set the log function to be used by the AXOLOTL library for logging. + * + * @return 0 on success, negative on failure + */ +int axolotl_context_set_log_function(axolotl_context *context, + void (*log)(int level, const char *message, size_t len, void *user_data)); + +void axolotl_context_destroy(axolotl_context *context); + +/** + * Create a new instance of the AXOLOTL data store interface. + */ +int axolotl_store_context_create(axolotl_store_context **context, axolotl_context *global_context); + +int axolotl_store_context_set_session_store(axolotl_store_context *context, const axolotl_session_store *store); +int axolotl_store_context_set_pre_key_store(axolotl_store_context *context, const axolotl_pre_key_store *store); +int axolotl_store_context_set_signed_pre_key_store(axolotl_store_context *context, const axolotl_signed_pre_key_store *store); +int axolotl_store_context_set_identity_key_store(axolotl_store_context *context, const axolotl_identity_key_store *store); +int axolotl_store_context_set_sender_key_store(axolotl_store_context *context, const axolotl_sender_key_store *store); + +void axolotl_store_context_destroy(axolotl_store_context *context); + +/* + * Interface to the session store. + * These functions will use the callbacks in the provided + * axolotl_store_context instance and operate in terms of higher level + * library data structures. + */ + +int axolotl_session_load_session(axolotl_store_context *context, session_record **record, const axolotl_address *address); +int axolotl_session_get_sub_device_sessions(axolotl_store_context *context, axolotl_int_list **sessions, const char *name, size_t name_len); +int axolotl_session_store_session(axolotl_store_context *context, const axolotl_address *address, session_record *record); +int axolotl_session_contains_session(axolotl_store_context *context, const axolotl_address *address); +int axolotl_session_delete_session(axolotl_store_context *context, const axolotl_address *address); +int axolotl_session_delete_all_sessions(axolotl_store_context *context, const char *name, size_t name_len); + + +/* + * Interface to the pre-key store. + * These functions will use the callbacks in the provided + * axolotl_store_context instance and operate in terms of higher level + * library data structures. + */ + +int axolotl_pre_key_load_key(axolotl_store_context *context, session_pre_key **pre_key, uint32_t pre_key_id); +int axolotl_pre_key_store_key(axolotl_store_context *context, session_pre_key *pre_key); +int axolotl_pre_key_contains_key(axolotl_store_context *context, uint32_t pre_key_id); +int axolotl_pre_key_remove_key(axolotl_store_context *context, uint32_t pre_key_id); + + +/* + * Interface to the signed pre-key store. + * These functions will use the callbacks in the provided + * axolotl_store_context instance and operate in terms of higher level + * library data structures. + */ + +int axolotl_signed_pre_key_load_key(axolotl_store_context *context, session_signed_pre_key **pre_key, uint32_t signed_pre_key_id); +int axolotl_signed_pre_key_store_key(axolotl_store_context *context, session_signed_pre_key *pre_key); +int axolotl_signed_pre_key_contains_key(axolotl_store_context *context, uint32_t signed_pre_key_id); +int axolotl_signed_pre_key_remove_key(axolotl_store_context *context, uint32_t signed_pre_key_id); + + +/* + * Interface to the identity key store. + * These functions will use the callbacks in the provided + * axolotl_store_context instance and operate in terms of higher level + * library data structures. + */ + +int axolotl_identity_get_key_pair(axolotl_store_context *context, ratchet_identity_key_pair **key_pair); +int axolotl_identity_get_local_registration_id(axolotl_store_context *context, uint32_t *registration_id); +int axolotl_identity_save_identity(axolotl_store_context *context, const char *name, size_t name_len, ec_public_key *identity_key); +int axolotl_identity_is_trusted_identity(axolotl_store_context *context, const char *name, size_t name_len, ec_public_key *identity_key); + + +/* + * Interface to the sender key store. + * These functions will use the callbacks in the provided + * axolotl_store_context instance and operate in terms of higher level + * library data structures. + */ + +int axolotl_sender_key_store_key(axolotl_store_context *context, const axolotl_sender_key_name *sender_key_name, sender_key_record *record); +int axolotl_sender_key_load_key(axolotl_store_context *context, sender_key_record **record, const axolotl_sender_key_name *sender_key_name); + +#ifdef __cplusplus +} +#endif + +#endif /* AXOLOTL_H */ diff --git a/libs/libaxolotl/src/axolotl_internal.h b/libs/libaxolotl/src/axolotl_internal.h new file mode 100644 index 0000000000..3ecddbda7f --- /dev/null +++ b/libs/libaxolotl/src/axolotl_internal.h @@ -0,0 +1,82 @@ +#ifndef AXOLOTL_INTERNAL_H +#define AXOLOTL_INTERNAL_H + +#include "protobuf-c/protobuf-c.h" +#include "axolotl.h" +#include "LocalStorageProtocol.pb-c.h" + +struct axolotl_type_base { + unsigned int ref_count; + void (*destroy)(axolotl_type_base *instance); +}; + +void axolotl_type_init(axolotl_type_base *instance, + void (*destroy_func)(axolotl_type_base *instance)); + +#define AXOLOTL_INIT(instance, destroy_func) axolotl_type_init((axolotl_type_base *)instance, destroy_func) + +struct axolotl_buffer { + size_t len; + uint8_t data[]; +}; + +struct axolotl_context { + axolotl_crypto_provider crypto_provider; + void (*lock)(void *user_data); + void (*unlock)(void *user_data); + void (*log)(int level, const char *message, size_t len, void *user_data); + void *user_data; +}; + +int axolotl_crypto_random(axolotl_context *context, uint8_t *data, size_t len); +int axolotl_hmac_sha256_init(axolotl_context *context, void **hmac_context, const uint8_t *key, size_t key_len); +int axolotl_hmac_sha256_update(axolotl_context *context, void *hmac_context, const uint8_t *data, size_t data_len); +int axolotl_hmac_sha256_final(axolotl_context *context, void *hmac_context, axolotl_buffer **output); +void axolotl_hmac_sha256_cleanup(axolotl_context *context, void *hmac_context); + +int axolotl_sha512_digest(axolotl_context *context, axolotl_buffer **output, const uint8_t *data, size_t data_len); + +int axolotl_encrypt(axolotl_context *context, + axolotl_buffer **output, + int cipher, + const uint8_t *key, size_t key_len, + const uint8_t *iv, size_t iv_len, + const uint8_t *plaintext, size_t plaintext_len); + +int axolotl_decrypt(axolotl_context *context, + axolotl_buffer **output, + int cipher, + const uint8_t *key, size_t key_len, + const uint8_t *iv, size_t iv_len, + const uint8_t *ciphertext, size_t ciphertext_len); + +void axolotl_lock(axolotl_context *context); +void axolotl_unlock(axolotl_context *context); +void axolotl_log(axolotl_context *context, int level, const char *format, ...); +void axolotl_explicit_bzero(void *v, size_t n); +int axolotl_constant_memcmp(const void *s1, const void *s2, size_t n); + +/*------------------------------------------------------------------------*/ + +/* + * Functions used for internal protocol buffers serialization support. + */ + +int ec_public_key_serialize_protobuf(ProtobufCBinaryData *buffer, const ec_public_key *key); +int ec_private_key_serialize_protobuf(ProtobufCBinaryData *buffer, const ec_private_key *key); + +int ratchet_chain_key_get_key_protobuf(const ratchet_chain_key *chain_key, ProtobufCBinaryData *buffer); +int ratchet_root_key_get_key_protobuf(const ratchet_root_key *root_key, ProtobufCBinaryData *buffer); + +int session_state_serialize_prepare(session_state *state, Textsecure__SessionStructure *session_structure); +void session_state_serialize_prepare_free(Textsecure__SessionStructure *session_structure); +int session_state_deserialize_protobuf(session_state **state, Textsecure__SessionStructure *session_structure, axolotl_context *global_context); + +int sender_key_state_serialize_prepare(sender_key_state *state, Textsecure__SenderKeyStateStructure *state_structure); +void sender_key_state_serialize_prepare_free(Textsecure__SenderKeyStateStructure *state_structure); +int sender_key_state_deserialize_protobuf(sender_key_state **state, Textsecure__SenderKeyStateStructure *state_structure, axolotl_context *global_context); + +void axolotl_str_serialize_protobuf(ProtobufCBinaryData *buffer, const char *str); +char *axolotl_str_deserialize_protobuf(ProtobufCBinaryData *buffer); + +#endif /* AXOLOTL_INTERNAL_H */ diff --git a/libs/libaxolotl/src/axolotl_types.h b/libs/libaxolotl/src/axolotl_types.h new file mode 100644 index 0000000000..9421a87c14 --- /dev/null +++ b/libs/libaxolotl/src/axolotl_types.h @@ -0,0 +1,133 @@ +#ifndef AXOLOTL_TYPES_H +#define AXOLOTL_TYPES_H + +#include <stdint.h> +#include <stdlib.h> + +#ifndef _WINDOWS +#include <unistd.h> +#else +#include <basetsd.h> +typedef SSIZE_T ssize_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Base library types + */ +typedef struct axolotl_type_base axolotl_type_base; +typedef struct axolotl_buffer axolotl_buffer; +typedef struct axolotl_buffer_list axolotl_buffer_list; +typedef struct axolotl_int_list axolotl_int_list; + +/* + * Global context for the AXOLOTL library + */ +typedef struct axolotl_context axolotl_context; + +/* + * Context for the AXOLOTL data store implementation + */ +typedef struct axolotl_store_context axolotl_store_context; + +/* + * Address of an AXOLOTL message recipient + */ +typedef struct axolotl_address { + const char *name; + size_t name_len; + int32_t device_id; +} axolotl_address; + +/* + * A representation of a (group + sender + device) tuple + */ +typedef struct axolotl_sender_key_name { + const char *group_id; + size_t group_id_len; + axolotl_address sender; +} axolotl_sender_key_name; + +/* + * Curve key types + */ +typedef struct ec_public_key ec_public_key; +typedef struct ec_private_key ec_private_key; +typedef struct ec_key_pair ec_key_pair; + +/* + * HKDF types + */ +typedef struct hkdf_context hkdf_context; + +/* + * Key helper types + */ +typedef struct axolotl_key_helper_pre_key_list_node axolotl_key_helper_pre_key_list_node; + +/* + * Protocol types + */ +typedef struct key_exchange_message key_exchange_message; +typedef struct ciphertext_message ciphertext_message; +typedef struct whisper_message whisper_message; +typedef struct pre_key_whisper_message pre_key_whisper_message; +typedef struct sender_key_message sender_key_message; +typedef struct sender_key_distribution_message sender_key_distribution_message; + +/* + * Ratchet types + */ +#define RATCHET_CIPHER_KEY_LENGTH 32 +#define RATCHET_MAC_KEY_LENGTH 32 +#define RATCHET_IV_LENGTH 16 + +typedef struct ratchet_chain_key ratchet_chain_key; +typedef struct ratchet_root_key ratchet_root_key; +typedef struct ratchet_identity_key_pair ratchet_identity_key_pair; + +typedef struct ratchet_message_keys { + uint8_t cipher_key[RATCHET_CIPHER_KEY_LENGTH]; + uint8_t mac_key[RATCHET_MAC_KEY_LENGTH]; + uint8_t iv[RATCHET_IV_LENGTH]; + uint32_t counter; +} ratchet_message_keys; + +/* + * Session types + */ +typedef struct session_pre_key session_pre_key; +typedef struct session_signed_pre_key session_signed_pre_key; +typedef struct session_pre_key_bundle session_pre_key_bundle; +typedef struct session_builder session_builder; +typedef struct session_record session_record; +typedef struct session_record_state_node session_record_state_node; +typedef struct session_state session_state; +typedef struct session_cipher session_cipher; + +/* + * Group types + */ +typedef struct sender_message_key sender_message_key; +typedef struct sender_chain_key sender_chain_key; +typedef struct sender_key_state sender_key_state; +typedef struct sender_key_record sender_key_record; +typedef struct group_session_builder group_session_builder; +typedef struct group_cipher group_cipher; + +/* + * Fingerprint types + */ +typedef struct fingerprint fingerprint; +typedef struct displayable_fingerprint displayable_fingerprint; +typedef struct scannable_fingerprint scannable_fingerprint; +typedef struct fingerprint_generator fingerprint_generator; + +#ifdef __cplusplus +} +#endif + +#endif // AXOLOTL_TYPES_H diff --git a/libs/libaxolotl/src/curve.c b/libs/libaxolotl/src/curve.c new file mode 100644 index 0000000000..241a5b983b --- /dev/null +++ b/libs/libaxolotl/src/curve.c @@ -0,0 +1,433 @@ +#include "curve.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "protobuf-c/protobuf-c.h" + +#include "curve25519/curve25519-donna.h" +#include "curve25519/ed25519/additions/curve_sigs.h" +#include "axolotl_internal.h" + +#define DJB_TYPE 0x05 +#define DJB_KEY_LEN 32 + +struct ec_public_key +{ + axolotl_type_base base; + uint8_t data[DJB_KEY_LEN]; +}; + +struct ec_private_key +{ + axolotl_type_base base; + uint8_t data[DJB_KEY_LEN]; +}; + +struct ec_key_pair +{ + axolotl_type_base base; + ec_public_key *public_key; + ec_private_key *private_key; +}; + +int curve_decode_point(ec_public_key **public_key, const uint8_t *key_data, size_t key_len, axolotl_context *global_context) +{ + ec_public_key *key = 0; + + if(key_len > 0 && key_data[0] != DJB_TYPE) { + axolotl_log(global_context, AX_LOG_ERROR, "Invalid key type: %d", key_data[0]); + return AX_ERR_INVALID_KEY; + } + + if(key_len != DJB_KEY_LEN + 1) { + axolotl_log(global_context, AX_LOG_ERROR, "Invalid key length: %d", key_len); + return AX_ERR_INVALID_KEY; + } + + key = malloc(sizeof(ec_public_key)); + if(!key) { + return AX_ERR_NOMEM; + } + + AXOLOTL_INIT(key, ec_public_key_destroy); + + memcpy(key->data, key_data + 1, DJB_KEY_LEN); + + *public_key = key; + + return 0; +} + +int ec_public_key_compare(const ec_public_key *key1, const ec_public_key *key2) +{ + if(key1 == key2) { + return 0; + } + else if(key1 == 0 && key2 != 0) { + return -1; + } + else if(key1 != 0 && key2 == 0) { + return 1; + } + else { + return axolotl_constant_memcmp(key1->data, key2->data, DJB_KEY_LEN); + } +} + +int ec_public_key_memcmp(const ec_public_key *key1, const ec_public_key *key2) +{ + if(key1 == key2) { + return 0; + } + else if(key1 == 0 && key2 != 0) { + return -1; + } + else if(key1 != 0 && key2 == 0) { + return 1; + } + else { + return memcmp(key1->data, key2->data, DJB_KEY_LEN); + } +} + +int ec_public_key_serialize(axolotl_buffer **buffer, const ec_public_key *key) +{ + axolotl_buffer *buf = 0; + uint8_t *data = 0; + + buf = axolotl_buffer_alloc(sizeof(uint8_t) * (DJB_KEY_LEN + 1)); + if(!buf) { + return AX_ERR_NOMEM; + } + + data = axolotl_buffer_data(buf); + data[0] = DJB_TYPE; + memcpy(data + 1, key->data, DJB_KEY_LEN); + + *buffer = buf; + + return 0; +} + +int ec_public_key_serialize_protobuf(ProtobufCBinaryData *buffer, const ec_public_key *key) +{ + size_t len = 0; + uint8_t *data = 0; + + assert(buffer); + assert(key); + + len = sizeof(uint8_t) * (DJB_KEY_LEN + 1); + data = malloc(len); + if(!data) { + return AX_ERR_NOMEM; + } + + data[0] = DJB_TYPE; + memcpy(data + 1, key->data, DJB_KEY_LEN); + + buffer->data = data; + buffer->len = len; + return 0; +} + +void ec_public_key_destroy(axolotl_type_base *type) +{ + ec_public_key *public_key = (ec_public_key *)type; + free(public_key); +} + +int curve_decode_private_point(ec_private_key **private_key, const uint8_t *key_data, size_t key_len, axolotl_context *global_context) +{ + ec_private_key *key = 0; + + if(key_len != DJB_KEY_LEN) { + axolotl_log(global_context, AX_LOG_ERROR, "Invalid key length: %d", key_len); + return AX_ERR_INVALID_KEY; + } + + key = malloc(sizeof(ec_private_key)); + if(!key) { + return AX_ERR_NOMEM; + } + + AXOLOTL_INIT(key, ec_private_key_destroy); + + memcpy(key->data, key_data, DJB_KEY_LEN); + + *private_key = key; + + return 0; +} + +int ec_private_key_compare(const ec_private_key *key1, const ec_private_key *key2) +{ + if(key1 == key2) { + return 0; + } + else if(key1 == 0 && key2 != 0) { + return -1; + } + else if(key1 != 0 && key2 == 0) { + return 1; + } + else { + return axolotl_constant_memcmp(key1->data, key2->data, DJB_KEY_LEN); + } +} + +int ec_private_key_serialize(axolotl_buffer **buffer, const ec_private_key *key) +{ + axolotl_buffer *buf = 0; + uint8_t *data = 0 ; + + buf = axolotl_buffer_alloc(sizeof(uint8_t) * DJB_KEY_LEN); + if(!buf) { + return AX_ERR_NOMEM; + } + + data = axolotl_buffer_data(buf); + memcpy(data, key->data, DJB_KEY_LEN); + + *buffer = buf; + + return 0; +} + +int ec_private_key_serialize_protobuf(ProtobufCBinaryData *buffer, const ec_private_key *key) +{ + size_t len = 0; + uint8_t *data = 0; + + assert(buffer); + assert(key); + + len = sizeof(uint8_t) * DJB_KEY_LEN; + data = malloc(len); + if(!data) { + return AX_ERR_NOMEM; + } + + memcpy(data, key->data, DJB_KEY_LEN); + + buffer->data = data; + buffer->len = len; + return 0; +} + +void ec_private_key_destroy(axolotl_type_base *type) +{ + ec_private_key *private_key = (ec_private_key *)type; + axolotl_explicit_bzero(private_key, sizeof(ec_private_key)); + free(private_key); +} + +int ec_key_pair_create(ec_key_pair **key_pair, ec_public_key *public_key, ec_private_key *private_key) +{ + ec_key_pair *result = malloc(sizeof(ec_key_pair)); + if(!result) { + return AX_ERR_NOMEM; + } + + AXOLOTL_INIT(result, ec_key_pair_destroy); + result->public_key = public_key; + AXOLOTL_REF(public_key); + result->private_key = private_key; + AXOLOTL_REF(private_key); + + *key_pair = result; + + return 0; +} + +ec_public_key *ec_key_pair_get_public(const ec_key_pair *key_pair) +{ + return key_pair->public_key; +} + +ec_private_key *ec_key_pair_get_private(const ec_key_pair *key_pair) +{ + return key_pair->private_key; +} + +void ec_key_pair_destroy(axolotl_type_base *type) +{ + ec_key_pair *key_pair = (ec_key_pair *)type; + AXOLOTL_UNREF(key_pair->public_key); + AXOLOTL_UNREF(key_pair->private_key); + free(key_pair); +} + +int curve_generate_private_key(axolotl_context *context, ec_private_key **private_key) +{ + int result = 0; + ec_private_key *key = 0; + + assert(context); + + key = malloc(sizeof(ec_private_key)); + if(!key) { + result = AX_ERR_NOMEM; + goto complete; + } + + AXOLOTL_INIT(key, ec_private_key_destroy); + + result = axolotl_crypto_random(context, key->data, DJB_KEY_LEN); + if(result < 0) { + goto complete; + } + + key->data[0] &= 248; + key->data[31] &= 127; + key->data[31] |= 64; + + *private_key = key; + +complete: + return result; +} + +int curve_generate_public_key(ec_public_key **public_key, const ec_private_key *private_key) +{ + static const uint8_t basepoint[32] = {9}; + int result = 0; + + ec_public_key *key = malloc(sizeof(ec_public_key)); + if(!key) { + return AX_ERR_NOMEM; + } + + AXOLOTL_INIT(key, ec_public_key_destroy); + + result = curve25519_donna(key->data, private_key->data, basepoint); + + if(result == 0) { + *public_key = key; + return 0; + } + else { + if(key) { + AXOLOTL_UNREF(key); + } + return AX_ERR_UNKNOWN; + } +} + +int curve_generate_key_pair(axolotl_context *context, ec_key_pair **key_pair) +{ + int result = 0; + ec_key_pair *pair_result = 0; + ec_private_key *key_private = 0; + ec_public_key *key_public = 0; + + assert(context); + + result = curve_generate_private_key(context, &key_private); + if(result < 0) { + goto complete; + } + + result = curve_generate_public_key(&key_public, key_private); + if(result < 0) { + goto complete; + } + + result = ec_key_pair_create(&pair_result, key_public, key_private); + if(result < 0) { + goto complete; + } + +complete: + if(key_public) { + AXOLOTL_UNREF(key_public); + } + if(key_private) { + AXOLOTL_UNREF(key_private); + } + + if(result < 0) { + if(pair_result) { + AXOLOTL_UNREF(pair_result); + } + } + else { + *key_pair = pair_result; + } + + return result; +} + +int curve_calculate_agreement(uint8_t **shared_key_data, const ec_public_key *public_key, const ec_private_key *private_key) +{ + uint8_t *key = 0; + int result = 0; + + if(!public_key || !private_key) { + return AX_ERR_INVALID_KEY; + } + + key = malloc(DJB_KEY_LEN); + if(!key) { + return AX_ERR_NOMEM; + } + + result = curve25519_donna(key, private_key->data, public_key->data); + + if(result == 0) { + *shared_key_data = key; + return DJB_KEY_LEN; + } + else { + if(key) { + free(key); + } + return AX_ERR_UNKNOWN; + } +} + +int curve_verify_signature(const ec_public_key *signing_key, + const uint8_t *message_data, size_t message_len, + const uint8_t *signature_data, size_t signature_len) +{ + if(signature_len != 64) { + return AX_ERR_INVAL; + } + + return curve25519_verify(signature_data, signing_key->data, message_data, message_len) == 0; +} + +int curve_calculate_signature(axolotl_context *context, + axolotl_buffer **signature, + const ec_private_key *signing_key, + const uint8_t *message_data, size_t message_len) +{ + int result = 0; + uint8_t random_data[CURVE_SIGNATURE_LEN]; + axolotl_buffer *buffer = 0; + + result = axolotl_crypto_random(context, random_data, sizeof(random_data)); + if(result < 0) { + goto complete; + } + + buffer = axolotl_buffer_alloc(CURVE_SIGNATURE_LEN); + if(!buffer) { + result = AX_ERR_NOMEM; + goto complete; + } + + result = curve25519_sign(axolotl_buffer_data(buffer), signing_key->data, message_data, message_len, random_data); + +complete: + if(result < 0) { + if(buffer) { + axolotl_buffer_free(buffer); + } + } + else { + *signature = buffer; + } + return result; +} diff --git a/libs/libaxolotl/src/curve.h b/libs/libaxolotl/src/curve.h new file mode 100644 index 0000000000..851d05a319 --- /dev/null +++ b/libs/libaxolotl/src/curve.h @@ -0,0 +1,73 @@ +#ifndef CURVE_H +#define CURVE_H + +#include <stdint.h> +#include <stddef.h> +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CURVE_SIGNATURE_LEN 64 + +int curve_decode_point(ec_public_key **public_key, const uint8_t *key_data, size_t key_len, axolotl_context *global_context); +int ec_public_key_compare(const ec_public_key *key1, const ec_public_key *key2); +int ec_public_key_memcmp(const ec_public_key *key1, const ec_public_key *key2); + +/** + * Serialize the public key into a buffer that can be stored. + * The format of this data is compatible with the input format of + * curve_decode_point(). + * + * @param buffer Pointer to a buffer that will be allocated by this function + * and filled with the contents of the key. The caller is responsible for + * freeing this buffer with axolotl_buffer_free(). + * @param key Key to serialize + * @return 0 on success, negative on failure + */ +int ec_public_key_serialize(axolotl_buffer **buffer, const ec_public_key *key); + +void ec_public_key_destroy(axolotl_type_base *type); + +int curve_decode_private_point(ec_private_key **private_key, const uint8_t *key_data, size_t key_len, axolotl_context *global_context); +int ec_private_key_compare(const ec_private_key *key1, const ec_private_key *key2); + +/** + * Serialize the private key into a buffer that can be stored. + * The format of this data is compatible with the input format of + * curve_decode_private_point(). + * + * @param buffer Pointer to a buffer that will be allocated by this function + * and filled with the contents of the key. The caller is responsible for + * freeing this buffer with axolotl_buffer_free(). + * @param key Key to serialize + * @return 0 on success, negative on failure + */ +int ec_private_key_serialize(axolotl_buffer **buffer, const ec_private_key *key); + +void ec_private_key_destroy(axolotl_type_base *type); + +int ec_key_pair_create(ec_key_pair **key_pair, ec_public_key *public_key, ec_private_key *private_key); +ec_public_key *ec_key_pair_get_public(const ec_key_pair *key_pair); +ec_private_key *ec_key_pair_get_private(const ec_key_pair *key_pair); +void ec_key_pair_destroy(axolotl_type_base *type); + +int curve_generate_private_key(axolotl_context *context, ec_private_key **private_key); +int curve_generate_public_key(ec_public_key **public_key, const ec_private_key *private_key); +int curve_generate_key_pair(axolotl_context *context, ec_key_pair **key_pair); + +int curve_calculate_agreement(uint8_t **shared_key_data, const ec_public_key *public_key, const ec_private_key *private_key); +int curve_verify_signature(const ec_public_key *signing_key, + const uint8_t *message_data, size_t message_len, + const uint8_t *signature_data, size_t signature_len); +int curve_calculate_signature(axolotl_context *context, + axolotl_buffer **signature, + const ec_private_key *signing_key, + const uint8_t *message_data, size_t message_len); + +#ifdef __cplusplus +} +#endif + +#endif /* CURVE_H */ diff --git a/libs/libaxolotl/src/curve25519/CMakeLists.txt b/libs/libaxolotl/src/curve25519/CMakeLists.txt new file mode 100644 index 0000000000..50eae030a6 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/CMakeLists.txt @@ -0,0 +1,65 @@ +IF(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-variable -Wno-unused-function -Wno-shadow") +ENDIF(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") + +IF(CMAKE_COMPILER_IS_GNUCC) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-sign-compare -Wno-sign-conversion") +ENDIF(CMAKE_COMPILER_IS_GNUCC) + +IF(CMAKE_C_COMPILER_ID MATCHES "Clang") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-shorten-64-to-32") +ENDIF(CMAKE_C_COMPILER_ID MATCHES "Clang") + +set(curve25519_SRCS + curve25519-donna.c +) + +set(ed25519_SRCS + ed25519/fe_0.c + ed25519/fe_1.c + ed25519/fe_add.c + ed25519/fe_cmov.c + ed25519/fe_copy.c + ed25519/fe_frombytes.c + ed25519/fe_invert.c + ed25519/fe_isnegative.c + ed25519/fe_isnonzero.c + ed25519/fe_mul.c + ed25519/fe_neg.c + ed25519/fe_pow22523.c + ed25519/fe_sq.c + ed25519/fe_sq2.c + ed25519/fe_sub.c + ed25519/fe_tobytes.c + ed25519/ge_add.c + ed25519/ge_double_scalarmult.c + ed25519/ge_frombytes.c + ed25519/ge_madd.c + ed25519/ge_msub.c + ed25519/ge_p1p1_to_p2.c + ed25519/ge_p1p1_to_p3.c + ed25519/ge_p2_0.c + ed25519/ge_p2_dbl.c + ed25519/ge_p3_0.c + ed25519/ge_p3_dbl.c + ed25519/ge_p3_to_cached.c + ed25519/ge_p3_to_p2.c + ed25519/ge_p3_tobytes.c + ed25519/ge_precomp_0.c + ed25519/ge_scalarmult_base.c + ed25519/ge_sub.c + ed25519/ge_tobytes.c + ed25519/open.c + ed25519/sc_muladd.c + ed25519/sc_reduce.c + ed25519/sign.c + ed25519/additions/compare.c + ed25519/additions/curve_sigs.c + ed25519/additions/sign_modified.c + ed25519/additions/zeroize.c + ed25519/additions/crypto_hash_sha512.h + ed25519/nacl_sha512/blocks.c + ed25519/nacl_sha512/hash.c +) + +add_library(curve25519 OBJECT ${curve25519_SRCS} ${ed25519_SRCS}) diff --git a/libs/libaxolotl/src/curve25519/curve25519-donna.c b/libs/libaxolotl/src/curve25519/curve25519-donna.c new file mode 100644 index 0000000000..f2c2ac58e6 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/curve25519-donna.c @@ -0,0 +1,870 @@ +/* Copyright 2008, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * curve25519-donna: Curve25519 elliptic curve, public key function + * + * http://code.google.com/p/curve25519-donna/ + * + * Adam Langley <agl@imperialviolet.org> + * + * Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to> + * + * More information about curve25519 can be found here + * http://cr.yp.to/ecdh.html + * + * djb's sample implementation of curve25519 is written in a special assembly + * language called qhasm and uses the floating point registers. + * + * This is, almost, a clean room reimplementation from the curve25519 paper. It + * uses many of the tricks described therein. Only the crecip function is taken + * from the sample implementation. */ + +#include <string.h> +#include <stdint.h> + +#ifdef _MSC_VER +#define inline __inline +#endif + +typedef uint8_t u8; +typedef int32_t s32; +typedef int64_t limb; + +/* Field element representation: + * + * Field elements are written as an array of signed, 64-bit limbs, least + * significant first. The value of the field element is: + * x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ... + * + * i.e. the limbs are 26, 25, 26, 25, ... bits wide. */ + +/* Sum two numbers: output += in */ +static void fsum(limb *output, const limb *in) { + unsigned i; + for (i = 0; i < 10; i += 2) { + output[0+i] = output[0+i] + in[0+i]; + output[1+i] = output[1+i] + in[1+i]; + } +} + +/* Find the difference of two numbers: output = in - output + * (note the order of the arguments!). */ +static void fdifference(limb *output, const limb *in) { + unsigned i; + for (i = 0; i < 10; ++i) { + output[i] = in[i] - output[i]; + } +} + +/* Multiply a number by a scalar: output = in * scalar */ +static void fscalar_product(limb *output, const limb *in, const limb scalar) { + unsigned i; + for (i = 0; i < 10; ++i) { + output[i] = in[i] * scalar; + } +} + +/* Multiply two numbers: output = in2 * in + * + * output must be distinct to both inputs. The inputs are reduced coefficient + * form, the output is not. + * + * output[x] <= 14 * the largest product of the input limbs. */ +static void fproduct(limb *output, const limb *in2, const limb *in) { + output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]); + output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) + + ((limb) ((s32) in2[1])) * ((s32) in[0]); + output[2] = 2 * ((limb) ((s32) in2[1])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[2]) + + ((limb) ((s32) in2[2])) * ((s32) in[0]); + output[3] = ((limb) ((s32) in2[1])) * ((s32) in[2]) + + ((limb) ((s32) in2[2])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[0]); + output[4] = ((limb) ((s32) in2[2])) * ((s32) in[2]) + + 2 * (((limb) ((s32) in2[1])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[1])) + + ((limb) ((s32) in2[0])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[0]); + output[5] = ((limb) ((s32) in2[2])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[0]); + output[6] = 2 * (((limb) ((s32) in2[3])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[1])) + + ((limb) ((s32) in2[2])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[2]) + + ((limb) ((s32) in2[0])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[0]); + output[7] = ((limb) ((s32) in2[3])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[0]); + output[8] = ((limb) ((s32) in2[4])) * ((s32) in[4]) + + 2 * (((limb) ((s32) in2[3])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[1])) + + ((limb) ((s32) in2[2])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[2]) + + ((limb) ((s32) in2[0])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[0]); + output[9] = ((limb) ((s32) in2[4])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[4]) + + ((limb) ((s32) in2[3])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[0]); + output[10] = 2 * (((limb) ((s32) in2[5])) * ((s32) in[5]) + + ((limb) ((s32) in2[3])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[1])) + + ((limb) ((s32) in2[4])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[4]) + + ((limb) ((s32) in2[2])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[2]); + output[11] = ((limb) ((s32) in2[5])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[5]) + + ((limb) ((s32) in2[4])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[4]) + + ((limb) ((s32) in2[3])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[2]); + output[12] = ((limb) ((s32) in2[6])) * ((s32) in[6]) + + 2 * (((limb) ((s32) in2[5])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[5]) + + ((limb) ((s32) in2[3])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[3])) + + ((limb) ((s32) in2[4])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[4]); + output[13] = ((limb) ((s32) in2[6])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[6]) + + ((limb) ((s32) in2[5])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[5]) + + ((limb) ((s32) in2[4])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[4]); + output[14] = 2 * (((limb) ((s32) in2[7])) * ((s32) in[7]) + + ((limb) ((s32) in2[5])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[5])) + + ((limb) ((s32) in2[6])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[6]); + output[15] = ((limb) ((s32) in2[7])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[7]) + + ((limb) ((s32) in2[6])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[6]); + output[16] = ((limb) ((s32) in2[8])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in2[7])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[7])); + output[17] = ((limb) ((s32) in2[8])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[8]); + output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]); +} + +/* Reduce a long form to a short form by taking the input mod 2^255 - 19. + * + * On entry: |output[i]| < 14*2^54 + * On exit: |output[0..8]| < 280*2^54 */ +static void freduce_degree(limb *output) { + /* Each of these shifts and adds ends up multiplying the value by 19. + * + * For output[0..8], the absolute entry value is < 14*2^54 and we add, at + * most, 19*14*2^54 thus, on exit, |output[0..8]| < 280*2^54. */ + output[8] += output[18] << 4; + output[8] += output[18] << 1; + output[8] += output[18]; + output[7] += output[17] << 4; + output[7] += output[17] << 1; + output[7] += output[17]; + output[6] += output[16] << 4; + output[6] += output[16] << 1; + output[6] += output[16]; + output[5] += output[15] << 4; + output[5] += output[15] << 1; + output[5] += output[15]; + output[4] += output[14] << 4; + output[4] += output[14] << 1; + output[4] += output[14]; + output[3] += output[13] << 4; + output[3] += output[13] << 1; + output[3] += output[13]; + output[2] += output[12] << 4; + output[2] += output[12] << 1; + output[2] += output[12]; + output[1] += output[11] << 4; + output[1] += output[11] << 1; + output[1] += output[11]; + output[0] += output[10] << 4; + output[0] += output[10] << 1; + output[0] += output[10]; +} + +#if (-1 & 3) != 3 +#error "This code only works on a two's complement system" +#endif + +/* return v / 2^26, using only shifts and adds. + * + * On entry: v can take any value. */ +static inline limb +div_by_2_26(const limb v) +{ + /* High word of v; no shift needed. */ + const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32); + /* Set to all 1s if v was negative; else set to 0s. */ + const int32_t sign = ((int32_t) highword) >> 31; + /* Set to 0x3ffffff if v was negative; else set to 0. */ + const int32_t roundoff = ((uint32_t) sign) >> 6; + /* Should return v / (1<<26) */ + return (v + roundoff) >> 26; +} + +/* return v / (2^25), using only shifts and adds. + * + * On entry: v can take any value. */ +static inline limb +div_by_2_25(const limb v) +{ + /* High word of v; no shift needed*/ + const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32); + /* Set to all 1s if v was negative; else set to 0s. */ + const int32_t sign = ((int32_t) highword) >> 31; + /* Set to 0x1ffffff if v was negative; else set to 0. */ + const int32_t roundoff = ((uint32_t) sign) >> 7; + /* Should return v / (1<<25) */ + return (v + roundoff) >> 25; +} + +/* return v / (2^25), using only shifts and adds. + * + * On entry: v can take any value. */ +static inline s32 +div_s32_by_2_25(const s32 v) +{ + const s32 roundoff = ((uint32_t)(v >> 31)) >> 7; + return (v + roundoff) >> 25; +} + +/* Reduce all coefficients of the short form input so that |x| < 2^26. + * + * On entry: |output[i]| < 280*2^54 */ +static void freduce_coefficients(limb *output) { + unsigned i; + + output[10] = 0; + + for (i = 0; i < 10; i += 2) { + limb over = div_by_2_26(output[i]); + /* The entry condition (that |output[i]| < 280*2^54) means that over is, at + * most, 280*2^28 in the first iteration of this loop. This is added to the + * next limb and we can approximate the resulting bound of that limb by + * 281*2^54. */ + output[i] -= over << 26; + output[i+1] += over; + + /* For the first iteration, |output[i+1]| < 281*2^54, thus |over| < + * 281*2^29. When this is added to the next limb, the resulting bound can + * be approximated as 281*2^54. + * + * For subsequent iterations of the loop, 281*2^54 remains a conservative + * bound and no overflow occurs. */ + over = div_by_2_25(output[i+1]); + output[i+1] -= over << 25; + output[i+2] += over; + } + /* Now |output[10]| < 281*2^29 and all other coefficients are reduced. */ + output[0] += output[10] << 4; + output[0] += output[10] << 1; + output[0] += output[10]; + + output[10] = 0; + + /* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19*281*2^29 + * So |over| will be no more than 2^16. */ + { + limb over = div_by_2_26(output[0]); + output[0] -= over << 26; + output[1] += over; + } + + /* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 2^16 < 2^26. The + * bound on |output[1]| is sufficient to meet our needs. */ +} + +/* A helpful wrapper around fproduct: output = in * in2. + * + * On entry: |in[i]| < 2^27 and |in2[i]| < 2^27. + * + * output must be distinct to both inputs. The output is reduced degree + * (indeed, one need only provide storage for 10 limbs) and |output[i]| < 2^26. */ +static void +fmul(limb *output, const limb *in, const limb *in2) { + limb t[19]; + fproduct(t, in, in2); + /* |t[i]| < 14*2^54 */ + freduce_degree(t); + freduce_coefficients(t); + /* |t[i]| < 2^26 */ + memcpy(output, t, sizeof(limb) * 10); +} + +/* Square a number: output = in**2 + * + * output must be distinct from the input. The inputs are reduced coefficient + * form, the output is not. + * + * output[x] <= 14 * the largest product of the input limbs. */ +static void fsquare_inner(limb *output, const limb *in) { + output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]); + output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]); + output[2] = 2 * (((limb) ((s32) in[1])) * ((s32) in[1]) + + ((limb) ((s32) in[0])) * ((s32) in[2])); + output[3] = 2 * (((limb) ((s32) in[1])) * ((s32) in[2]) + + ((limb) ((s32) in[0])) * ((s32) in[3])); + output[4] = ((limb) ((s32) in[2])) * ((s32) in[2]) + + 4 * ((limb) ((s32) in[1])) * ((s32) in[3]) + + 2 * ((limb) ((s32) in[0])) * ((s32) in[4]); + output[5] = 2 * (((limb) ((s32) in[2])) * ((s32) in[3]) + + ((limb) ((s32) in[1])) * ((s32) in[4]) + + ((limb) ((s32) in[0])) * ((s32) in[5])); + output[6] = 2 * (((limb) ((s32) in[3])) * ((s32) in[3]) + + ((limb) ((s32) in[2])) * ((s32) in[4]) + + ((limb) ((s32) in[0])) * ((s32) in[6]) + + 2 * ((limb) ((s32) in[1])) * ((s32) in[5])); + output[7] = 2 * (((limb) ((s32) in[3])) * ((s32) in[4]) + + ((limb) ((s32) in[2])) * ((s32) in[5]) + + ((limb) ((s32) in[1])) * ((s32) in[6]) + + ((limb) ((s32) in[0])) * ((s32) in[7])); + output[8] = ((limb) ((s32) in[4])) * ((s32) in[4]) + + 2 * (((limb) ((s32) in[2])) * ((s32) in[6]) + + ((limb) ((s32) in[0])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[1])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[5]))); + output[9] = 2 * (((limb) ((s32) in[4])) * ((s32) in[5]) + + ((limb) ((s32) in[3])) * ((s32) in[6]) + + ((limb) ((s32) in[2])) * ((s32) in[7]) + + ((limb) ((s32) in[1])) * ((s32) in[8]) + + ((limb) ((s32) in[0])) * ((s32) in[9])); + output[10] = 2 * (((limb) ((s32) in[5])) * ((s32) in[5]) + + ((limb) ((s32) in[4])) * ((s32) in[6]) + + ((limb) ((s32) in[2])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[3])) * ((s32) in[7]) + + ((limb) ((s32) in[1])) * ((s32) in[9]))); + output[11] = 2 * (((limb) ((s32) in[5])) * ((s32) in[6]) + + ((limb) ((s32) in[4])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[8]) + + ((limb) ((s32) in[2])) * ((s32) in[9])); + output[12] = ((limb) ((s32) in[6])) * ((s32) in[6]) + + 2 * (((limb) ((s32) in[4])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[5])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[9]))); + output[13] = 2 * (((limb) ((s32) in[6])) * ((s32) in[7]) + + ((limb) ((s32) in[5])) * ((s32) in[8]) + + ((limb) ((s32) in[4])) * ((s32) in[9])); + output[14] = 2 * (((limb) ((s32) in[7])) * ((s32) in[7]) + + ((limb) ((s32) in[6])) * ((s32) in[8]) + + 2 * ((limb) ((s32) in[5])) * ((s32) in[9])); + output[15] = 2 * (((limb) ((s32) in[7])) * ((s32) in[8]) + + ((limb) ((s32) in[6])) * ((s32) in[9])); + output[16] = ((limb) ((s32) in[8])) * ((s32) in[8]) + + 4 * ((limb) ((s32) in[7])) * ((s32) in[9]); + output[17] = 2 * ((limb) ((s32) in[8])) * ((s32) in[9]); + output[18] = 2 * ((limb) ((s32) in[9])) * ((s32) in[9]); +} + +/* fsquare sets output = in^2. + * + * On entry: The |in| argument is in reduced coefficients form and |in[i]| < + * 2^27. + * + * On exit: The |output| argument is in reduced coefficients form (indeed, one + * need only provide storage for 10 limbs) and |out[i]| < 2^26. */ +static void +fsquare(limb *output, const limb *in) { + limb t[19]; + fsquare_inner(t, in); + /* |t[i]| < 14*2^54 because the largest product of two limbs will be < + * 2^(27+27) and fsquare_inner adds together, at most, 14 of those + * products. */ + freduce_degree(t); + freduce_coefficients(t); + /* |t[i]| < 2^26 */ + memcpy(output, t, sizeof(limb) * 10); +} + +/* Take a little-endian, 32-byte number and expand it into polynomial form */ +static void +fexpand(limb *output, const u8 *input) { +#define F(n,start,shift,mask) \ + output[n] = ((((limb) input[start + 0]) | \ + ((limb) input[start + 1]) << 8 | \ + ((limb) input[start + 2]) << 16 | \ + ((limb) input[start + 3]) << 24) >> shift) & mask; + F(0, 0, 0, 0x3ffffff); + F(1, 3, 2, 0x1ffffff); + F(2, 6, 3, 0x3ffffff); + F(3, 9, 5, 0x1ffffff); + F(4, 12, 6, 0x3ffffff); + F(5, 16, 0, 0x1ffffff); + F(6, 19, 1, 0x3ffffff); + F(7, 22, 3, 0x1ffffff); + F(8, 25, 4, 0x3ffffff); + F(9, 28, 6, 0x1ffffff); +#undef F +} + +#if (-32 >> 1) != -16 +#error "This code only works when >> does sign-extension on negative numbers" +#endif + +/* s32_eq returns 0xffffffff iff a == b and zero otherwise. */ +static s32 s32_eq(s32 a, s32 b) { + a = ~(a ^ b); + a &= a << 16; + a &= a << 8; + a &= a << 4; + a &= a << 2; + a &= a << 1; + return a >> 31; +} + +/* s32_gte returns 0xffffffff if a >= b and zero otherwise, where a and b are + * both non-negative. */ +static s32 s32_gte(s32 a, s32 b) { + a -= b; + /* a >= 0 iff a >= b. */ + return ~(a >> 31); +} + +/* Take a fully reduced polynomial form number and contract it into a + * little-endian, 32-byte array. + * + * On entry: |input_limbs[i]| < 2^26 */ +static void +fcontract(u8 *output, limb *input_limbs) { + int i; + int j; + s32 input[10]; + s32 mask; + + /* |input_limbs[i]| < 2^26, so it's valid to convert to an s32. */ + for (i = 0; i < 10; i++) { + input[i] = input_limbs[i]; + } + + for (j = 0; j < 2; ++j) { + for (i = 0; i < 9; ++i) { + if ((i & 1) == 1) { + /* This calculation is a time-invariant way to make input[i] + * non-negative by borrowing from the next-larger limb. */ + const s32 mask = input[i] >> 31; + const s32 carry = -((input[i] & mask) >> 25); + input[i] = input[i] + (carry << 25); + input[i+1] = input[i+1] - carry; + } else { + const s32 mask = input[i] >> 31; + const s32 carry = -((input[i] & mask) >> 26); + input[i] = input[i] + (carry << 26); + input[i+1] = input[i+1] - carry; + } + } + + /* There's no greater limb for input[9] to borrow from, but we can multiply + * by 19 and borrow from input[0], which is valid mod 2^255-19. */ + { + const s32 mask = input[9] >> 31; + const s32 carry = -((input[9] & mask) >> 25); + input[9] = input[9] + (carry << 25); + input[0] = input[0] - (carry * 19); + } + + /* After the first iteration, input[1..9] are non-negative and fit within + * 25 or 26 bits, depending on position. However, input[0] may be + * negative. */ + } + + /* The first borrow-propagation pass above ended with every limb + except (possibly) input[0] non-negative. + + If input[0] was negative after the first pass, then it was because of a + carry from input[9]. On entry, input[9] < 2^26 so the carry was, at most, + one, since (2**26-1) >> 25 = 1. Thus input[0] >= -19. + + In the second pass, each limb is decreased by at most one. Thus the second + borrow-propagation pass could only have wrapped around to decrease + input[0] again if the first pass left input[0] negative *and* input[1] + through input[9] were all zero. In that case, input[1] is now 2^25 - 1, + and this last borrow-propagation step will leave input[1] non-negative. */ + { + const s32 mask = input[0] >> 31; + const s32 carry = -((input[0] & mask) >> 26); + input[0] = input[0] + (carry << 26); + input[1] = input[1] - carry; + } + + /* All input[i] are now non-negative. However, there might be values between + * 2^25 and 2^26 in a limb which is, nominally, 25 bits wide. */ + for (j = 0; j < 2; j++) { + for (i = 0; i < 9; i++) { + if ((i & 1) == 1) { + const s32 carry = input[i] >> 25; + input[i] &= 0x1ffffff; + input[i+1] += carry; + } else { + const s32 carry = input[i] >> 26; + input[i] &= 0x3ffffff; + input[i+1] += carry; + } + } + + { + const s32 carry = input[9] >> 25; + input[9] &= 0x1ffffff; + input[0] += 19*carry; + } + } + + /* If the first carry-chain pass, just above, ended up with a carry from + * input[9], and that caused input[0] to be out-of-bounds, then input[0] was + * < 2^26 + 2*19, because the carry was, at most, two. + * + * If the second pass carried from input[9] again then input[0] is < 2*19 and + * the input[9] -> input[0] carry didn't push input[0] out of bounds. */ + + /* It still remains the case that input might be between 2^255-19 and 2^255. + * In this case, input[1..9] must take their maximum value and input[0] must + * be >= (2^255-19) & 0x3ffffff, which is 0x3ffffed. */ + mask = s32_gte(input[0], 0x3ffffed); + for (i = 1; i < 10; i++) { + if ((i & 1) == 1) { + mask &= s32_eq(input[i], 0x1ffffff); + } else { + mask &= s32_eq(input[i], 0x3ffffff); + } + } + + /* mask is either 0xffffffff (if input >= 2^255-19) and zero otherwise. Thus + * this conditionally subtracts 2^255-19. */ + input[0] -= mask & 0x3ffffed; + + for (i = 1; i < 10; i++) { + if ((i & 1) == 1) { + input[i] -= mask & 0x1ffffff; + } else { + input[i] -= mask & 0x3ffffff; + } + } + + input[1] <<= 2; + input[2] <<= 3; + input[3] <<= 5; + input[4] <<= 6; + input[6] <<= 1; + input[7] <<= 3; + input[8] <<= 4; + input[9] <<= 6; +#define F(i, s) \ + output[s+0] |= input[i] & 0xff; \ + output[s+1] = (input[i] >> 8) & 0xff; \ + output[s+2] = (input[i] >> 16) & 0xff; \ + output[s+3] = (input[i] >> 24) & 0xff; + output[0] = 0; + output[16] = 0; + F(0,0); + F(1,3); + F(2,6); + F(3,9); + F(4,12); + F(5,16); + F(6,19); + F(7,22); + F(8,25); + F(9,28); +#undef F +} + +/* Input: Q, Q', Q-Q' + * Output: 2Q, Q+Q' + * + * x2 z3: long form + * x3 z3: long form + * x z: short form, destroyed + * xprime zprime: short form, destroyed + * qmqp: short form, preserved + * + * On entry and exit, the absolute value of the limbs of all inputs and outputs + * are < 2^26. */ +static void fmonty(limb *x2, limb *z2, /* output 2Q */ + limb *x3, limb *z3, /* output Q + Q' */ + limb *x, limb *z, /* input Q */ + limb *xprime, limb *zprime, /* input Q' */ + const limb *qmqp /* input Q - Q' */) { + limb origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19], + zzprime[19], zzzprime[19], xxxprime[19]; + + memcpy(origx, x, 10 * sizeof(limb)); + fsum(x, z); + /* |x[i]| < 2^27 */ + fdifference(z, origx); /* does x - z */ + /* |z[i]| < 2^27 */ + + memcpy(origxprime, xprime, sizeof(limb) * 10); + fsum(xprime, zprime); + /* |xprime[i]| < 2^27 */ + fdifference(zprime, origxprime); + /* |zprime[i]| < 2^27 */ + fproduct(xxprime, xprime, z); + /* |xxprime[i]| < 14*2^54: the largest product of two limbs will be < + * 2^(27+27) and fproduct adds together, at most, 14 of those products. + * (Approximating that to 2^58 doesn't work out.) */ + fproduct(zzprime, x, zprime); + /* |zzprime[i]| < 14*2^54 */ + freduce_degree(xxprime); + freduce_coefficients(xxprime); + /* |xxprime[i]| < 2^26 */ + freduce_degree(zzprime); + freduce_coefficients(zzprime); + /* |zzprime[i]| < 2^26 */ + memcpy(origxprime, xxprime, sizeof(limb) * 10); + fsum(xxprime, zzprime); + /* |xxprime[i]| < 2^27 */ + fdifference(zzprime, origxprime); + /* |zzprime[i]| < 2^27 */ + fsquare(xxxprime, xxprime); + /* |xxxprime[i]| < 2^26 */ + fsquare(zzzprime, zzprime); + /* |zzzprime[i]| < 2^26 */ + fproduct(zzprime, zzzprime, qmqp); + /* |zzprime[i]| < 14*2^52 */ + freduce_degree(zzprime); + freduce_coefficients(zzprime); + /* |zzprime[i]| < 2^26 */ + memcpy(x3, xxxprime, sizeof(limb) * 10); + memcpy(z3, zzprime, sizeof(limb) * 10); + + fsquare(xx, x); + /* |xx[i]| < 2^26 */ + fsquare(zz, z); + /* |zz[i]| < 2^26 */ + fproduct(x2, xx, zz); + /* |x2[i]| < 14*2^52 */ + freduce_degree(x2); + freduce_coefficients(x2); + /* |x2[i]| < 2^26 */ + fdifference(zz, xx); // does zz = xx - zz + /* |zz[i]| < 2^27 */ + memset(zzz + 10, 0, sizeof(limb) * 9); + fscalar_product(zzz, zz, 121665); + /* |zzz[i]| < 2^(27+17) */ + /* No need to call freduce_degree here: + fscalar_product doesn't increase the degree of its input. */ + freduce_coefficients(zzz); + /* |zzz[i]| < 2^26 */ + fsum(zzz, xx); + /* |zzz[i]| < 2^27 */ + fproduct(z2, zz, zzz); + /* |z2[i]| < 14*2^(26+27) */ + freduce_degree(z2); + freduce_coefficients(z2); + /* |z2|i| < 2^26 */ +} + +/* Conditionally swap two reduced-form limb arrays if 'iswap' is 1, but leave + * them unchanged if 'iswap' is 0. Runs in data-invariant time to avoid + * side-channel attacks. + * + * NOTE that this function requires that 'iswap' be 1 or 0; other values give + * wrong results. Also, the two limb arrays must be in reduced-coefficient, + * reduced-degree form: the values in a[10..19] or b[10..19] aren't swapped, + * and all all values in a[0..9],b[0..9] must have magnitude less than + * INT32_MAX. */ +static void +swap_conditional(limb a[19], limb b[19], limb iswap) { + unsigned i; + const s32 swap = (s32) -iswap; + + for (i = 0; i < 10; ++i) { + const s32 x = swap & ( ((s32)a[i]) ^ ((s32)b[i]) ); + a[i] = ((s32)a[i]) ^ x; + b[i] = ((s32)b[i]) ^ x; + } +} + +/* Calculates nQ where Q is the x-coordinate of a point on the curve + * + * resultx/resultz: the x coordinate of the resulting curve point (short form) + * n: a little endian, 32-byte number + * q: a point of the curve (short form) */ +static void +cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) { + limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0}; + limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t; + limb e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1}; + limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h; + + unsigned i, j; + + memcpy(nqpqx, q, sizeof(limb) * 10); + + for (i = 0; i < 32; ++i) { + u8 byte = n[31 - i]; + for (j = 0; j < 8; ++j) { + const limb bit = byte >> 7; + + swap_conditional(nqx, nqpqx, bit); + swap_conditional(nqz, nqpqz, bit); + fmonty(nqx2, nqz2, + nqpqx2, nqpqz2, + nqx, nqz, + nqpqx, nqpqz, + q); + swap_conditional(nqx2, nqpqx2, bit); + swap_conditional(nqz2, nqpqz2, bit); + + t = nqx; + nqx = nqx2; + nqx2 = t; + t = nqz; + nqz = nqz2; + nqz2 = t; + t = nqpqx; + nqpqx = nqpqx2; + nqpqx2 = t; + t = nqpqz; + nqpqz = nqpqz2; + nqpqz2 = t; + + byte <<= 1; + } + } + + memcpy(resultx, nqx, sizeof(limb) * 10); + memcpy(resultz, nqz, sizeof(limb) * 10); +} + +// ----------------------------------------------------------------------------- +// Shamelessly copied from djb's code +// ----------------------------------------------------------------------------- +static void +crecip(limb *out, const limb *z) { + limb z2[10]; + limb z9[10]; + limb z11[10]; + limb z2_5_0[10]; + limb z2_10_0[10]; + limb z2_20_0[10]; + limb z2_50_0[10]; + limb z2_100_0[10]; + limb t0[10]; + limb t1[10]; + int i; + + /* 2 */ fsquare(z2,z); + /* 4 */ fsquare(t1,z2); + /* 8 */ fsquare(t0,t1); + /* 9 */ fmul(z9,t0,z); + /* 11 */ fmul(z11,z9,z2); + /* 22 */ fsquare(t0,z11); + /* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9); + + /* 2^6 - 2^1 */ fsquare(t0,z2_5_0); + /* 2^7 - 2^2 */ fsquare(t1,t0); + /* 2^8 - 2^3 */ fsquare(t0,t1); + /* 2^9 - 2^4 */ fsquare(t1,t0); + /* 2^10 - 2^5 */ fsquare(t0,t1); + /* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0); + + /* 2^11 - 2^1 */ fsquare(t0,z2_10_0); + /* 2^12 - 2^2 */ fsquare(t1,t0); + /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0); + + /* 2^21 - 2^1 */ fsquare(t0,z2_20_0); + /* 2^22 - 2^2 */ fsquare(t1,t0); + /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0); + + /* 2^41 - 2^1 */ fsquare(t1,t0); + /* 2^42 - 2^2 */ fsquare(t0,t1); + /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); } + /* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0); + + /* 2^51 - 2^1 */ fsquare(t0,z2_50_0); + /* 2^52 - 2^2 */ fsquare(t1,t0); + /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0); + + /* 2^101 - 2^1 */ fsquare(t1,z2_100_0); + /* 2^102 - 2^2 */ fsquare(t0,t1); + /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); } + /* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0); + + /* 2^201 - 2^1 */ fsquare(t0,t1); + /* 2^202 - 2^2 */ fsquare(t1,t0); + /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0); + + /* 2^251 - 2^1 */ fsquare(t1,t0); + /* 2^252 - 2^2 */ fsquare(t0,t1); + /* 2^253 - 2^3 */ fsquare(t1,t0); + /* 2^254 - 2^4 */ fsquare(t0,t1); + /* 2^255 - 2^5 */ fsquare(t1,t0); + /* 2^255 - 21 */ fmul(out,t1,z11); +} + +int +curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) { + limb bp[10], x[10], z[11], zmone[10]; + uint8_t e[32]; + int i; + + for (i = 0; i < 32; ++i) e[i] = secret[i]; +// e[0] &= 248; +// e[31] &= 127; +// e[31] |= 64; + + fexpand(bp, basepoint); + cmult(x, z, e, bp); + crecip(zmone, z); + fmul(z, x, zmone); + fcontract(mypublic, z); + return 0; +} diff --git a/libs/libaxolotl/src/curve25519/curve25519-donna.h b/libs/libaxolotl/src/curve25519/curve25519-donna.h new file mode 100644 index 0000000000..3e4dfcee04 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/curve25519-donna.h @@ -0,0 +1,6 @@ +#ifndef CURVE25519_DONNA_H +#define CURVE25519_DONNA_H + +extern int curve25519_donna(uint8_t *, const uint8_t *, const uint8_t *); + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/additions/compare.c b/libs/libaxolotl/src/curve25519/ed25519/additions/compare.c new file mode 100644 index 0000000000..8b1e31389f --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/additions/compare.c @@ -0,0 +1,44 @@ +#include <string.h> +#include "compare.h" + +/* Const-time comparison from SUPERCOP, but here it's only used for + signature verification, so doesn't need to be const-time. But + copied the nacl version anyways. */ +int crypto_verify_32_ref(const unsigned char *x, const unsigned char *y) +{ + unsigned int differentbits = 0; +#define F(i) differentbits |= x[i] ^ y[i]; + F(0) + F(1) + F(2) + F(3) + F(4) + F(5) + F(6) + F(7) + F(8) + F(9) + F(10) + F(11) + F(12) + F(13) + F(14) + F(15) + F(16) + F(17) + F(18) + F(19) + F(20) + F(21) + F(22) + F(23) + F(24) + F(25) + F(26) + F(27) + F(28) + F(29) + F(30) + F(31) + return (1 & ((differentbits - 1) >> 8)) - 1; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/additions/compare.h b/libs/libaxolotl/src/curve25519/ed25519/additions/compare.h new file mode 100644 index 0000000000..5a2fa910d4 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/additions/compare.h @@ -0,0 +1,6 @@ +#ifndef __COMPARE_H__ +#define __COMPARE_H__ + +int crypto_verify_32_ref(const unsigned char *b1, const unsigned char *b2); + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/additions/crypto_hash_sha512.h b/libs/libaxolotl/src/curve25519/ed25519/additions/crypto_hash_sha512.h new file mode 100644 index 0000000000..a51a190d25 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/additions/crypto_hash_sha512.h @@ -0,0 +1,6 @@ +#ifndef crypto_hash_sha512_H +#define crypto_hash_sha512_H + +extern int crypto_hash_sha512(unsigned char *,const unsigned char *,unsigned long long); + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/additions/curve_sigs.c b/libs/libaxolotl/src/curve25519/ed25519/additions/curve_sigs.c new file mode 100644 index 0000000000..2a0aa54d4f --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/additions/curve_sigs.c @@ -0,0 +1,138 @@ +#include <string.h> +#include <stdlib.h> +#include "ge.h" +#include "curve_sigs.h" +#include "crypto_sign.h" + +void curve25519_keygen(unsigned char* curve25519_pubkey_out, + const unsigned char* curve25519_privkey_in) +{ + ge_p3 ed; /* Ed25519 pubkey point */ + fe ed_y, ed_y_plus_one, one_minus_ed_y, inv_one_minus_ed_y; + fe mont_x; + + /* Perform a fixed-base multiplication of the Edwards base point, + (which is efficient due to precalculated tables), then convert + to the Curve25519 montgomery-format public key. In particular, + convert Curve25519's "montgomery" x-coordinate into an Ed25519 + "edwards" y-coordinate: + + mont_x = (ed_y + 1) / (1 - ed_y) + + with projective coordinates: + + mont_x = (ed_y + ed_z) / (ed_z - ed_y) + + NOTE: ed_y=1 is converted to mont_x=0 since fe_invert is mod-exp + */ + + ge_scalarmult_base(&ed, curve25519_privkey_in); + fe_add(ed_y_plus_one, ed.Y, ed.Z); + fe_sub(one_minus_ed_y, ed.Z, ed.Y); + fe_invert(inv_one_minus_ed_y, one_minus_ed_y); + fe_mul(mont_x, ed_y_plus_one, inv_one_minus_ed_y); + fe_tobytes(curve25519_pubkey_out, mont_x); +} + +int curve25519_sign(unsigned char* signature_out, + const unsigned char* curve25519_privkey, + const unsigned char* msg, const unsigned long msg_len, + const unsigned char* random) +{ + ge_p3 ed_pubkey_point; /* Ed25519 pubkey point */ + unsigned char ed_pubkey[32]; /* Ed25519 encoded pubkey */ + unsigned char *sigbuf; /* working buffer */ + unsigned char sign_bit = 0; + + if ((sigbuf = malloc(msg_len + 128)) == 0) { + memset(signature_out, 0, 64); + return -1; + } + + /* Convert the Curve25519 privkey to an Ed25519 public key */ + ge_scalarmult_base(&ed_pubkey_point, curve25519_privkey); + ge_p3_tobytes(ed_pubkey, &ed_pubkey_point); + sign_bit = ed_pubkey[31] & 0x80; + + /* Perform an Ed25519 signature with explicit private key */ + crypto_sign_modified(sigbuf, msg, msg_len, curve25519_privkey, + ed_pubkey, random); + memmove(signature_out, sigbuf, 64); + + /* Encode the sign bit into signature (in unused high bit of S) */ + signature_out[63] &= 0x7F; /* bit should be zero already, but just in case */ + signature_out[63] |= sign_bit; + + free(sigbuf); + return 0; +} + +int curve25519_verify(const unsigned char* signature, + const unsigned char* curve25519_pubkey, + const unsigned char* msg, const unsigned long msg_len) +{ + fe mont_x, mont_x_minus_one, mont_x_plus_one, inv_mont_x_plus_one; + fe one; + fe ed_y; + unsigned char ed_pubkey[32]; + unsigned long long some_retval; + unsigned char *verifybuf = NULL; /* working buffer */ + unsigned char *verifybuf2 = NULL; /* working buffer #2 */ + int result; + + if ((verifybuf = malloc(msg_len + 64)) == 0) { + result = -1; + goto err; + } + + if ((verifybuf2 = malloc(msg_len + 64)) == 0) { + result = -1; + goto err; + } + + /* Convert the Curve25519 public key into an Ed25519 public key. In + particular, convert Curve25519's "montgomery" x-coordinate into an + Ed25519 "edwards" y-coordinate: + + ed_y = (mont_x - 1) / (mont_x + 1) + + NOTE: mont_x=-1 is converted to ed_y=0 since fe_invert is mod-exp + + Then move the sign bit into the pubkey from the signature. + */ + fe_frombytes(mont_x, curve25519_pubkey); + fe_1(one); + fe_sub(mont_x_minus_one, mont_x, one); + fe_add(mont_x_plus_one, mont_x, one); + fe_invert(inv_mont_x_plus_one, mont_x_plus_one); + fe_mul(ed_y, mont_x_minus_one, inv_mont_x_plus_one); + fe_tobytes(ed_pubkey, ed_y); + + /* Copy the sign bit, and remove it from signature */ + ed_pubkey[31] &= 0x7F; /* bit should be zero already, but just in case */ + ed_pubkey[31] |= (signature[63] & 0x80); + memmove(verifybuf, signature, 64); + verifybuf[63] &= 0x7F; + + memmove(verifybuf+64, msg, msg_len); + + /* Then perform a normal Ed25519 verification, return 0 on success */ + /* The below call has a strange API: */ + /* verifybuf = R || S || message */ + /* verifybuf2 = internal to next call gets a copy of verifybuf, S gets + replaced with pubkey for hashing, then the whole thing gets zeroized + (if bad sig), or contains a copy of msg (good sig) */ + result = crypto_sign_open(verifybuf2, &some_retval, verifybuf, 64 + msg_len, ed_pubkey); + + err: + + if (verifybuf != NULL) { + free(verifybuf); + } + + if (verifybuf2 != NULL) { + free(verifybuf2); + } + + return result; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/additions/curve_sigs.h b/libs/libaxolotl/src/curve25519/ed25519/additions/curve_sigs.h new file mode 100644 index 0000000000..b3dbad0155 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/additions/curve_sigs.h @@ -0,0 +1,48 @@ + +#ifndef __CURVE_SIGS_H__ +#define __CURVE_SIGS_H__ + +void curve25519_keygen(unsigned char* curve25519_pubkey_out, /* 32 bytes */ + const unsigned char* curve25519_privkey_in); /* 32 bytes */ + +/* returns 0 on success */ +int curve25519_sign(unsigned char* signature_out, /* 64 bytes */ + const unsigned char* curve25519_privkey, /* 32 bytes */ + const unsigned char* msg, const unsigned long msg_len, + const unsigned char* random); /* 64 bytes */ + +/* returns 0 on success */ +int curve25519_verify(const unsigned char* signature, /* 64 bytes */ + const unsigned char* curve25519_pubkey, /* 32 bytes */ + const unsigned char* msg, const unsigned long msg_len); + +/* helper function - modified version of crypto_sign() to use + explicit private key. In particular: + + sk : private key + pk : public key + msg : message + prefix : 0xFE || [0xFF]*31 + random : 64 bytes random + q : main subgroup order + + The prefix is chosen to distinguish the two SHA512 uses below, since + prefix is an invalid encoding for R (it would encode a "field element" + of 2^255 - 2). 0xFF*32 is set aside for use in ECDH protocols, which + is why the first byte here ix 0xFE. + + sig_nonce = SHA512(prefix || sk || msg || random) % q + R = g^sig_nonce + M = SHA512(R || pk || m) + S = sig_nonce + (m * sk) + signature = (R || S) + */ +int crypto_sign_modified( + unsigned char *sm, + const unsigned char *m,unsigned long long mlen, + const unsigned char *sk, /* Curve/Ed25519 private key */ + const unsigned char *pk, /* Ed25519 public key */ + const unsigned char *random /* 64 bytes random to hash into nonce */ + ); + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/additions/sign_modified.c b/libs/libaxolotl/src/curve25519/ed25519/additions/sign_modified.c new file mode 100644 index 0000000000..61332e70e7 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/additions/sign_modified.c @@ -0,0 +1,47 @@ +#include <string.h> +#include "crypto_sign.h" +#include "crypto_hash_sha512.h" +#include "ge.h" +#include "sc.h" +#include "zeroize.h" + +/* NEW: Compare to pristine crypto_sign() + Uses explicit private key for nonce derivation and as scalar, + instead of deriving both from a master key. +*/ +int crypto_sign_modified( + unsigned char *sm, + const unsigned char *m,unsigned long long mlen, + const unsigned char *sk, const unsigned char* pk, + const unsigned char* random +) +{ + unsigned char nonce[64]; + unsigned char hram[64]; + ge_p3 R; + int count=0; + + memmove(sm + 64,m,mlen); + memmove(sm + 32,sk,32); /* NEW: Use privkey directly for nonce derivation */ + + /* NEW : add prefix to separate hash uses - see .h */ + sm[0] = 0xFE; + for (count = 1; count < 32; count++) + sm[count] = 0xFF; + + /* NEW: add suffix of random data */ + memmove(sm + mlen + 64, random, 64); + + crypto_hash_sha512(nonce,sm,mlen + 128); + memmove(sm + 32,pk,32); + + sc_reduce(nonce); + ge_scalarmult_base(&R,nonce); + ge_p3_tobytes(sm,&R); + + crypto_hash_sha512(hram,sm,mlen + 64); + sc_reduce(hram); + sc_muladd(sm + 32,hram,sk,nonce); /* NEW: Use privkey directly */ + + return 0; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/additions/zeroize.c b/libs/libaxolotl/src/curve25519/ed25519/additions/zeroize.c new file mode 100644 index 0000000000..37c1f708bc --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/additions/zeroize.c @@ -0,0 +1,17 @@ +#include "zeroize.h" + +void zeroize(unsigned char* b, size_t len) +{ + size_t count = 0; + unsigned long retval = 0; + volatile unsigned char *p = b; + + for (count = 0; count < len; count++) + p[count] = 0; +} + +void zeroize_stack() +{ + unsigned char m[ZEROIZE_STACK_SIZE]; + zeroize(m, sizeof m); +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/additions/zeroize.h b/libs/libaxolotl/src/curve25519/ed25519/additions/zeroize.h new file mode 100644 index 0000000000..80fcffb768 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/additions/zeroize.h @@ -0,0 +1,12 @@ +#ifndef __ZEROIZE_H__ +#define __ZEROIZE_H__ + +#include <stdlib.h> + +#define ZEROIZE_STACK_SIZE 2048 + +void zeroize(unsigned char* b, size_t len); + +void zeroize_stack(); + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/base.h b/libs/libaxolotl/src/curve25519/ed25519/base.h new file mode 100644 index 0000000000..573bd8a05c --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/base.h @@ -0,0 +1,1344 @@ +{ + { + { 25967493,-14356035,29566456,3660896,-12694345,4014787,27544626,-11754271,-6079156,2047605 }, + { -12545711,934262,-2722910,3049990,-727428,9406986,12720692,5043384,19500929,-15469378 }, + { -8738181,4489570,9688441,-14785194,10184609,-12363380,29287919,11864899,-24514362,-4438546 }, + }, + { + { -12815894,-12976347,-21581243,11784320,-25355658,-2750717,-11717903,-3814571,-358445,-10211303 }, + { -21703237,6903825,27185491,6451973,-29577724,-9554005,-15616551,11189268,-26829678,-5319081 }, + { 26966642,11152617,32442495,15396054,14353839,-12752335,-3128826,-9541118,-15472047,-4166697 }, + }, + { + { 15636291,-9688557,24204773,-7912398,616977,-16685262,27787600,-14772189,28944400,-1550024 }, + { 16568933,4717097,-11556148,-1102322,15682896,-11807043,16354577,-11775962,7689662,11199574 }, + { 30464156,-5976125,-11779434,-15670865,23220365,15915852,7512774,10017326,-17749093,-9920357 }, + }, + { + { -17036878,13921892,10945806,-6033431,27105052,-16084379,-28926210,15006023,3284568,-6276540 }, + { 23599295,-8306047,-11193664,-7687416,13236774,10506355,7464579,9656445,13059162,10374397 }, + { 7798556,16710257,3033922,2874086,28997861,2835604,32406664,-3839045,-641708,-101325 }, + }, + { + { 10861363,11473154,27284546,1981175,-30064349,12577861,32867885,14515107,-15438304,10819380 }, + { 4708026,6336745,20377586,9066809,-11272109,6594696,-25653668,12483688,-12668491,5581306 }, + { 19563160,16186464,-29386857,4097519,10237984,-4348115,28542350,13850243,-23678021,-15815942 }, + }, + { + { -15371964,-12862754,32573250,4720197,-26436522,5875511,-19188627,-15224819,-9818940,-12085777 }, + { -8549212,109983,15149363,2178705,22900618,4543417,3044240,-15689887,1762328,14866737 }, + { -18199695,-15951423,-10473290,1707278,-17185920,3916101,-28236412,3959421,27914454,4383652 }, + }, + { + { 5153746,9909285,1723747,-2777874,30523605,5516873,19480852,5230134,-23952439,-15175766 }, + { -30269007,-3463509,7665486,10083793,28475525,1649722,20654025,16520125,30598449,7715701 }, + { 28881845,14381568,9657904,3680757,-20181635,7843316,-31400660,1370708,29794553,-1409300 }, + }, + { + { 14499471,-2729599,-33191113,-4254652,28494862,14271267,30290735,10876454,-33154098,2381726 }, + { -7195431,-2655363,-14730155,462251,-27724326,3941372,-6236617,3696005,-32300832,15351955 }, + { 27431194,8222322,16448760,-3907995,-18707002,11938355,-32961401,-2970515,29551813,10109425 }, + }, +}, +{ + { + { -13657040,-13155431,-31283750,11777098,21447386,6519384,-2378284,-1627556,10092783,-4764171 }, + { 27939166,14210322,4677035,16277044,-22964462,-12398139,-32508754,12005538,-17810127,12803510 }, + { 17228999,-15661624,-1233527,300140,-1224870,-11714777,30364213,-9038194,18016357,4397660 }, + }, + { + { -10958843,-7690207,4776341,-14954238,27850028,-15602212,-26619106,14544525,-17477504,982639 }, + { 29253598,15796703,-2863982,-9908884,10057023,3163536,7332899,-4120128,-21047696,9934963 }, + { 5793303,16271923,-24131614,-10116404,29188560,1206517,-14747930,4559895,-30123922,-10897950 }, + }, + { + { -27643952,-11493006,16282657,-11036493,28414021,-15012264,24191034,4541697,-13338309,5500568 }, + { 12650548,-1497113,9052871,11355358,-17680037,-8400164,-17430592,12264343,10874051,13524335 }, + { 25556948,-3045990,714651,2510400,23394682,-10415330,33119038,5080568,-22528059,5376628 }, + }, + { + { -26088264,-4011052,-17013699,-3537628,-6726793,1920897,-22321305,-9447443,4535768,1569007 }, + { -2255422,14606630,-21692440,-8039818,28430649,8775819,-30494562,3044290,31848280,12543772 }, + { -22028579,2943893,-31857513,6777306,13784462,-4292203,-27377195,-2062731,7718482,14474653 }, + }, + { + { 2385315,2454213,-22631320,46603,-4437935,-15680415,656965,-7236665,24316168,-5253567 }, + { 13741529,10911568,-33233417,-8603737,-20177830,-1033297,33040651,-13424532,-20729456,8321686 }, + { 21060490,-2212744,15712757,-4336099,1639040,10656336,23845965,-11874838,-9984458,608372 }, + }, + { + { -13672732,-15087586,-10889693,-7557059,-6036909,11305547,1123968,-6780577,27229399,23887 }, + { -23244140,-294205,-11744728,14712571,-29465699,-2029617,12797024,-6440308,-1633405,16678954 }, + { -29500620,4770662,-16054387,14001338,7830047,9564805,-1508144,-4795045,-17169265,4904953 }, + }, + { + { 24059557,14617003,19037157,-15039908,19766093,-14906429,5169211,16191880,2128236,-4326833 }, + { -16981152,4124966,-8540610,-10653797,30336522,-14105247,-29806336,916033,-6882542,-2986532 }, + { -22630907,12419372,-7134229,-7473371,-16478904,16739175,285431,2763829,15736322,4143876 }, + }, + { + { 2379352,11839345,-4110402,-5988665,11274298,794957,212801,-14594663,23527084,-16458268 }, + { 33431127,-11130478,-17838966,-15626900,8909499,8376530,-32625340,4087881,-15188911,-14416214 }, + { 1767683,7197987,-13205226,-2022635,-13091350,448826,5799055,4357868,-4774191,-16323038 }, + }, +}, +{ + { + { 6721966,13833823,-23523388,-1551314,26354293,-11863321,23365147,-3949732,7390890,2759800 }, + { 4409041,2052381,23373853,10530217,7676779,-12885954,21302353,-4264057,1244380,-12919645 }, + { -4421239,7169619,4982368,-2957590,30256825,-2777540,14086413,9208236,15886429,16489664 }, + }, + { + { 1996075,10375649,14346367,13311202,-6874135,-16438411,-13693198,398369,-30606455,-712933 }, + { -25307465,9795880,-2777414,14878809,-33531835,14780363,13348553,12076947,-30836462,5113182 }, + { -17770784,11797796,31950843,13929123,-25888302,12288344,-30341101,-7336386,13847711,5387222 }, + }, + { + { -18582163,-3416217,17824843,-2340966,22744343,-10442611,8763061,3617786,-19600662,10370991 }, + { 20246567,-14369378,22358229,-543712,18507283,-10413996,14554437,-8746092,32232924,16763880 }, + { 9648505,10094563,26416693,14745928,-30374318,-6472621,11094161,15689506,3140038,-16510092 }, + }, + { + { -16160072,5472695,31895588,4744994,8823515,10365685,-27224800,9448613,-28774454,366295 }, + { 19153450,11523972,-11096490,-6503142,-24647631,5420647,28344573,8041113,719605,11671788 }, + { 8678025,2694440,-6808014,2517372,4964326,11152271,-15432916,-15266516,27000813,-10195553 }, + }, + { + { -15157904,7134312,8639287,-2814877,-7235688,10421742,564065,5336097,6750977,-14521026 }, + { 11836410,-3979488,26297894,16080799,23455045,15735944,1695823,-8819122,8169720,16220347 }, + { -18115838,8653647,17578566,-6092619,-8025777,-16012763,-11144307,-2627664,-5990708,-14166033 }, + }, + { + { -23308498,-10968312,15213228,-10081214,-30853605,-11050004,27884329,2847284,2655861,1738395 }, + { -27537433,-14253021,-25336301,-8002780,-9370762,8129821,21651608,-3239336,-19087449,-11005278 }, + { 1533110,3437855,23735889,459276,29970501,11335377,26030092,5821408,10478196,8544890 }, + }, + { + { 32173121,-16129311,24896207,3921497,22579056,-3410854,19270449,12217473,17789017,-3395995 }, + { -30552961,-2228401,-15578829,-10147201,13243889,517024,15479401,-3853233,30460520,1052596 }, + { -11614875,13323618,32618793,8175907,-15230173,12596687,27491595,-4612359,3179268,-9478891 }, + }, + { + { 31947069,-14366651,-4640583,-15339921,-15125977,-6039709,-14756777,-16411740,19072640,-9511060 }, + { 11685058,11822410,3158003,-13952594,33402194,-4165066,5977896,-5215017,473099,5040608 }, + { -20290863,8198642,-27410132,11602123,1290375,-2799760,28326862,1721092,-19558642,-3131606 }, + }, +}, +{ + { + { 7881532,10687937,7578723,7738378,-18951012,-2553952,21820786,8076149,-27868496,11538389 }, + { -19935666,3899861,18283497,-6801568,-15728660,-11249211,8754525,7446702,-5676054,5797016 }, + { -11295600,-3793569,-15782110,-7964573,12708869,-8456199,2014099,-9050574,-2369172,-5877341 }, + }, + { + { -22472376,-11568741,-27682020,1146375,18956691,16640559,1192730,-3714199,15123619,10811505 }, + { 14352098,-3419715,-18942044,10822655,32750596,4699007,-70363,15776356,-28886779,-11974553 }, + { -28241164,-8072475,-4978962,-5315317,29416931,1847569,-20654173,-16484855,4714547,-9600655 }, + }, + { + { 15200332,8368572,19679101,15970074,-31872674,1959451,24611599,-4543832,-11745876,12340220 }, + { 12876937,-10480056,33134381,6590940,-6307776,14872440,9613953,8241152,15370987,9608631 }, + { -4143277,-12014408,8446281,-391603,4407738,13629032,-7724868,15866074,-28210621,-8814099 }, + }, + { + { 26660628,-15677655,8393734,358047,-7401291,992988,-23904233,858697,20571223,8420556 }, + { 14620715,13067227,-15447274,8264467,14106269,15080814,33531827,12516406,-21574435,-12476749 }, + { 236881,10476226,57258,-14677024,6472998,2466984,17258519,7256740,8791136,15069930 }, + }, + { + { 1276410,-9371918,22949635,-16322807,-23493039,-5702186,14711875,4874229,-30663140,-2331391 }, + { 5855666,4990204,-13711848,7294284,-7804282,1924647,-1423175,-7912378,-33069337,9234253 }, + { 20590503,-9018988,31529744,-7352666,-2706834,10650548,31559055,-11609587,18979186,13396066 }, + }, + { + { 24474287,4968103,22267082,4407354,24063882,-8325180,-18816887,13594782,33514650,7021958 }, + { -11566906,-6565505,-21365085,15928892,-26158305,4315421,-25948728,-3916677,-21480480,12868082 }, + { -28635013,13504661,19988037,-2132761,21078225,6443208,-21446107,2244500,-12455797,-8089383 }, + }, + { + { -30595528,13793479,-5852820,319136,-25723172,-6263899,33086546,8957937,-15233648,5540521 }, + { -11630176,-11503902,-8119500,-7643073,2620056,1022908,-23710744,-1568984,-16128528,-14962807 }, + { 23152971,775386,27395463,14006635,-9701118,4649512,1689819,892185,-11513277,-15205948 }, + }, + { + { 9770129,9586738,26496094,4324120,1556511,-3550024,27453819,4763127,-19179614,5867134 }, + { -32765025,1927590,31726409,-4753295,23962434,-16019500,27846559,5931263,-29749703,-16108455 }, + { 27461885,-2977536,22380810,1815854,-23033753,-3031938,7283490,-15148073,-19526700,7734629 }, + }, +}, +{ + { + { -8010264,-9590817,-11120403,6196038,29344158,-13430885,7585295,-3176626,18549497,15302069 }, + { -32658337,-6171222,-7672793,-11051681,6258878,13504381,10458790,-6418461,-8872242,8424746 }, + { 24687205,8613276,-30667046,-3233545,1863892,-1830544,19206234,7134917,-11284482,-828919 }, + }, + { + { 11334899,-9218022,8025293,12707519,17523892,-10476071,10243738,-14685461,-5066034,16498837 }, + { 8911542,6887158,-9584260,-6958590,11145641,-9543680,17303925,-14124238,6536641,10543906 }, + { -28946384,15479763,-17466835,568876,-1497683,11223454,-2669190,-16625574,-27235709,8876771 }, + }, + { + { -25742899,-12566864,-15649966,-846607,-33026686,-796288,-33481822,15824474,-604426,-9039817 }, + { 10330056,70051,7957388,-9002667,9764902,15609756,27698697,-4890037,1657394,3084098 }, + { 10477963,-7470260,12119566,-13250805,29016247,-5365589,31280319,14396151,-30233575,15272409 }, + }, + { + { -12288309,3169463,28813183,16658753,25116432,-5630466,-25173957,-12636138,-25014757,1950504 }, + { -26180358,9489187,11053416,-14746161,-31053720,5825630,-8384306,-8767532,15341279,8373727 }, + { 28685821,7759505,-14378516,-12002860,-31971820,4079242,298136,-10232602,-2878207,15190420 }, + }, + { + { -32932876,13806336,-14337485,-15794431,-24004620,10940928,8669718,2742393,-26033313,-6875003 }, + { -1580388,-11729417,-25979658,-11445023,-17411874,-10912854,9291594,-16247779,-12154742,6048605 }, + { -30305315,14843444,1539301,11864366,20201677,1900163,13934231,5128323,11213262,9168384 }, + }, + { + { -26280513,11007847,19408960,-940758,-18592965,-4328580,-5088060,-11105150,20470157,-16398701 }, + { -23136053,9282192,14855179,-15390078,-7362815,-14408560,-22783952,14461608,14042978,5230683 }, + { 29969567,-2741594,-16711867,-8552442,9175486,-2468974,21556951,3506042,-5933891,-12449708 }, + }, + { + { -3144746,8744661,19704003,4581278,-20430686,6830683,-21284170,8971513,-28539189,15326563 }, + { -19464629,10110288,-17262528,-3503892,-23500387,1355669,-15523050,15300988,-20514118,9168260 }, + { -5353335,4488613,-23803248,16314347,7780487,-15638939,-28948358,9601605,33087103,-9011387 }, + }, + { + { -19443170,-15512900,-20797467,-12445323,-29824447,10229461,-27444329,-15000531,-5996870,15664672 }, + { 23294591,-16632613,-22650781,-8470978,27844204,11461195,13099750,-2460356,18151676,13417686 }, + { -24722913,-4176517,-31150679,5988919,-26858785,6685065,1661597,-12551441,15271676,-15452665 }, + }, +}, +{ + { + { 11433042,-13228665,8239631,-5279517,-1985436,-725718,-18698764,2167544,-6921301,-13440182 }, + { -31436171,15575146,30436815,12192228,-22463353,9395379,-9917708,-8638997,12215110,12028277 }, + { 14098400,6555944,23007258,5757252,-15427832,-12950502,30123440,4617780,-16900089,-655628 }, + }, + { + { -4026201,-15240835,11893168,13718664,-14809462,1847385,-15819999,10154009,23973261,-12684474 }, + { -26531820,-3695990,-1908898,2534301,-31870557,-16550355,18341390,-11419951,32013174,-10103539 }, + { -25479301,10876443,-11771086,-14625140,-12369567,1838104,21911214,6354752,4425632,-837822 }, + }, + { + { -10433389,-14612966,22229858,-3091047,-13191166,776729,-17415375,-12020462,4725005,14044970 }, + { 19268650,-7304421,1555349,8692754,-21474059,-9910664,6347390,-1411784,-19522291,-16109756 }, + { -24864089,12986008,-10898878,-5558584,-11312371,-148526,19541418,8180106,9282262,10282508 }, + }, + { + { -26205082,4428547,-8661196,-13194263,4098402,-14165257,15522535,8372215,5542595,-10702683 }, + { -10562541,14895633,26814552,-16673850,-17480754,-2489360,-2781891,6993761,-18093885,10114655 }, + { -20107055,-929418,31422704,10427861,-7110749,6150669,-29091755,-11529146,25953725,-106158 }, + }, + { + { -4234397,-8039292,-9119125,3046000,2101609,-12607294,19390020,6094296,-3315279,12831125 }, + { -15998678,7578152,5310217,14408357,-33548620,-224739,31575954,6326196,7381791,-2421839 }, + { -20902779,3296811,24736065,-16328389,18374254,7318640,6295303,8082724,-15362489,12339664 }, + }, + { + { 27724736,2291157,6088201,-14184798,1792727,5857634,13848414,15768922,25091167,14856294 }, + { -18866652,8331043,24373479,8541013,-701998,-9269457,12927300,-12695493,-22182473,-9012899 }, + { -11423429,-5421590,11632845,3405020,30536730,-11674039,-27260765,13866390,30146206,9142070 }, + }, + { + { 3924129,-15307516,-13817122,-10054960,12291820,-668366,-27702774,9326384,-8237858,4171294 }, + { -15921940,16037937,6713787,16606682,-21612135,2790944,26396185,3731949,345228,-5462949 }, + { -21327538,13448259,25284571,1143661,20614966,-8849387,2031539,-12391231,-16253183,-13582083 }, + }, + { + { 31016211,-16722429,26371392,-14451233,-5027349,14854137,17477601,3842657,28012650,-16405420 }, + { -5075835,9368966,-8562079,-4600902,-15249953,6970560,-9189873,16292057,-8867157,3507940 }, + { 29439664,3537914,23333589,6997794,-17555561,-11018068,-15209202,-15051267,-9164929,6580396 }, + }, +}, +{ + { + { -12185861,-7679788,16438269,10826160,-8696817,-6235611,17860444,-9273846,-2095802,9304567 }, + { 20714564,-4336911,29088195,7406487,11426967,-5095705,14792667,-14608617,5289421,-477127 }, + { -16665533,-10650790,-6160345,-13305760,9192020,-1802462,17271490,12349094,26939669,-3752294 }, + }, + { + { -12889898,9373458,31595848,16374215,21471720,13221525,-27283495,-12348559,-3698806,117887 }, + { 22263325,-6560050,3984570,-11174646,-15114008,-566785,28311253,5358056,-23319780,541964 }, + { 16259219,3261970,2309254,-15534474,-16885711,-4581916,24134070,-16705829,-13337066,-13552195 }, + }, + { + { 9378160,-13140186,-22845982,-12745264,28198281,-7244098,-2399684,-717351,690426,14876244 }, + { 24977353,-314384,-8223969,-13465086,28432343,-1176353,-13068804,-12297348,-22380984,6618999 }, + { -1538174,11685646,12944378,13682314,-24389511,-14413193,8044829,-13817328,32239829,-5652762 }, + }, + { + { -18603066,4762990,-926250,8885304,-28412480,-3187315,9781647,-10350059,32779359,5095274 }, + { -33008130,-5214506,-32264887,-3685216,9460461,-9327423,-24601656,14506724,21639561,-2630236 }, + { -16400943,-13112215,25239338,15531969,3987758,-4499318,-1289502,-6863535,17874574,558605 }, + }, + { + { -13600129,10240081,9171883,16131053,-20869254,9599700,33499487,5080151,2085892,5119761 }, + { -22205145,-2519528,-16381601,414691,-25019550,2170430,30634760,-8363614,-31999993,-5759884 }, + { -6845704,15791202,8550074,-1312654,29928809,-12092256,27534430,-7192145,-22351378,12961482 }, + }, + { + { -24492060,-9570771,10368194,11582341,-23397293,-2245287,16533930,8206996,-30194652,-5159638 }, + { -11121496,-3382234,2307366,6362031,-135455,8868177,-16835630,7031275,7589640,8945490 }, + { -32152748,8917967,6661220,-11677616,-1192060,-15793393,7251489,-11182180,24099109,-14456170 }, + }, + { + { 5019558,-7907470,4244127,-14714356,-26933272,6453165,-19118182,-13289025,-6231896,-10280736 }, + { 10853594,10721687,26480089,5861829,-22995819,1972175,-1866647,-10557898,-3363451,-6441124 }, + { -17002408,5906790,221599,-6563147,7828208,-13248918,24362661,-2008168,-13866408,7421392 }, + }, + { + { 8139927,-6546497,32257646,-5890546,30375719,1886181,-21175108,15441252,28826358,-4123029 }, + { 6267086,9695052,7709135,-16603597,-32869068,-1886135,14795160,-7840124,13746021,-1742048 }, + { 28584902,7787108,-6732942,-15050729,22846041,-7571236,-3181936,-363524,4771362,-8419958 }, + }, +}, +{ + { + { 24949256,6376279,-27466481,-8174608,-18646154,-9930606,33543569,-12141695,3569627,11342593 }, + { 26514989,4740088,27912651,3697550,19331575,-11472339,6809886,4608608,7325975,-14801071 }, + { -11618399,-14554430,-24321212,7655128,-1369274,5214312,-27400540,10258390,-17646694,-8186692 }, + }, + { + { 11431204,15823007,26570245,14329124,18029990,4796082,-31446179,15580664,9280358,-3973687 }, + { -160783,-10326257,-22855316,-4304997,-20861367,-13621002,-32810901,-11181622,-15545091,4387441 }, + { -20799378,12194512,3937617,-5805892,-27154820,9340370,-24513992,8548137,20617071,-7482001 }, + }, + { + { -938825,-3930586,-8714311,16124718,24603125,-6225393,-13775352,-11875822,24345683,10325460 }, + { -19855277,-1568885,-22202708,8714034,14007766,6928528,16318175,-1010689,4766743,3552007 }, + { -21751364,-16730916,1351763,-803421,-4009670,3950935,3217514,14481909,10988822,-3994762 }, + }, + { + { 15564307,-14311570,3101243,5684148,30446780,-8051356,12677127,-6505343,-8295852,13296005 }, + { -9442290,6624296,-30298964,-11913677,-4670981,-2057379,31521204,9614054,-30000824,12074674 }, + { 4771191,-135239,14290749,-13089852,27992298,14998318,-1413936,-1556716,29832613,-16391035 }, + }, + { + { 7064884,-7541174,-19161962,-5067537,-18891269,-2912736,25825242,5293297,-27122660,13101590 }, + { -2298563,2439670,-7466610,1719965,-27267541,-16328445,32512469,-5317593,-30356070,-4190957 }, + { -30006540,10162316,-33180176,3981723,-16482138,-13070044,14413974,9515896,19568978,9628812 }, + }, + { + { 33053803,199357,15894591,1583059,27380243,-4580435,-17838894,-6106839,-6291786,3437740 }, + { -18978877,3884493,19469877,12726490,15913552,13614290,-22961733,70104,7463304,4176122 }, + { -27124001,10659917,11482427,-16070381,12771467,-6635117,-32719404,-5322751,24216882,5944158 }, + }, + { + { 8894125,7450974,-2664149,-9765752,-28080517,-12389115,19345746,14680796,11632993,5847885 }, + { 26942781,-2315317,9129564,-4906607,26024105,11769399,-11518837,6367194,-9727230,4782140 }, + { 19916461,-4828410,-22910704,-11414391,25606324,-5972441,33253853,8220911,6358847,-1873857 }, + }, + { + { 801428,-2081702,16569428,11065167,29875704,96627,7908388,-4480480,-13538503,1387155 }, + { 19646058,5720633,-11416706,12814209,11607948,12749789,14147075,15156355,-21866831,11835260 }, + { 19299512,1155910,28703737,14890794,2925026,7269399,26121523,15467869,-26560550,5052483 }, + }, +}, +{ + { + { -3017432,10058206,1980837,3964243,22160966,12322533,-6431123,-12618185,12228557,-7003677 }, + { 32944382,14922211,-22844894,5188528,21913450,-8719943,4001465,13238564,-6114803,8653815 }, + { 22865569,-4652735,27603668,-12545395,14348958,8234005,24808405,5719875,28483275,2841751 }, + }, + { + { -16420968,-1113305,-327719,-12107856,21886282,-15552774,-1887966,-315658,19932058,-12739203 }, + { -11656086,10087521,-8864888,-5536143,-19278573,-3055912,3999228,13239134,-4777469,-13910208 }, + { 1382174,-11694719,17266790,9194690,-13324356,9720081,20403944,11284705,-14013818,3093230 }, + }, + { + { 16650921,-11037932,-1064178,1570629,-8329746,7352753,-302424,16271225,-24049421,-6691850 }, + { -21911077,-5927941,-4611316,-5560156,-31744103,-10785293,24123614,15193618,-21652117,-16739389 }, + { -9935934,-4289447,-25279823,4372842,2087473,10399484,31870908,14690798,17361620,11864968 }, + }, + { + { -11307610,6210372,13206574,5806320,-29017692,-13967200,-12331205,-7486601,-25578460,-16240689 }, + { 14668462,-12270235,26039039,15305210,25515617,4542480,10453892,6577524,9145645,-6443880 }, + { 5974874,3053895,-9433049,-10385191,-31865124,3225009,-7972642,3936128,-5652273,-3050304 }, + }, + { + { 30625386,-4729400,-25555961,-12792866,-20484575,7695099,17097188,-16303496,-27999779,1803632 }, + { -3553091,9865099,-5228566,4272701,-5673832,-16689700,14911344,12196514,-21405489,7047412 }, + { 20093277,9920966,-11138194,-5343857,13161587,12044805,-32856851,4124601,-32343828,-10257566 }, + }, + { + { -20788824,14084654,-13531713,7842147,19119038,-13822605,4752377,-8714640,-21679658,2288038 }, + { -26819236,-3283715,29965059,3039786,-14473765,2540457,29457502,14625692,-24819617,12570232 }, + { -1063558,-11551823,16920318,12494842,1278292,-5869109,-21159943,-3498680,-11974704,4724943 }, + }, + { + { 17960970,-11775534,-4140968,-9702530,-8876562,-1410617,-12907383,-8659932,-29576300,1903856 }, + { 23134274,-14279132,-10681997,-1611936,20684485,15770816,-12989750,3190296,26955097,14109738 }, + { 15308788,5320727,-30113809,-14318877,22902008,7767164,29425325,-11277562,31960942,11934971 }, + }, + { + { -27395711,8435796,4109644,12222639,-24627868,14818669,20638173,4875028,10491392,1379718 }, + { -13159415,9197841,3875503,-8936108,-1383712,-5879801,33518459,16176658,21432314,12180697 }, + { -11787308,11500838,13787581,-13832590,-22430679,10140205,1465425,12689540,-10301319,-13872883 }, + }, +}, +{ + { + { 5414091,-15386041,-21007664,9643570,12834970,1186149,-2622916,-1342231,26128231,6032912 }, + { -26337395,-13766162,32496025,-13653919,17847801,-12669156,3604025,8316894,-25875034,-10437358 }, + { 3296484,6223048,24680646,-12246460,-23052020,5903205,-8862297,-4639164,12376617,3188849 }, + }, + { + { 29190488,-14659046,27549113,-1183516,3520066,-10697301,32049515,-7309113,-16109234,-9852307 }, + { -14744486,-9309156,735818,-598978,-20407687,-5057904,25246078,-15795669,18640741,-960977 }, + { -6928835,-16430795,10361374,5642961,4910474,12345252,-31638386,-494430,10530747,1053335 }, + }, + { + { -29265967,-14186805,-13538216,-12117373,-19457059,-10655384,-31462369,-2948985,24018831,15026644 }, + { -22592535,-3145277,-2289276,5953843,-13440189,9425631,25310643,13003497,-2314791,-15145616 }, + { -27419985,-603321,-8043984,-1669117,-26092265,13987819,-27297622,187899,-23166419,-2531735 }, + }, + { + { -21744398,-13810475,1844840,5021428,-10434399,-15911473,9716667,16266922,-5070217,726099 }, + { 29370922,-6053998,7334071,-15342259,9385287,2247707,-13661962,-4839461,30007388,-15823341 }, + { -936379,16086691,23751945,-543318,-1167538,-5189036,9137109,730663,9835848,4555336 }, + }, + { + { -23376435,1410446,-22253753,-12899614,30867635,15826977,17693930,544696,-11985298,12422646 }, + { 31117226,-12215734,-13502838,6561947,-9876867,-12757670,-5118685,-4096706,29120153,13924425 }, + { -17400879,-14233209,19675799,-2734756,-11006962,-5858820,-9383939,-11317700,7240931,-237388 }, + }, + { + { -31361739,-11346780,-15007447,-5856218,-22453340,-12152771,1222336,4389483,3293637,-15551743 }, + { -16684801,-14444245,11038544,11054958,-13801175,-3338533,-24319580,7733547,12796905,-6335822 }, + { -8759414,-10817836,-25418864,10783769,-30615557,-9746811,-28253339,3647836,3222231,-11160462 }, + }, + { + { 18606113,1693100,-25448386,-15170272,4112353,10045021,23603893,-2048234,-7550776,2484985 }, + { 9255317,-3131197,-12156162,-1004256,13098013,-9214866,16377220,-2102812,-19802075,-3034702 }, + { -22729289,7496160,-5742199,11329249,19991973,-3347502,-31718148,9936966,-30097688,-10618797 }, + }, + { + { 21878590,-5001297,4338336,13643897,-3036865,13160960,19708896,5415497,-7360503,-4109293 }, + { 27736861,10103576,12500508,8502413,-3413016,-9633558,10436918,-1550276,-23659143,-8132100 }, + { 19492550,-12104365,-29681976,-852630,-3208171,12403437,30066266,8367329,13243957,8709688 }, + }, +}, +{ + { + { 12015105,2801261,28198131,10151021,24818120,-4743133,-11194191,-5645734,5150968,7274186 }, + { 2831366,-12492146,1478975,6122054,23825128,-12733586,31097299,6083058,31021603,-9793610 }, + { -2529932,-2229646,445613,10720828,-13849527,-11505937,-23507731,16354465,15067285,-14147707 }, + }, + { + { 7840942,14037873,-33364863,15934016,-728213,-3642706,21403988,1057586,-19379462,-12403220 }, + { 915865,-16469274,15608285,-8789130,-24357026,6060030,-17371319,8410997,-7220461,16527025 }, + { 32922597,-556987,20336074,-16184568,10903705,-5384487,16957574,52992,23834301,6588044 }, + }, + { + { 32752030,11232950,3381995,-8714866,22652988,-10744103,17159699,16689107,-20314580,-1305992 }, + { -4689649,9166776,-25710296,-10847306,11576752,12733943,7924251,-2752281,1976123,-7249027 }, + { 21251222,16309901,-2983015,-6783122,30810597,12967303,156041,-3371252,12331345,-8237197 }, + }, + { + { 8651614,-4477032,-16085636,-4996994,13002507,2950805,29054427,-5106970,10008136,-4667901 }, + { 31486080,15114593,-14261250,12951354,14369431,-7387845,16347321,-13662089,8684155,-10532952 }, + { 19443825,11385320,24468943,-9659068,-23919258,2187569,-26263207,-6086921,31316348,14219878 }, + }, + { + { -28594490,1193785,32245219,11392485,31092169,15722801,27146014,6992409,29126555,9207390 }, + { 32382935,1110093,18477781,11028262,-27411763,-7548111,-4980517,10843782,-7957600,-14435730 }, + { 2814918,7836403,27519878,-7868156,-20894015,-11553689,-21494559,8550130,28346258,1994730 }, + }, + { + { -19578299,8085545,-14000519,-3948622,2785838,-16231307,-19516951,7174894,22628102,8115180 }, + { -30405132,955511,-11133838,-15078069,-32447087,-13278079,-25651578,3317160,-9943017,930272 }, + { -15303681,-6833769,28856490,1357446,23421993,1057177,24091212,-1388970,-22765376,-10650715 }, + }, + { + { -22751231,-5303997,-12907607,-12768866,-15811511,-7797053,-14839018,-16554220,-1867018,8398970 }, + { -31969310,2106403,-4736360,1362501,12813763,16200670,22981545,-6291273,18009408,-15772772 }, + { -17220923,-9545221,-27784654,14166835,29815394,7444469,29551787,-3727419,19288549,1325865 }, + }, + { + { 15100157,-15835752,-23923978,-1005098,-26450192,15509408,12376730,-3479146,33166107,-8042750 }, + { 20909231,13023121,-9209752,16251778,-5778415,-8094914,12412151,10018715,2213263,-13878373 }, + { 32529814,-11074689,30361439,-16689753,-9135940,1513226,22922121,6382134,-5766928,8371348 }, + }, +}, +{ + { + { 9923462,11271500,12616794,3544722,-29998368,-1721626,12891687,-8193132,-26442943,10486144 }, + { -22597207,-7012665,8587003,-8257861,4084309,-12970062,361726,2610596,-23921530,-11455195 }, + { 5408411,-1136691,-4969122,10561668,24145918,14240566,31319731,-4235541,19985175,-3436086 }, + }, + { + { -13994457,16616821,14549246,3341099,32155958,13648976,-17577068,8849297,65030,8370684 }, + { -8320926,-12049626,31204563,5839400,-20627288,-1057277,-19442942,6922164,12743482,-9800518 }, + { -2361371,12678785,28815050,4759974,-23893047,4884717,23783145,11038569,18800704,255233 }, + }, + { + { -5269658,-1773886,13957886,7990715,23132995,728773,13393847,9066957,19258688,-14753793 }, + { -2936654,-10827535,-10432089,14516793,-3640786,4372541,-31934921,2209390,-1524053,2055794 }, + { 580882,16705327,5468415,-2683018,-30926419,-14696000,-7203346,-8994389,-30021019,7394435 }, + }, + { + { 23838809,1822728,-15738443,15242727,8318092,-3733104,-21672180,-3492205,-4821741,14799921 }, + { 13345610,9759151,3371034,-16137791,16353039,8577942,31129804,13496856,-9056018,7402518 }, + { 2286874,-4435931,-20042458,-2008336,-13696227,5038122,11006906,-15760352,8205061,1607563 }, + }, + { + { 14414086,-8002132,3331830,-3208217,22249151,-5594188,18364661,-2906958,30019587,-9029278 }, + { -27688051,1585953,-10775053,931069,-29120221,-11002319,-14410829,12029093,9944378,8024 }, + { 4368715,-3709630,29874200,-15022983,-20230386,-11410704,-16114594,-999085,-8142388,5640030 }, + }, + { + { 10299610,13746483,11661824,16234854,7630238,5998374,9809887,-16694564,15219798,-14327783 }, + { 27425505,-5719081,3055006,10660664,23458024,595578,-15398605,-1173195,-18342183,9742717 }, + { 6744077,2427284,26042789,2720740,-847906,1118974,32324614,7406442,12420155,1994844 }, + }, + { + { 14012521,-5024720,-18384453,-9578469,-26485342,-3936439,-13033478,-10909803,24319929,-6446333 }, + { 16412690,-4507367,10772641,15929391,-17068788,-4658621,10555945,-10484049,-30102368,-4739048 }, + { 22397382,-7767684,-9293161,-12792868,17166287,-9755136,-27333065,6199366,21880021,-12250760 }, + }, + { + { -4283307,5368523,-31117018,8163389,-30323063,3209128,16557151,8890729,8840445,4957760 }, + { -15447727,709327,-6919446,-10870178,-29777922,6522332,-21720181,12130072,-14796503,5005757 }, + { -2114751,-14308128,23019042,15765735,-25269683,6002752,10183197,-13239326,-16395286,-2176112 }, + }, +}, +{ + { + { -19025756,1632005,13466291,-7995100,-23640451,16573537,-32013908,-3057104,22208662,2000468 }, + { 3065073,-1412761,-25598674,-361432,-17683065,-5703415,-8164212,11248527,-3691214,-7414184 }, + { 10379208,-6045554,8877319,1473647,-29291284,-12507580,16690915,2553332,-3132688,16400289 }, + }, + { + { 15716668,1254266,-18472690,7446274,-8448918,6344164,-22097271,-7285580,26894937,9132066 }, + { 24158887,12938817,11085297,-8177598,-28063478,-4457083,-30576463,64452,-6817084,-2692882 }, + { 13488534,7794716,22236231,5989356,25426474,-12578208,2350710,-3418511,-4688006,2364226 }, + }, + { + { 16335052,9132434,25640582,6678888,1725628,8517937,-11807024,-11697457,15445875,-7798101 }, + { 29004207,-7867081,28661402,-640412,-12794003,-7943086,31863255,-4135540,-278050,-15759279 }, + { -6122061,-14866665,-28614905,14569919,-10857999,-3591829,10343412,-6976290,-29828287,-10815811 }, + }, + { + { 27081650,3463984,14099042,-4517604,1616303,-6205604,29542636,15372179,17293797,960709 }, + { 20263915,11434237,-5765435,11236810,13505955,-10857102,-16111345,6493122,-19384511,7639714 }, + { -2830798,-14839232,25403038,-8215196,-8317012,-16173699,18006287,-16043750,29994677,-15808121 }, + }, + { + { 9769828,5202651,-24157398,-13631392,-28051003,-11561624,-24613141,-13860782,-31184575,709464 }, + { 12286395,13076066,-21775189,-1176622,-25003198,4057652,-32018128,-8890874,16102007,13205847 }, + { 13733362,5599946,10557076,3195751,-5557991,8536970,-25540170,8525972,10151379,10394400 }, + }, + { + { 4024660,-16137551,22436262,12276534,-9099015,-2686099,19698229,11743039,-33302334,8934414 }, + { -15879800,-4525240,-8580747,-2934061,14634845,-698278,-9449077,3137094,-11536886,11721158 }, + { 17555939,-5013938,8268606,2331751,-22738815,9761013,9319229,8835153,-9205489,-1280045 }, + }, + { + { -461409,-7830014,20614118,16688288,-7514766,-4807119,22300304,505429,6108462,-6183415 }, + { -5070281,12367917,-30663534,3234473,32617080,-8422642,29880583,-13483331,-26898490,-7867459 }, + { -31975283,5726539,26934134,10237677,-3173717,-605053,24199304,3795095,7592688,-14992079 }, + }, + { + { 21594432,-14964228,17466408,-4077222,32537084,2739898,6407723,12018833,-28256052,4298412 }, + { -20650503,-11961496,-27236275,570498,3767144,-1717540,13891942,-1569194,13717174,10805743 }, + { -14676630,-15644296,15287174,11927123,24177847,-8175568,-796431,14860609,-26938930,-5863836 }, + }, +}, +{ + { + { 12962541,5311799,-10060768,11658280,18855286,-7954201,13286263,-12808704,-4381056,9882022 }, + { 18512079,11319350,-20123124,15090309,18818594,5271736,-22727904,3666879,-23967430,-3299429 }, + { -6789020,-3146043,16192429,13241070,15898607,-14206114,-10084880,-6661110,-2403099,5276065 }, + }, + { + { 30169808,-5317648,26306206,-11750859,27814964,7069267,7152851,3684982,1449224,13082861 }, + { 10342826,3098505,2119311,193222,25702612,12233820,23697382,15056736,-21016438,-8202000 }, + { -33150110,3261608,22745853,7948688,19370557,-15177665,-26171976,6482814,-10300080,-11060101 }, + }, + { + { 32869458,-5408545,25609743,15678670,-10687769,-15471071,26112421,2521008,-22664288,6904815 }, + { 29506923,4457497,3377935,-9796444,-30510046,12935080,1561737,3841096,-29003639,-6657642 }, + { 10340844,-6630377,-18656632,-2278430,12621151,-13339055,30878497,-11824370,-25584551,5181966 }, + }, + { + { 25940115,-12658025,17324188,-10307374,-8671468,15029094,24396252,-16450922,-2322852,-12388574 }, + { -21765684,9916823,-1300409,4079498,-1028346,11909559,1782390,12641087,20603771,-6561742 }, + { -18882287,-11673380,24849422,11501709,13161720,-4768874,1925523,11914390,4662781,7820689 }, + }, + { + { 12241050,-425982,8132691,9393934,32846760,-1599620,29749456,12172924,16136752,15264020 }, + { -10349955,-14680563,-8211979,2330220,-17662549,-14545780,10658213,6671822,19012087,3772772 }, + { 3753511,-3421066,10617074,2028709,14841030,-6721664,28718732,-15762884,20527771,12988982 }, + }, + { + { -14822485,-5797269,-3707987,12689773,-898983,-10914866,-24183046,-10564943,3299665,-12424953 }, + { -16777703,-15253301,-9642417,4978983,3308785,8755439,6943197,6461331,-25583147,8991218 }, + { -17226263,1816362,-1673288,-6086439,31783888,-8175991,-32948145,7417950,-30242287,1507265 }, + }, + { + { 29692663,6829891,-10498800,4334896,20945975,-11906496,-28887608,8209391,14606362,-10647073 }, + { -3481570,8707081,32188102,5672294,22096700,1711240,-33020695,9761487,4170404,-2085325 }, + { -11587470,14855945,-4127778,-1531857,-26649089,15084046,22186522,16002000,-14276837,-8400798 }, + }, + { + { -4811456,13761029,-31703877,-2483919,-3312471,7869047,-7113572,-9620092,13240845,10965870 }, + { -7742563,-8256762,-14768334,-13656260,-23232383,12387166,4498947,14147411,29514390,4302863 }, + { -13413405,-12407859,20757302,-13801832,14785143,8976368,-5061276,-2144373,17846988,-13971927 }, + }, +}, +{ + { + { -2244452,-754728,-4597030,-1066309,-6247172,1455299,-21647728,-9214789,-5222701,12650267 }, + { -9906797,-16070310,21134160,12198166,-27064575,708126,387813,13770293,-19134326,10958663 }, + { 22470984,12369526,23446014,-5441109,-21520802,-9698723,-11772496,-11574455,-25083830,4271862 }, + }, + { + { -25169565,-10053642,-19909332,15361595,-5984358,2159192,75375,-4278529,-32526221,8469673 }, + { 15854970,4148314,-8893890,7259002,11666551,13824734,-30531198,2697372,24154791,-9460943 }, + { 15446137,-15806644,29759747,14019369,30811221,-9610191,-31582008,12840104,24913809,9815020 }, + }, + { + { -4709286,-5614269,-31841498,-12288893,-14443537,10799414,-9103676,13438769,18735128,9466238 }, + { 11933045,9281483,5081055,-5183824,-2628162,-4905629,-7727821,-10896103,-22728655,16199064 }, + { 14576810,379472,-26786533,-8317236,-29426508,-10812974,-102766,1876699,30801119,2164795 }, + }, + { + { 15995086,3199873,13672555,13712240,-19378835,-4647646,-13081610,-15496269,-13492807,1268052 }, + { -10290614,-3659039,-3286592,10948818,23037027,3794475,-3470338,-12600221,-17055369,3565904 }, + { 29210088,-9419337,-5919792,-4952785,10834811,-13327726,-16512102,-10820713,-27162222,-14030531 }, + }, + { + { -13161890,15508588,16663704,-8156150,-28349942,9019123,-29183421,-3769423,2244111,-14001979 }, + { -5152875,-3800936,-9306475,-6071583,16243069,14684434,-25673088,-16180800,13491506,4641841 }, + { 10813417,643330,-19188515,-728916,30292062,-16600078,27548447,-7721242,14476989,-12767431 }, + }, + { + { 10292079,9984945,6481436,8279905,-7251514,7032743,27282937,-1644259,-27912810,12651324 }, + { -31185513,-813383,22271204,11835308,10201545,15351028,17099662,3988035,21721536,-3148940 }, + { 10202177,-6545839,-31373232,-9574638,-32150642,-8119683,-12906320,3852694,13216206,14842320 }, + }, + { + { -15815640,-10601066,-6538952,-7258995,-6984659,-6581778,-31500847,13765824,-27434397,9900184 }, + { 14465505,-13833331,-32133984,-14738873,-27443187,12990492,33046193,15796406,-7051866,-8040114 }, + { 30924417,-8279620,6359016,-12816335,16508377,9071735,-25488601,15413635,9524356,-7018878 }, + }, + { + { 12274201,-13175547,32627641,-1785326,6736625,13267305,5237659,-5109483,15663516,4035784 }, + { -2951309,8903985,17349946,601635,-16432815,-4612556,-13732739,-15889334,-22258478,4659091 }, + { -16916263,-4952973,-30393711,-15158821,20774812,15897498,5736189,15026997,-2178256,-13455585 }, + }, +}, +{ + { + { -8858980,-2219056,28571666,-10155518,-474467,-10105698,-3801496,278095,23440562,-290208 }, + { 10226241,-5928702,15139956,120818,-14867693,5218603,32937275,11551483,-16571960,-7442864 }, + { 17932739,-12437276,-24039557,10749060,11316803,7535897,22503767,5561594,-3646624,3898661 }, + }, + { + { 7749907,-969567,-16339731,-16464,-25018111,15122143,-1573531,7152530,21831162,1245233 }, + { 26958459,-14658026,4314586,8346991,-5677764,11960072,-32589295,-620035,-30402091,-16716212 }, + { -12165896,9166947,33491384,13673479,29787085,13096535,6280834,14587357,-22338025,13987525 }, + }, + { + { -24349909,7778775,21116000,15572597,-4833266,-5357778,-4300898,-5124639,-7469781,-2858068 }, + { 9681908,-6737123,-31951644,13591838,-6883821,386950,31622781,6439245,-14581012,4091397 }, + { -8426427,1470727,-28109679,-1596990,3978627,-5123623,-19622683,12092163,29077877,-14741988 }, + }, + { + { 5269168,-6859726,-13230211,-8020715,25932563,1763552,-5606110,-5505881,-20017847,2357889 }, + { 32264008,-15407652,-5387735,-1160093,-2091322,-3946900,23104804,-12869908,5727338,189038 }, + { 14609123,-8954470,-6000566,-16622781,-14577387,-7743898,-26745169,10942115,-25888931,-14884697 }, + }, + { + { 20513500,5557931,-15604613,7829531,26413943,-2019404,-21378968,7471781,13913677,-5137875 }, + { -25574376,11967826,29233242,12948236,-6754465,4713227,-8940970,14059180,12878652,8511905 }, + { -25656801,3393631,-2955415,-7075526,-2250709,9366908,-30223418,6812974,5568676,-3127656 }, + }, + { + { 11630004,12144454,2116339,13606037,27378885,15676917,-17408753,-13504373,-14395196,8070818 }, + { 27117696,-10007378,-31282771,-5570088,1127282,12772488,-29845906,10483306,-11552749,-1028714 }, + { 10637467,-5688064,5674781,1072708,-26343588,-6982302,-1683975,9177853,-27493162,15431203 }, + }, + { + { 20525145,10892566,-12742472,12779443,-29493034,16150075,-28240519,14943142,-15056790,-7935931 }, + { -30024462,5626926,-551567,-9981087,753598,11981191,25244767,-3239766,-3356550,9594024 }, + { -23752644,2636870,-5163910,-10103818,585134,7877383,11345683,-6492290,13352335,-10977084 }, + }, + { + { -1931799,-5407458,3304649,-12884869,17015806,-4877091,-29783850,-7752482,-13215537,-319204 }, + { 20239939,6607058,6203985,3483793,-18386976,-779229,-20723742,15077870,-22750759,14523817 }, + { 27406042,-6041657,27423596,-4497394,4996214,10002360,-28842031,-4545494,-30172742,-4805667 }, + }, +}, +{ + { + { 11374242,12660715,17861383,-12540833,10935568,1099227,-13886076,-9091740,-27727044,11358504 }, + { -12730809,10311867,1510375,10778093,-2119455,-9145702,32676003,11149336,-26123651,4985768 }, + { -19096303,341147,-6197485,-239033,15756973,-8796662,-983043,13794114,-19414307,-15621255 }, + }, + { + { 6490081,11940286,25495923,-7726360,8668373,-8751316,3367603,6970005,-1691065,-9004790 }, + { 1656497,13457317,15370807,6364910,13605745,8362338,-19174622,-5475723,-16796596,-5031438 }, + { -22273315,-13524424,-64685,-4334223,-18605636,-10921968,-20571065,-7007978,-99853,-10237333 }, + }, + { + { 17747465,10039260,19368299,-4050591,-20630635,-16041286,31992683,-15857976,-29260363,-5511971 }, + { 31932027,-4986141,-19612382,16366580,22023614,88450,11371999,-3744247,4882242,-10626905 }, + { 29796507,37186,19818052,10115756,-11829032,3352736,18551198,3272828,-5190932,-4162409 }, + }, + { + { 12501286,4044383,-8612957,-13392385,-32430052,5136599,-19230378,-3529697,330070,-3659409 }, + { 6384877,2899513,17807477,7663917,-2358888,12363165,25366522,-8573892,-271295,12071499 }, + { -8365515,-4042521,25133448,-4517355,-6211027,2265927,-32769618,1936675,-5159697,3829363 }, + }, + { + { 28425966,-5835433,-577090,-4697198,-14217555,6870930,7921550,-6567787,26333140,14267664 }, + { -11067219,11871231,27385719,-10559544,-4585914,-11189312,10004786,-8709488,-21761224,8930324 }, + { -21197785,-16396035,25654216,-1725397,12282012,11008919,1541940,4757911,-26491501,-16408940 }, + }, + { + { 13537262,-7759490,-20604840,10961927,-5922820,-13218065,-13156584,6217254,-15943699,13814990 }, + { -17422573,15157790,18705543,29619,24409717,-260476,27361681,9257833,-1956526,-1776914 }, + { -25045300,-10191966,15366585,15166509,-13105086,8423556,-29171540,12361135,-18685978,4578290 }, + }, + { + { 24579768,3711570,1342322,-11180126,-27005135,14124956,-22544529,14074919,21964432,8235257 }, + { -6528613,-2411497,9442966,-5925588,12025640,-1487420,-2981514,-1669206,13006806,2355433 }, + { -16304899,-13605259,-6632427,-5142349,16974359,-10911083,27202044,1719366,1141648,-12796236 }, + }, + { + { -12863944,-13219986,-8318266,-11018091,-6810145,-4843894,13475066,-3133972,32674895,13715045 }, + { 11423335,-5468059,32344216,8962751,24989809,9241752,-13265253,16086212,-28740881,-15642093 }, + { -1409668,12530728,-6368726,10847387,19531186,-14132160,-11709148,7791794,-27245943,4383347 }, + }, +}, +{ + { + { -28970898,5271447,-1266009,-9736989,-12455236,16732599,-4862407,-4906449,27193557,6245191 }, + { -15193956,5362278,-1783893,2695834,4960227,12840725,23061898,3260492,22510453,8577507 }, + { -12632451,11257346,-32692994,13548177,-721004,10879011,31168030,13952092,-29571492,-3635906 }, + }, + { + { 3877321,-9572739,32416692,5405324,-11004407,-13656635,3759769,11935320,5611860,8164018 }, + { -16275802,14667797,15906460,12155291,-22111149,-9039718,32003002,-8832289,5773085,-8422109 }, + { -23788118,-8254300,1950875,8937633,18686727,16459170,-905725,12376320,31632953,190926 }, + }, + { + { -24593607,-16138885,-8423991,13378746,14162407,6901328,-8288749,4508564,-25341555,-3627528 }, + { 8884438,-5884009,6023974,10104341,-6881569,-4941533,18722941,-14786005,-1672488,827625 }, + { -32720583,-16289296,-32503547,7101210,13354605,2659080,-1800575,-14108036,-24878478,1541286 }, + }, + { + { 2901347,-1117687,3880376,-10059388,-17620940,-3612781,-21802117,-3567481,20456845,-1885033 }, + { 27019610,12299467,-13658288,-1603234,-12861660,-4861471,-19540150,-5016058,29439641,15138866 }, + { 21536104,-6626420,-32447818,-10690208,-22408077,5175814,-5420040,-16361163,7779328,109896 }, + }, + { + { 30279744,14648750,-8044871,6425558,13639621,-743509,28698390,12180118,23177719,-554075 }, + { 26572847,3405927,-31701700,12890905,-19265668,5335866,-6493768,2378492,4439158,-13279347 }, + { -22716706,3489070,-9225266,-332753,18875722,-1140095,14819434,-12731527,-17717757,-5461437 }, + }, + { + { -5056483,16566551,15953661,3767752,-10436499,15627060,-820954,2177225,8550082,-15114165 }, + { -18473302,16596775,-381660,15663611,22860960,15585581,-27844109,-3582739,-23260460,-8428588 }, + { -32480551,15707275,-8205912,-5652081,29464558,2713815,-22725137,15860482,-21902570,1494193 }, + }, + { + { -19562091,-14087393,-25583872,-9299552,13127842,759709,21923482,16529112,8742704,12967017 }, + { -28464899,1553205,32536856,-10473729,-24691605,-406174,-8914625,-2933896,-29903758,15553883 }, + { 21877909,3230008,9881174,10539357,-4797115,2841332,11543572,14513274,19375923,-12647961 }, + }, + { + { 8832269,-14495485,13253511,5137575,5037871,4078777,24880818,-6222716,2862653,9455043 }, + { 29306751,5123106,20245049,-14149889,9592566,8447059,-2077124,-2990080,15511449,4789663 }, + { -20679756,7004547,8824831,-9434977,-4045704,-3750736,-5754762,108893,23513200,16652362 }, + }, +}, +{ + { + { -33256173,4144782,-4476029,-6579123,10770039,-7155542,-6650416,-12936300,-18319198,10212860 }, + { 2756081,8598110,7383731,-6859892,22312759,-1105012,21179801,2600940,-9988298,-12506466 }, + { -24645692,13317462,-30449259,-15653928,21365574,-10869657,11344424,864440,-2499677,-16710063 }, + }, + { + { -26432803,6148329,-17184412,-14474154,18782929,-275997,-22561534,211300,2719757,4940997 }, + { -1323882,3911313,-6948744,14759765,-30027150,7851207,21690126,8518463,26699843,5276295 }, + { -13149873,-6429067,9396249,365013,24703301,-10488939,1321586,149635,-15452774,7159369 }, + }, + { + { 9987780,-3404759,17507962,9505530,9731535,-2165514,22356009,8312176,22477218,-8403385 }, + { 18155857,-16504990,19744716,9006923,15154154,-10538976,24256460,-4864995,-22548173,9334109 }, + { 2986088,-4911893,10776628,-3473844,10620590,-7083203,-21413845,14253545,-22587149,536906 }, + }, + { + { 4377756,8115836,24567078,15495314,11625074,13064599,7390551,10589625,10838060,-15420424 }, + { -19342404,867880,9277171,-3218459,-14431572,-1986443,19295826,-15796950,6378260,699185 }, + { 7895026,4057113,-7081772,-13077756,-17886831,-323126,-716039,15693155,-5045064,-13373962 }, + }, + { + { -7737563,-5869402,-14566319,-7406919,11385654,13201616,31730678,-10962840,-3918636,-9669325 }, + { 10188286,-15770834,-7336361,13427543,22223443,14896287,30743455,7116568,-21786507,5427593 }, + { 696102,13206899,27047647,-10632082,15285305,-9853179,10798490,-4578720,19236243,12477404 }, + }, + { + { -11229439,11243796,-17054270,-8040865,-788228,-8167967,-3897669,11180504,-23169516,7733644 }, + { 17800790,-14036179,-27000429,-11766671,23887827,3149671,23466177,-10538171,10322027,15313801 }, + { 26246234,11968874,32263343,-5468728,6830755,-13323031,-15794704,-101982,-24449242,10890804 }, + }, + { + { -31365647,10271363,-12660625,-6267268,16690207,-13062544,-14982212,16484931,25180797,-5334884 }, + { -586574,10376444,-32586414,-11286356,19801893,10997610,2276632,9482883,316878,13820577 }, + { -9882808,-4510367,-2115506,16457136,-11100081,11674996,30756178,-7515054,30696930,-3712849 }, + }, + { + { 32988917,-9603412,12499366,7910787,-10617257,-11931514,-7342816,-9985397,-32349517,7392473 }, + { -8855661,15927861,9866406,-3649411,-2396914,-16655781,-30409476,-9134995,25112947,-2926644 }, + { -2504044,-436966,25621774,-5678772,15085042,-5479877,-24884878,-13526194,5537438,-13914319 }, + }, +}, +{ + { + { -11225584,2320285,-9584280,10149187,-33444663,5808648,-14876251,-1729667,31234590,6090599 }, + { -9633316,116426,26083934,2897444,-6364437,-2688086,609721,15878753,-6970405,-9034768 }, + { -27757857,247744,-15194774,-9002551,23288161,-10011936,-23869595,6503646,20650474,1804084 }, + }, + { + { -27589786,15456424,8972517,8469608,15640622,4439847,3121995,-10329713,27842616,-202328 }, + { -15306973,2839644,22530074,10026331,4602058,5048462,28248656,5031932,-11375082,12714369 }, + { 20807691,-7270825,29286141,11421711,-27876523,-13868230,-21227475,1035546,-19733229,12796920 }, + }, + { + { 12076899,-14301286,-8785001,-11848922,-25012791,16400684,-17591495,-12899438,3480665,-15182815 }, + { -32361549,5457597,28548107,7833186,7303070,-11953545,-24363064,-15921875,-33374054,2771025 }, + { -21389266,421932,26597266,6860826,22486084,-6737172,-17137485,-4210226,-24552282,15673397 }, + }, + { + { -20184622,2338216,19788685,-9620956,-4001265,-8740893,-20271184,4733254,3727144,-12934448 }, + { 6120119,814863,-11794402,-622716,6812205,-15747771,2019594,7975683,31123697,-10958981 }, + { 30069250,-11435332,30434654,2958439,18399564,-976289,12296869,9204260,-16432438,9648165 }, + }, + { + { 32705432,-1550977,30705658,7451065,-11805606,9631813,3305266,5248604,-26008332,-11377501 }, + { 17219865,2375039,-31570947,-5575615,-19459679,9219903,294711,15298639,2662509,-16297073 }, + { -1172927,-7558695,-4366770,-4287744,-21346413,-8434326,32087529,-1222777,32247248,-14389861 }, + }, + { + { 14312628,1221556,17395390,-8700143,-4945741,-8684635,-28197744,-9637817,-16027623,-13378845 }, + { -1428825,-9678990,-9235681,6549687,-7383069,-468664,23046502,9803137,17597934,2346211 }, + { 18510800,15337574,26171504,981392,-22241552,7827556,-23491134,-11323352,3059833,-11782870 }, + }, + { + { 10141598,6082907,17829293,-1947643,9830092,13613136,-25556636,-5544586,-33502212,3592096 }, + { 33114168,-15889352,-26525686,-13343397,33076705,8716171,1151462,1521897,-982665,-6837803 }, + { -32939165,-4255815,23947181,-324178,-33072974,-12305637,-16637686,3891704,26353178,693168 }, + }, + { + { 30374239,1595580,-16884039,13186931,4600344,406904,9585294,-400668,31375464,14369965 }, + { -14370654,-7772529,1510301,6434173,-18784789,-6262728,32732230,-13108839,17901441,16011505 }, + { 18171223,-11934626,-12500402,15197122,-11038147,-15230035,-19172240,-16046376,8764035,12309598 }, + }, +}, +{ + { + { 5975908,-5243188,-19459362,-9681747,-11541277,14015782,-23665757,1228319,17544096,-10593782 }, + { 5811932,-1715293,3442887,-2269310,-18367348,-8359541,-18044043,-15410127,-5565381,12348900 }, + { -31399660,11407555,25755363,6891399,-3256938,14872274,-24849353,8141295,-10632534,-585479 }, + }, + { + { -12675304,694026,-5076145,13300344,14015258,-14451394,-9698672,-11329050,30944593,1130208 }, + { 8247766,-6710942,-26562381,-7709309,-14401939,-14648910,4652152,2488540,23550156,-271232 }, + { 17294316,-3788438,7026748,15626851,22990044,113481,2267737,-5908146,-408818,-137719 }, + }, + { + { 16091085,-16253926,18599252,7340678,2137637,-1221657,-3364161,14550936,3260525,-7166271 }, + { -4910104,-13332887,18550887,10864893,-16459325,-7291596,-23028869,-13204905,-12748722,2701326 }, + { -8574695,16099415,4629974,-16340524,-20786213,-6005432,-10018363,9276971,11329923,1862132 }, + }, + { + { 14763076,-15903608,-30918270,3689867,3511892,10313526,-21951088,12219231,-9037963,-940300 }, + { 8894987,-3446094,6150753,3013931,301220,15693451,-31981216,-2909717,-15438168,11595570 }, + { 15214962,3537601,-26238722,-14058872,4418657,-15230761,13947276,10730794,-13489462,-4363670 }, + }, + { + { -2538306,7682793,32759013,263109,-29984731,-7955452,-22332124,-10188635,977108,699994 }, + { -12466472,4195084,-9211532,550904,-15565337,12917920,19118110,-439841,-30534533,-14337913 }, + { 31788461,-14507657,4799989,7372237,8808585,-14747943,9408237,-10051775,12493932,-5409317 }, + }, + { + { -25680606,5260744,-19235809,-6284470,-3695942,16566087,27218280,2607121,29375955,6024730 }, + { 842132,-2794693,-4763381,-8722815,26332018,-12405641,11831880,6985184,-9940361,2854096 }, + { -4847262,-7969331,2516242,-5847713,9695691,-7221186,16512645,960770,12121869,16648078 }, + }, + { + { -15218652,14667096,-13336229,2013717,30598287,-464137,-31504922,-7882064,20237806,2838411 }, + { -19288047,4453152,15298546,-16178388,22115043,-15972604,12544294,-13470457,1068881,-12499905 }, + { -9558883,-16518835,33238498,13506958,30505848,-1114596,-8486907,-2630053,12521378,4845654 }, + }, + { + { -28198521,10744108,-2958380,10199664,7759311,-13088600,3409348,-873400,-6482306,-12885870 }, + { -23561822,6230156,-20382013,10655314,-24040585,-11621172,10477734,-1240216,-3113227,13974498 }, + { 12966261,15550616,-32038948,-1615346,21025980,-629444,5642325,7188737,18895762,12629579 }, + }, +}, +{ + { + { 14741879,-14946887,22177208,-11721237,1279741,8058600,11758140,789443,32195181,3895677 }, + { 10758205,15755439,-4509950,9243698,-4879422,6879879,-2204575,-3566119,-8982069,4429647 }, + { -2453894,15725973,-20436342,-10410672,-5803908,-11040220,-7135870,-11642895,18047436,-15281743 }, + }, + { + { -25173001,-11307165,29759956,11776784,-22262383,-15820455,10993114,-12850837,-17620701,-9408468 }, + { 21987233,700364,-24505048,14972008,-7774265,-5718395,32155026,2581431,-29958985,8773375 }, + { -25568350,454463,-13211935,16126715,25240068,8594567,20656846,12017935,-7874389,-13920155 }, + }, + { + { 6028182,6263078,-31011806,-11301710,-818919,2461772,-31841174,-5468042,-1721788,-2776725 }, + { -12278994,16624277,987579,-5922598,32908203,1248608,7719845,-4166698,28408820,6816612 }, + { -10358094,-8237829,19549651,-12169222,22082623,16147817,20613181,13982702,-10339570,5067943 }, + }, + { + { -30505967,-3821767,12074681,13582412,-19877972,2443951,-19719286,12746132,5331210,-10105944 }, + { 30528811,3601899,-1957090,4619785,-27361822,-15436388,24180793,-12570394,27679908,-1648928 }, + { 9402404,-13957065,32834043,10838634,-26580150,-13237195,26653274,-8685565,22611444,-12715406 }, + }, + { + { 22190590,1118029,22736441,15130463,-30460692,-5991321,19189625,-4648942,4854859,6622139 }, + { -8310738,-2953450,-8262579,-3388049,-10401731,-271929,13424426,-3567227,26404409,13001963 }, + { -31241838,-15415700,-2994250,8939346,11562230,-12840670,-26064365,-11621720,-15405155,11020693 }, + }, + { + { 1866042,-7949489,-7898649,-10301010,12483315,13477547,3175636,-12424163,28761762,1406734 }, + { -448555,-1777666,13018551,3194501,-9580420,-11161737,24760585,-4347088,25577411,-13378680 }, + { -24290378,4759345,-690653,-1852816,2066747,10693769,-29595790,9884936,-9368926,4745410 }, + }, + { + { -9141284,6049714,-19531061,-4341411,-31260798,9944276,-15462008,-11311852,10931924,-11931931 }, + { -16561513,14112680,-8012645,4817318,-8040464,-11414606,-22853429,10856641,-20470770,13434654 }, + { 22759489,-10073434,-16766264,-1871422,13637442,-10168091,1765144,-12654326,28445307,-5364710 }, + }, + { + { 29875063,12493613,2795536,-3786330,1710620,15181182,-10195717,-8788675,9074234,1167180 }, + { -26205683,11014233,-9842651,-2635485,-26908120,7532294,-18716888,-9535498,3843903,9367684 }, + { -10969595,-6403711,9591134,9582310,11349256,108879,16235123,8601684,-139197,4242895 }, + }, +}, +{ + { + { 22092954,-13191123,-2042793,-11968512,32186753,-11517388,-6574341,2470660,-27417366,16625501 }, + { -11057722,3042016,13770083,-9257922,584236,-544855,-7770857,2602725,-27351616,14247413 }, + { 6314175,-10264892,-32772502,15957557,-10157730,168750,-8618807,14290061,27108877,-1180880 }, + }, + { + { -8586597,-7170966,13241782,10960156,-32991015,-13794596,33547976,-11058889,-27148451,981874 }, + { 22833440,9293594,-32649448,-13618667,-9136966,14756819,-22928859,-13970780,-10479804,-16197962 }, + { -7768587,3326786,-28111797,10783824,19178761,14905060,22680049,13906969,-15933690,3797899 }, + }, + { + { 21721356,-4212746,-12206123,9310182,-3882239,-13653110,23740224,-2709232,20491983,-8042152 }, + { 9209270,-15135055,-13256557,-6167798,-731016,15289673,25947805,15286587,30997318,-6703063 }, + { 7392032,16618386,23946583,-8039892,-13265164,-1533858,-14197445,-2321576,17649998,-250080 }, + }, + { + { -9301088,-14193827,30609526,-3049543,-25175069,-1283752,-15241566,-9525724,-2233253,7662146 }, + { -17558673,1763594,-33114336,15908610,-30040870,-12174295,7335080,-8472199,-3174674,3440183 }, + { -19889700,-5977008,-24111293,-9688870,10799743,-16571957,40450,-4431835,4862400,1133 }, + }, + { + { -32856209,-7873957,-5422389,14860950,-16319031,7956142,7258061,311861,-30594991,-7379421 }, + { -3773428,-1565936,28985340,7499440,24445838,9325937,29727763,16527196,18278453,15405622 }, + { -4381906,8508652,-19898366,-3674424,-5984453,15149970,-13313598,843523,-21875062,13626197 }, + }, + { + { 2281448,-13487055,-10915418,-2609910,1879358,16164207,-10783882,3953792,13340839,15928663 }, + { 31727126,-7179855,-18437503,-8283652,2875793,-16390330,-25269894,-7014826,-23452306,5964753 }, + { 4100420,-5959452,-17179337,6017714,-18705837,12227141,-26684835,11344144,2538215,-7570755 }, + }, + { + { -9433605,6123113,11159803,-2156608,30016280,14966241,-20474983,1485421,-629256,-15958862 }, + { -26804558,4260919,11851389,9658551,-32017107,16367492,-20205425,-13191288,11659922,-11115118 }, + { 26180396,10015009,-30844224,-8581293,5418197,9480663,2231568,-10170080,33100372,-1306171 }, + }, + { + { 15121113,-5201871,-10389905,15427821,-27509937,-15992507,21670947,4486675,-5931810,-14466380 }, + { 16166486,-9483733,-11104130,6023908,-31926798,-1364923,2340060,-16254968,-10735770,-10039824 }, + { 28042865,-3557089,-12126526,12259706,-3717498,-6945899,6766453,-8689599,18036436,5803270 }, + }, +}, +{ + { + { -817581,6763912,11803561,1585585,10958447,-2671165,23855391,4598332,-6159431,-14117438 }, + { -31031306,-14256194,17332029,-2383520,31312682,-5967183,696309,50292,-20095739,11763584 }, + { -594563,-2514283,-32234153,12643980,12650761,14811489,665117,-12613632,-19773211,-10713562 }, + }, + { + { 30464590,-11262872,-4127476,-12734478,19835327,-7105613,-24396175,2075773,-17020157,992471 }, + { 18357185,-6994433,7766382,16342475,-29324918,411174,14578841,8080033,-11574335,-10601610 }, + { 19598397,10334610,12555054,2555664,18821899,-10339780,21873263,16014234,26224780,16452269 }, + }, + { + { -30223925,5145196,5944548,16385966,3976735,2009897,-11377804,-7618186,-20533829,3698650 }, + { 14187449,3448569,-10636236,-10810935,-22663880,-3433596,7268410,-10890444,27394301,12015369 }, + { 19695761,16087646,28032085,12999827,6817792,11427614,20244189,-1312777,-13259127,-3402461 }, + }, + { + { 30860103,12735208,-1888245,-4699734,-16974906,2256940,-8166013,12298312,-8550524,-10393462 }, + { -5719826,-11245325,-1910649,15569035,26642876,-7587760,-5789354,-15118654,-4976164,12651793 }, + { -2848395,9953421,11531313,-5282879,26895123,-12697089,-13118820,-16517902,9768698,-2533218 }, + }, + { + { -24719459,1894651,-287698,-4704085,15348719,-8156530,32767513,12765450,4940095,10678226 }, + { 18860224,15980149,-18987240,-1562570,-26233012,-11071856,-7843882,13944024,-24372348,16582019 }, + { -15504260,4970268,-29893044,4175593,-20993212,-2199756,-11704054,15444560,-11003761,7989037 }, + }, + { + { 31490452,5568061,-2412803,2182383,-32336847,4531686,-32078269,6200206,-19686113,-14800171 }, + { -17308668,-15879940,-31522777,-2831,-32887382,16375549,8680158,-16371713,28550068,-6857132 }, + { -28126887,-5688091,16837845,-1820458,-6850681,12700016,-30039981,4364038,1155602,5988841 }, + }, + { + { 21890435,-13272907,-12624011,12154349,-7831873,15300496,23148983,-4470481,24618407,8283181 }, + { -33136107,-10512751,9975416,6841041,-31559793,16356536,3070187,-7025928,1466169,10740210 }, + { -1509399,-15488185,-13503385,-10655916,32799044,909394,-13938903,-5779719,-32164649,-15327040 }, + }, + { + { 3960823,-14267803,-28026090,-15918051,-19404858,13146868,15567327,951507,-3260321,-573935 }, + { 24740841,5052253,-30094131,8961361,25877428,6165135,-24368180,14397372,-7380369,-6144105 }, + { -28888365,3510803,-28103278,-1158478,-11238128,-10631454,-15441463,-14453128,-1625486,-6494814 }, + }, +}, +{ + { + { 793299,-9230478,8836302,-6235707,-27360908,-2369593,33152843,-4885251,-9906200,-621852 }, + { 5666233,525582,20782575,-8038419,-24538499,14657740,16099374,1468826,-6171428,-15186581 }, + { -4859255,-3779343,-2917758,-6748019,7778750,11688288,-30404353,-9871238,-1558923,-9863646 }, + }, + { + { 10896332,-7719704,824275,472601,-19460308,3009587,25248958,14783338,-30581476,-15757844 }, + { 10566929,12612572,-31944212,11118703,-12633376,12362879,21752402,8822496,24003793,14264025 }, + { 27713862,-7355973,-11008240,9227530,27050101,2504721,23886875,-13117525,13958495,-5732453 }, + }, + { + { -23481610,4867226,-27247128,3900521,29838369,-8212291,-31889399,-10041781,7340521,-15410068 }, + { 4646514,-8011124,-22766023,-11532654,23184553,8566613,31366726,-1381061,-15066784,-10375192 }, + { -17270517,12723032,-16993061,14878794,21619651,-6197576,27584817,3093888,-8843694,3849921 }, + }, + { + { -9064912,2103172,25561640,-15125738,-5239824,9582958,32477045,-9017955,5002294,-15550259 }, + { -12057553,-11177906,21115585,-13365155,8808712,-12030708,16489530,13378448,-25845716,12741426 }, + { -5946367,10645103,-30911586,15390284,-3286982,-7118677,24306472,15852464,28834118,-7646072 }, + }, + { + { -17335748,-9107057,-24531279,9434953,-8472084,-583362,-13090771,455841,20461858,5491305 }, + { 13669248,-16095482,-12481974,-10203039,-14569770,-11893198,-24995986,11293807,-28588204,-9421832 }, + { 28497928,6272777,-33022994,14470570,8906179,-1225630,18504674,-14165166,29867745,-8795943 }, + }, + { + { -16207023,13517196,-27799630,-13697798,24009064,-6373891,-6367600,-13175392,22853429,-4012011 }, + { 24191378,16712145,-13931797,15217831,14542237,1646131,18603514,-11037887,12876623,-2112447 }, + { 17902668,4518229,-411702,-2829247,26878217,5258055,-12860753,608397,16031844,3723494 }, + }, + { + { -28632773,12763728,-20446446,7577504,33001348,-13017745,17558842,-7872890,23896954,-4314245 }, + { -20005381,-12011952,31520464,605201,2543521,5991821,-2945064,7229064,-9919646,-8826859 }, + { 28816045,298879,-28165016,-15920938,19000928,-1665890,-12680833,-2949325,-18051778,-2082915 }, + }, + { + { 16000882,-344896,3493092,-11447198,-29504595,-13159789,12577740,16041268,-19715240,7847707 }, + { 10151868,10572098,27312476,7922682,14825339,4723128,-32855931,-6519018,-10020567,3852848 }, + { -11430470,15697596,-21121557,-4420647,5386314,15063598,16514493,-15932110,29330899,-15076224 }, + }, +}, +{ + { + { -25499735,-4378794,-15222908,-6901211,16615731,2051784,3303702,15490,-27548796,12314391 }, + { 15683520,-6003043,18109120,-9980648,15337968,-5997823,-16717435,15921866,16103996,-3731215 }, + { -23169824,-10781249,13588192,-1628807,-3798557,-1074929,-19273607,5402699,-29815713,-9841101 }, + }, + { + { 23190676,2384583,-32714340,3462154,-29903655,-1529132,-11266856,8911517,-25205859,2739713 }, + { 21374101,-3554250,-33524649,9874411,15377179,11831242,-33529904,6134907,4931255,11987849 }, + { -7732,-2978858,-16223486,7277597,105524,-322051,-31480539,13861388,-30076310,10117930 }, + }, + { + { -29501170,-10744872,-26163768,13051539,-25625564,5089643,-6325503,6704079,12890019,15728940 }, + { -21972360,-11771379,-951059,-4418840,14704840,2695116,903376,-10428139,12885167,8311031 }, + { -17516482,5352194,10384213,-13811658,7506451,13453191,26423267,4384730,1888765,-5435404 }, + }, + { + { -25817338,-3107312,-13494599,-3182506,30896459,-13921729,-32251644,-12707869,-19464434,-3340243 }, + { -23607977,-2665774,-526091,4651136,5765089,4618330,6092245,14845197,17151279,-9854116 }, + { -24830458,-12733720,-15165978,10367250,-29530908,-265356,22825805,-7087279,-16866484,16176525 }, + }, + { + { -23583256,6564961,20063689,3798228,-4740178,7359225,2006182,-10363426,-28746253,-10197509 }, + { -10626600,-4486402,-13320562,-5125317,3432136,-6393229,23632037,-1940610,32808310,1099883 }, + { 15030977,5768825,-27451236,-2887299,-6427378,-15361371,-15277896,-6809350,2051441,-15225865 }, + }, + { + { -3362323,-7239372,7517890,9824992,23555850,295369,5148398,-14154188,-22686354,16633660 }, + { 4577086,-16752288,13249841,-15304328,19958763,-14537274,18559670,-10759549,8402478,-9864273 }, + { -28406330,-1051581,-26790155,-907698,-17212414,-11030789,9453451,-14980072,17983010,9967138 }, + }, + { + { -25762494,6524722,26585488,9969270,24709298,1220360,-1677990,7806337,17507396,3651560 }, + { -10420457,-4118111,14584639,15971087,-15768321,8861010,26556809,-5574557,-18553322,-11357135 }, + { 2839101,14284142,4029895,3472686,14402957,12689363,-26642121,8459447,-5605463,-7621941 }, + }, + { + { -4839289,-3535444,9744961,2871048,25113978,3187018,-25110813,-849066,17258084,-7977739 }, + { 18164541,-10595176,-17154882,-1542417,19237078,-9745295,23357533,-15217008,26908270,12150756 }, + { -30264870,-7647865,5112249,-7036672,-1499807,-6974257,43168,-5537701,-32302074,16215819 }, + }, +}, +{ + { + { -6898905,9824394,-12304779,-4401089,-31397141,-6276835,32574489,12532905,-7503072,-8675347 }, + { -27343522,-16515468,-27151524,-10722951,946346,16291093,254968,7168080,21676107,-1943028 }, + { 21260961,-8424752,-16831886,-11920822,-23677961,3968121,-3651949,-6215466,-3556191,-7913075 }, + }, + { + { 16544754,13250366,-16804428,15546242,-4583003,12757258,-2462308,-8680336,-18907032,-9662799 }, + { -2415239,-15577728,18312303,4964443,-15272530,-12653564,26820651,16690659,25459437,-4564609 }, + { -25144690,11425020,28423002,-11020557,-6144921,-15826224,9142795,-2391602,-6432418,-1644817 }, + }, + { + { -23104652,6253476,16964147,-3768872,-25113972,-12296437,-27457225,-16344658,6335692,7249989 }, + { -30333227,13979675,7503222,-12368314,-11956721,-4621693,-30272269,2682242,25993170,-12478523 }, + { 4364628,5930691,32304656,-10044554,-8054781,15091131,22857016,-10598955,31820368,15075278 }, + }, + { + { 31879134,-8918693,17258761,90626,-8041836,-4917709,24162788,-9650886,-17970238,12833045 }, + { 19073683,14851414,-24403169,-11860168,7625278,11091125,-19619190,2074449,-9413939,14905377 }, + { 24483667,-11935567,-2518866,-11547418,-1553130,15355506,-25282080,9253129,27628530,-7555480 }, + }, + { + { 17597607,8340603,19355617,552187,26198470,-3176583,4593324,-9157582,-14110875,15297016 }, + { 510886,14337390,-31785257,16638632,6328095,2713355,-20217417,-11864220,8683221,2921426 }, + { 18606791,11874196,27155355,-5281482,-24031742,6265446,-25178240,-1278924,4674690,13890525 }, + }, + { + { 13609624,13069022,-27372361,-13055908,24360586,9592974,14977157,9835105,4389687,288396 }, + { 9922506,-519394,13613107,5883594,-18758345,-434263,-12304062,8317628,23388070,16052080 }, + { 12720016,11937594,-31970060,-5028689,26900120,8561328,-20155687,-11632979,-14754271,-10812892 }, + }, + { + { 15961858,14150409,26716931,-665832,-22794328,13603569,11829573,7467844,-28822128,929275 }, + { 11038231,-11582396,-27310482,-7316562,-10498527,-16307831,-23479533,-9371869,-21393143,2465074 }, + { 20017163,-4323226,27915242,1529148,12396362,15675764,13817261,-9658066,2463391,-4622140 }, + }, + { + { -16358878,-12663911,-12065183,4996454,-1256422,1073572,9583558,12851107,4003896,12673717 }, + { -1731589,-15155870,-3262930,16143082,19294135,13385325,14741514,-9103726,7903886,2348101 }, + { 24536016,-16515207,12715592,-3862155,1511293,10047386,-3842346,-7129159,-28377538,10048127 }, + }, +}, +{ + { + { -12622226,-6204820,30718825,2591312,-10617028,12192840,18873298,-7297090,-32297756,15221632 }, + { -26478122,-11103864,11546244,-1852483,9180880,7656409,-21343950,2095755,29769758,6593415 }, + { -31994208,-2907461,4176912,3264766,12538965,-868111,26312345,-6118678,30958054,8292160 }, + }, + { + { 31429822,-13959116,29173532,15632448,12174511,-2760094,32808831,3977186,26143136,-3148876 }, + { 22648901,1402143,-22799984,13746059,7936347,365344,-8668633,-1674433,-3758243,-2304625 }, + { -15491917,8012313,-2514730,-12702462,-23965846,-10254029,-1612713,-1535569,-16664475,8194478 }, + }, + { + { 27338066,-7507420,-7414224,10140405,-19026427,-6589889,27277191,8855376,28572286,3005164 }, + { 26287124,4821776,25476601,-4145903,-3764513,-15788984,-18008582,1182479,-26094821,-13079595 }, + { -7171154,3178080,23970071,6201893,-17195577,-4489192,-21876275,-13982627,32208683,-1198248 }, + }, + { + { -16657702,2817643,-10286362,14811298,6024667,13349505,-27315504,-10497842,-27672585,-11539858 }, + { 15941029,-9405932,-21367050,8062055,31876073,-238629,-15278393,-1444429,15397331,-4130193 }, + { 8934485,-13485467,-23286397,-13423241,-32446090,14047986,31170398,-1441021,-27505566,15087184 }, + }, + { + { -18357243,-2156491,24524913,-16677868,15520427,-6360776,-15502406,11461896,16788528,-5868942 }, + { -1947386,16013773,21750665,3714552,-17401782,-16055433,-3770287,-10323320,31322514,-11615635 }, + { 21426655,-5650218,-13648287,-5347537,-28812189,-4920970,-18275391,-14621414,13040862,-12112948 }, + }, + { + { 11293895,12478086,-27136401,15083750,-29307421,14748872,14555558,-13417103,1613711,4896935 }, + { -25894883,15323294,-8489791,-8057900,25967126,-13425460,2825960,-4897045,-23971776,-11267415 }, + { -15924766,-5229880,-17443532,6410664,3622847,10243618,20615400,12405433,-23753030,-8436416 }, + }, + { + { -7091295,12556208,-20191352,9025187,-17072479,4333801,4378436,2432030,23097949,-566018 }, + { 4565804,-16025654,20084412,-7842817,1724999,189254,24767264,10103221,-18512313,2424778 }, + { 366633,-11976806,8173090,-6890119,30788634,5745705,-7168678,1344109,-3642553,12412659 }, + }, + { + { -24001791,7690286,14929416,-168257,-32210835,-13412986,24162697,-15326504,-3141501,11179385 }, + { 18289522,-14724954,8056945,16430056,-21729724,7842514,-6001441,-1486897,-18684645,-11443503 }, + { 476239,6601091,-6152790,-9723375,17503545,-4863900,27672959,13403813,11052904,5219329 }, + }, +}, +{ + { + { 20678546,-8375738,-32671898,8849123,-5009758,14574752,31186971,-3973730,9014762,-8579056 }, + { -13644050,-10350239,-15962508,5075808,-1514661,-11534600,-33102500,9160280,8473550,-3256838 }, + { 24900749,14435722,17209120,-15292541,-22592275,9878983,-7689309,-16335821,-24568481,11788948 }, + }, + { + { -3118155,-11395194,-13802089,14797441,9652448,-6845904,-20037437,10410733,-24568470,-1458691 }, + { -15659161,16736706,-22467150,10215878,-9097177,7563911,11871841,-12505194,-18513325,8464118 }, + { -23400612,8348507,-14585951,-861714,-3950205,-6373419,14325289,8628612,33313881,-8370517 }, + }, + { + { -20186973,-4967935,22367356,5271547,-1097117,-4788838,-24805667,-10236854,-8940735,-5818269 }, + { -6948785,-1795212,-32625683,-16021179,32635414,-7374245,15989197,-12838188,28358192,-4253904 }, + { -23561781,-2799059,-32351682,-1661963,-9147719,10429267,-16637684,4072016,-5351664,5596589 }, + }, + { + { -28236598,-3390048,12312896,6213178,3117142,16078565,29266239,2557221,1768301,15373193 }, + { -7243358,-3246960,-4593467,-7553353,-127927,-912245,-1090902,-4504991,-24660491,3442910 }, + { -30210571,5124043,14181784,8197961,18964734,-11939093,22597931,7176455,-18585478,13365930 }, + }, + { + { -7877390,-1499958,8324673,4690079,6261860,890446,24538107,-8570186,-9689599,-3031667 }, + { 25008904,-10771599,-4305031,-9638010,16265036,15721635,683793,-11823784,15723479,-15163481 }, + { -9660625,12374379,-27006999,-7026148,-7724114,-12314514,11879682,5400171,519526,-1235876 }, + }, + { + { 22258397,-16332233,-7869817,14613016,-22520255,-2950923,-20353881,7315967,16648397,7605640 }, + { -8081308,-8464597,-8223311,9719710,19259459,-15348212,23994942,-5281555,-9468848,4763278 }, + { -21699244,9220969,-15730624,1084137,-25476107,-2852390,31088447,-7764523,-11356529,728112 }, + }, + { + { 26047220,-11751471,-6900323,-16521798,24092068,9158119,-4273545,-12555558,-29365436,-5498272 }, + { 17510331,-322857,5854289,8403524,17133918,-3112612,-28111007,12327945,10750447,10014012 }, + { -10312768,3936952,9156313,-8897683,16498692,-994647,-27481051,-666732,3424691,7540221 }, + }, + { + { 30322361,-6964110,11361005,-4143317,7433304,4989748,-7071422,-16317219,-9244265,15258046 }, + { 13054562,-2779497,19155474,469045,-12482797,4566042,5631406,2711395,1062915,-5136345 }, + { -19240248,-11254599,-29509029,-7499965,-5835763,13005411,-6066489,12194497,32960380,1459310 }, + }, +}, +{ + { + { 19852034,7027924,23669353,10020366,8586503,-6657907,394197,-6101885,18638003,-11174937 }, + { 31395534,15098109,26581030,8030562,-16527914,-5007134,9012486,-7584354,-6643087,-5442636 }, + { -9192165,-2347377,-1997099,4529534,25766844,607986,-13222,9677543,-32294889,-6456008 }, + }, + { + { -2444496,-149937,29348902,8186665,1873760,12489863,-30934579,-7839692,-7852844,-8138429 }, + { -15236356,-15433509,7766470,746860,26346930,-10221762,-27333451,10754588,-9431476,5203576 }, + { 31834314,14135496,-770007,5159118,20917671,-16768096,-7467973,-7337524,31809243,7347066 }, + }, + { + { -9606723,-11874240,20414459,13033986,13716524,-11691881,19797970,-12211255,15192876,-2087490 }, + { -12663563,-2181719,1168162,-3804809,26747877,-14138091,10609330,12694420,33473243,-13382104 }, + { 33184999,11180355,15832085,-11385430,-1633671,225884,15089336,-11023903,-6135662,14480053 }, + }, + { + { 31308717,-5619998,31030840,-1897099,15674547,-6582883,5496208,13685227,27595050,8737275 }, + { -20318852,-15150239,10933843,-16178022,8335352,-7546022,-31008351,-12610604,26498114,66511 }, + { 22644454,-8761729,-16671776,4884562,-3105614,-13559366,30540766,-4286747,-13327787,-7515095 }, + }, + { + { -28017847,9834845,18617207,-2681312,-3401956,-13307506,8205540,13585437,-17127465,15115439 }, + { 23711543,-672915,31206561,-8362711,6164647,-9709987,-33535882,-1426096,8236921,16492939 }, + { -23910559,-13515526,-26299483,-4503841,25005590,-7687270,19574902,10071562,6708380,-6222424 }, + }, + { + { 2101391,-4930054,19702731,2367575,-15427167,1047675,5301017,9328700,29955601,-11678310 }, + { 3096359,9271816,-21620864,-15521844,-14847996,-7592937,-25892142,-12635595,-9917575,6216608 }, + { -32615849,338663,-25195611,2510422,-29213566,-13820213,24822830,-6146567,-26767480,7525079 }, + }, + { + { -23066649,-13985623,16133487,-7896178,-3389565,778788,-910336,-2782495,-19386633,11994101 }, + { 21691500,-13624626,-641331,-14367021,3285881,-3483596,-25064666,9718258,-7477437,13381418 }, + { 18445390,-4202236,14979846,11622458,-1727110,-3582980,23111648,-6375247,28535282,15779576 }, + }, + { + { 30098053,3089662,-9234387,16662135,-21306940,11308411,-14068454,12021730,9955285,-16303356 }, + { 9734894,-14576830,-7473633,-9138735,2060392,11313496,-18426029,9924399,20194861,13380996 }, + { -26378102,-7965207,-22167821,15789297,-18055342,-6168792,-1984914,15707771,26342023,10146099 }, + }, +}, +{ + { + { -26016874,-219943,21339191,-41388,19745256,-2878700,-29637280,2227040,21612326,-545728 }, + { -13077387,1184228,23562814,-5970442,-20351244,-6348714,25764461,12243797,-20856566,11649658 }, + { -10031494,11262626,27384172,2271902,26947504,-15997771,39944,6114064,33514190,2333242 }, + }, + { + { -21433588,-12421821,8119782,7219913,-21830522,-9016134,-6679750,-12670638,24350578,-13450001 }, + { -4116307,-11271533,-23886186,4843615,-30088339,690623,-31536088,-10406836,8317860,12352766 }, + { 18200138,-14475911,-33087759,-2696619,-23702521,-9102511,-23552096,-2287550,20712163,6719373 }, + }, + { + { 26656208,6075253,-7858556,1886072,-28344043,4262326,11117530,-3763210,26224235,-3297458 }, + { -17168938,-14854097,-3395676,-16369877,-19954045,14050420,21728352,9493610,18620611,-16428628 }, + { -13323321,13325349,11432106,5964811,18609221,6062965,-5269471,-9725556,-30701573,-16479657 }, + }, + { + { -23860538,-11233159,26961357,1640861,-32413112,-16737940,12248509,-5240639,13735342,1934062 }, + { 25089769,6742589,17081145,-13406266,21909293,-16067981,-15136294,-3765346,-21277997,5473616 }, + { 31883677,-7961101,1083432,-11572403,22828471,13290673,-7125085,12469656,29111212,-5451014 }, + }, + { + { 24244947,-15050407,-26262976,2791540,-14997599,16666678,24367466,6388839,-10295587,452383 }, + { -25640782,-3417841,5217916,16224624,19987036,-4082269,-24236251,-5915248,15766062,8407814 }, + { -20406999,13990231,15495425,16395525,5377168,15166495,-8917023,-4388953,-8067909,2276718 }, + }, + { + { 30157918,12924066,-17712050,9245753,19895028,3368142,-23827587,5096219,22740376,-7303417 }, + { 2041139,-14256350,7783687,13876377,-25946985,-13352459,24051124,13742383,-15637599,13295222 }, + { 33338237,-8505733,12532113,7977527,9106186,-1715251,-17720195,-4612972,-4451357,-14669444 }, + }, + { + { -20045281,5454097,-14346548,6447146,28862071,1883651,-2469266,-4141880,7770569,9620597 }, + { 23208068,7979712,33071466,8149229,1758231,-10834995,30945528,-1694323,-33502340,-14767970 }, + { 1439958,-16270480,-1079989,-793782,4625402,10647766,-5043801,1220118,30494170,-11440799 }, + }, + { + { -5037580,-13028295,-2970559,-3061767,15640974,-6701666,-26739026,926050,-1684339,-13333647 }, + { 13908495,-3549272,30919928,-6273825,-21521863,7989039,9021034,9078865,3353509,4033511 }, + { -29663431,-15113610,32259991,-344482,24295849,-12912123,23161163,8839127,27485041,7356032 }, + }, +}, +{ + { + { 9661027,705443,11980065,-5370154,-1628543,14661173,-6346142,2625015,28431036,-16771834 }, + { -23839233,-8311415,-25945511,7480958,-17681669,-8354183,-22545972,14150565,15970762,4099461 }, + { 29262576,16756590,26350592,-8793563,8529671,-11208050,13617293,-9937143,11465739,8317062 }, + }, + { + { -25493081,-6962928,32500200,-9419051,-23038724,-2302222,14898637,3848455,20969334,-5157516 }, + { -20384450,-14347713,-18336405,13884722,-33039454,2842114,-21610826,-3649888,11177095,14989547 }, + { -24496721,-11716016,16959896,2278463,12066309,10137771,13515641,2581286,-28487508,9930240 }, + }, + { + { -17751622,-2097826,16544300,-13009300,-15914807,-14949081,18345767,-13403753,16291481,-5314038 }, + { -33229194,2553288,32678213,9875984,8534129,6889387,-9676774,6957617,4368891,9788741 }, + { 16660756,7281060,-10830758,12911820,20108584,-8101676,-21722536,-8613148,16250552,-11111103 }, + }, + { + { -19765507,2390526,-16551031,14161980,1905286,6414907,4689584,10604807,-30190403,4782747 }, + { -1354539,14736941,-7367442,-13292886,7710542,-14155590,-9981571,4383045,22546403,437323 }, + { 31665577,-12180464,-16186830,1491339,-18368625,3294682,27343084,2786261,-30633590,-14097016 }, + }, + { + { -14467279,-683715,-33374107,7448552,19294360,14334329,-19690631,2355319,-19284671,-6114373 }, + { 15121312,-15796162,6377020,-6031361,-10798111,-12957845,18952177,15496498,-29380133,11754228 }, + { -2637277,-13483075,8488727,-14303896,12728761,-1622493,7141596,11724556,22761615,-10134141 }, + }, + { + { 16918416,11729663,-18083579,3022987,-31015732,-13339659,-28741185,-12227393,32851222,11717399 }, + { 11166634,7338049,-6722523,4531520,-29468672,-7302055,31474879,3483633,-1193175,-4030831 }, + { -185635,9921305,31456609,-13536438,-12013818,13348923,33142652,6546660,-19985279,-3948376 }, + }, + { + { -32460596,11266712,-11197107,-7899103,31703694,3855903,-8537131,-12833048,-30772034,-15486313 }, + { -18006477,12709068,3991746,-6479188,-21491523,-10550425,-31135347,-16049879,10928917,3011958 }, + { -6957757,-15594337,31696059,334240,29576716,14796075,-30831056,-12805180,18008031,10258577 }, + }, + { + { -22448644,15655569,7018479,-4410003,-30314266,-1201591,-1853465,1367120,25127874,6671743 }, + { 29701166,-14373934,-10878120,9279288,-17568,13127210,21382910,11042292,25838796,4642684 }, + { -20430234,14955537,-24126347,8124619,-5369288,-5990470,30468147,-13900640,18423289,4177476 }, + }, +}, diff --git a/libs/libaxolotl/src/curve25519/ed25519/base2.h b/libs/libaxolotl/src/curve25519/ed25519/base2.h new file mode 100644 index 0000000000..8c538440ff --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/base2.h @@ -0,0 +1,40 @@ + { + { 25967493,-14356035,29566456,3660896,-12694345,4014787,27544626,-11754271,-6079156,2047605 }, + { -12545711,934262,-2722910,3049990,-727428,9406986,12720692,5043384,19500929,-15469378 }, + { -8738181,4489570,9688441,-14785194,10184609,-12363380,29287919,11864899,-24514362,-4438546 }, + }, + { + { 15636291,-9688557,24204773,-7912398,616977,-16685262,27787600,-14772189,28944400,-1550024 }, + { 16568933,4717097,-11556148,-1102322,15682896,-11807043,16354577,-11775962,7689662,11199574 }, + { 30464156,-5976125,-11779434,-15670865,23220365,15915852,7512774,10017326,-17749093,-9920357 }, + }, + { + { 10861363,11473154,27284546,1981175,-30064349,12577861,32867885,14515107,-15438304,10819380 }, + { 4708026,6336745,20377586,9066809,-11272109,6594696,-25653668,12483688,-12668491,5581306 }, + { 19563160,16186464,-29386857,4097519,10237984,-4348115,28542350,13850243,-23678021,-15815942 }, + }, + { + { 5153746,9909285,1723747,-2777874,30523605,5516873,19480852,5230134,-23952439,-15175766 }, + { -30269007,-3463509,7665486,10083793,28475525,1649722,20654025,16520125,30598449,7715701 }, + { 28881845,14381568,9657904,3680757,-20181635,7843316,-31400660,1370708,29794553,-1409300 }, + }, + { + { -22518993,-6692182,14201702,-8745502,-23510406,8844726,18474211,-1361450,-13062696,13821877 }, + { -6455177,-7839871,3374702,-4740862,-27098617,-10571707,31655028,-7212327,18853322,-14220951 }, + { 4566830,-12963868,-28974889,-12240689,-7602672,-2830569,-8514358,-10431137,2207753,-3209784 }, + }, + { + { -25154831,-4185821,29681144,7868801,-6854661,-9423865,-12437364,-663000,-31111463,-16132436 }, + { 25576264,-2703214,7349804,-11814844,16472782,9300885,3844789,15725684,171356,6466918 }, + { 23103977,13316479,9739013,-16149481,817875,-15038942,8965339,-14088058,-30714912,16193877 }, + }, + { + { -33521811,3180713,-2394130,14003687,-16903474,-16270840,17238398,4729455,-18074513,9256800 }, + { -25182317,-4174131,32336398,5036987,-21236817,11360617,22616405,9761698,-19827198,630305 }, + { -13720693,2639453,-24237460,-7406481,9494427,-5774029,-6554551,-15960994,-2449256,-14291300 }, + }, + { + { -3151181,-5046075,9282714,6866145,-31907062,-863023,-18940575,15033784,25105118,-7894876 }, + { -24326370,15950226,-31801215,-14592823,-11662737,-5090925,1573892,-2625887,2198790,-15804619 }, + { -3099351,10324967,-2241613,7453183,-5446979,-2735503,-13812022,-16236442,-32461234,-12290683 }, + }, diff --git a/libs/libaxolotl/src/curve25519/ed25519/d.h b/libs/libaxolotl/src/curve25519/ed25519/d.h new file mode 100644 index 0000000000..e25f578350 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/d.h @@ -0,0 +1 @@ +-10913610,13857413,-15372611,6949391,114729,-8787816,-6275908,-3247719,-18696448,-12055116 diff --git a/libs/libaxolotl/src/curve25519/ed25519/d2.h b/libs/libaxolotl/src/curve25519/ed25519/d2.h new file mode 100644 index 0000000000..01aaec7512 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/d2.h @@ -0,0 +1 @@ +-21827239,-5839606,-30745221,13898782,229458,15978800,-12551817,-6495438,29715968,9444199 diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe.h b/libs/libaxolotl/src/curve25519/ed25519/fe.h new file mode 100644 index 0000000000..60c308ba46 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe.h @@ -0,0 +1,56 @@ +#ifndef FE_H +#define FE_H + +#include "crypto_int32.h" + +typedef crypto_int32 fe[10]; + +/* +fe means field element. +Here the field is \Z/(2^255-19). +An element t, entries t[0]...t[9], represents the integer +t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9]. +Bounds on each t[i] vary depending on context. +*/ + +#define fe_frombytes crypto_sign_ed25519_ref10_fe_frombytes +#define fe_tobytes crypto_sign_ed25519_ref10_fe_tobytes +#define fe_copy crypto_sign_ed25519_ref10_fe_copy +#define fe_isnonzero crypto_sign_ed25519_ref10_fe_isnonzero +#define fe_isnegative crypto_sign_ed25519_ref10_fe_isnegative +#define fe_0 crypto_sign_ed25519_ref10_fe_0 +#define fe_1 crypto_sign_ed25519_ref10_fe_1 +#define fe_cswap crypto_sign_ed25519_ref10_fe_cswap +#define fe_cmov crypto_sign_ed25519_ref10_fe_cmov +#define fe_add crypto_sign_ed25519_ref10_fe_add +#define fe_sub crypto_sign_ed25519_ref10_fe_sub +#define fe_neg crypto_sign_ed25519_ref10_fe_neg +#define fe_mul crypto_sign_ed25519_ref10_fe_mul +#define fe_sq crypto_sign_ed25519_ref10_fe_sq +#define fe_sq2 crypto_sign_ed25519_ref10_fe_sq2 +#define fe_mul121666 crypto_sign_ed25519_ref10_fe_mul121666 +#define fe_invert crypto_sign_ed25519_ref10_fe_invert +#define fe_pow22523 crypto_sign_ed25519_ref10_fe_pow22523 + +extern void fe_frombytes(fe,const unsigned char *); +extern void fe_tobytes(unsigned char *,const fe); + +extern void fe_copy(fe,const fe); +extern int fe_isnonzero(const fe); +extern int fe_isnegative(const fe); +extern void fe_0(fe); +extern void fe_1(fe); +extern void fe_cswap(fe,fe,unsigned int); +extern void fe_cmov(fe,const fe,unsigned int); + +extern void fe_add(fe,const fe,const fe); +extern void fe_sub(fe,const fe,const fe); +extern void fe_neg(fe,const fe); +extern void fe_mul(fe,const fe,const fe); +extern void fe_sq(fe,const fe); +extern void fe_sq2(fe,const fe); +extern void fe_mul121666(fe,const fe); +extern void fe_invert(fe,const fe); +extern void fe_pow22523(fe,const fe); + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_0.c b/libs/libaxolotl/src/curve25519/ed25519/fe_0.c new file mode 100644 index 0000000000..ec879d7337 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_0.c @@ -0,0 +1,19 @@ +#include "fe.h" + +/* +h = 0 +*/ + +void fe_0(fe h) +{ + h[0] = 0; + h[1] = 0; + h[2] = 0; + h[3] = 0; + h[4] = 0; + h[5] = 0; + h[6] = 0; + h[7] = 0; + h[8] = 0; + h[9] = 0; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_1.c b/libs/libaxolotl/src/curve25519/ed25519/fe_1.c new file mode 100644 index 0000000000..8cf7784844 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_1.c @@ -0,0 +1,19 @@ +#include "fe.h" + +/* +h = 1 +*/ + +void fe_1(fe h) +{ + h[0] = 1; + h[1] = 0; + h[2] = 0; + h[3] = 0; + h[4] = 0; + h[5] = 0; + h[6] = 0; + h[7] = 0; + h[8] = 0; + h[9] = 0; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_add.c b/libs/libaxolotl/src/curve25519/ed25519/fe_add.c new file mode 100644 index 0000000000..e6a81da202 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_add.c @@ -0,0 +1,57 @@ +#include "fe.h" + +/* +h = f + g +Can overlap h with f or g. + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +void fe_add(fe h,const fe f,const fe g) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + crypto_int32 g0 = g[0]; + crypto_int32 g1 = g[1]; + crypto_int32 g2 = g[2]; + crypto_int32 g3 = g[3]; + crypto_int32 g4 = g[4]; + crypto_int32 g5 = g[5]; + crypto_int32 g6 = g[6]; + crypto_int32 g7 = g[7]; + crypto_int32 g8 = g[8]; + crypto_int32 g9 = g[9]; + crypto_int32 h0 = f0 + g0; + crypto_int32 h1 = f1 + g1; + crypto_int32 h2 = f2 + g2; + crypto_int32 h3 = f3 + g3; + crypto_int32 h4 = f4 + g4; + crypto_int32 h5 = f5 + g5; + crypto_int32 h6 = f6 + g6; + crypto_int32 h7 = f7 + g7; + crypto_int32 h8 = f8 + g8; + crypto_int32 h9 = f9 + g9; + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_cmov.c b/libs/libaxolotl/src/curve25519/ed25519/fe_cmov.c new file mode 100644 index 0000000000..8ca584fb19 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_cmov.c @@ -0,0 +1,63 @@ +#include "fe.h" + +/* +Replace (f,g) with (g,g) if b == 1; +replace (f,g) with (f,g) if b == 0. + +Preconditions: b in {0,1}. +*/ + +void fe_cmov(fe f,const fe g,unsigned int b) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + crypto_int32 g0 = g[0]; + crypto_int32 g1 = g[1]; + crypto_int32 g2 = g[2]; + crypto_int32 g3 = g[3]; + crypto_int32 g4 = g[4]; + crypto_int32 g5 = g[5]; + crypto_int32 g6 = g[6]; + crypto_int32 g7 = g[7]; + crypto_int32 g8 = g[8]; + crypto_int32 g9 = g[9]; + crypto_int32 x0 = f0 ^ g0; + crypto_int32 x1 = f1 ^ g1; + crypto_int32 x2 = f2 ^ g2; + crypto_int32 x3 = f3 ^ g3; + crypto_int32 x4 = f4 ^ g4; + crypto_int32 x5 = f5 ^ g5; + crypto_int32 x6 = f6 ^ g6; + crypto_int32 x7 = f7 ^ g7; + crypto_int32 x8 = f8 ^ g8; + crypto_int32 x9 = f9 ^ g9; + b = -b; + x0 &= b; + x1 &= b; + x2 &= b; + x3 &= b; + x4 &= b; + x5 &= b; + x6 &= b; + x7 &= b; + x8 &= b; + x9 &= b; + f[0] = f0 ^ x0; + f[1] = f1 ^ x1; + f[2] = f2 ^ x2; + f[3] = f3 ^ x3; + f[4] = f4 ^ x4; + f[5] = f5 ^ x5; + f[6] = f6 ^ x6; + f[7] = f7 ^ x7; + f[8] = f8 ^ x8; + f[9] = f9 ^ x9; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_copy.c b/libs/libaxolotl/src/curve25519/ed25519/fe_copy.c new file mode 100644 index 0000000000..9c5bf865a2 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_copy.c @@ -0,0 +1,29 @@ +#include "fe.h" + +/* +h = f +*/ + +void fe_copy(fe h,const fe f) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + h[0] = f0; + h[1] = f1; + h[2] = f2; + h[3] = f3; + h[4] = f4; + h[5] = f5; + h[6] = f6; + h[7] = f7; + h[8] = f8; + h[9] = f9; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_frombytes.c b/libs/libaxolotl/src/curve25519/ed25519/fe_frombytes.c new file mode 100644 index 0000000000..5c17917487 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_frombytes.c @@ -0,0 +1,73 @@ +#include "fe.h" +#include "crypto_int64.h" +#include "crypto_uint64.h" + +static crypto_uint64 load_3(const unsigned char *in) +{ + crypto_uint64 result; + result = (crypto_uint64) in[0]; + result |= ((crypto_uint64) in[1]) << 8; + result |= ((crypto_uint64) in[2]) << 16; + return result; +} + +static crypto_uint64 load_4(const unsigned char *in) +{ + crypto_uint64 result; + result = (crypto_uint64) in[0]; + result |= ((crypto_uint64) in[1]) << 8; + result |= ((crypto_uint64) in[2]) << 16; + result |= ((crypto_uint64) in[3]) << 24; + return result; +} + +/* +Ignores top bit of h. +*/ + +void fe_frombytes(fe h,const unsigned char *s) +{ + crypto_int64 h0 = load_4(s); + crypto_int64 h1 = load_3(s + 4) << 6; + crypto_int64 h2 = load_3(s + 7) << 5; + crypto_int64 h3 = load_3(s + 10) << 3; + crypto_int64 h4 = load_3(s + 13) << 2; + crypto_int64 h5 = load_4(s + 16); + crypto_int64 h6 = load_3(s + 20) << 7; + crypto_int64 h7 = load_3(s + 23) << 5; + crypto_int64 h8 = load_3(s + 26) << 4; + crypto_int64 h9 = (load_3(s + 29) & 8388607) << 2; + crypto_int64 carry0; + crypto_int64 carry1; + crypto_int64 carry2; + crypto_int64 carry3; + crypto_int64 carry4; + crypto_int64 carry5; + crypto_int64 carry6; + crypto_int64 carry7; + crypto_int64 carry8; + crypto_int64 carry9; + + carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_invert.c b/libs/libaxolotl/src/curve25519/ed25519/fe_invert.c new file mode 100644 index 0000000000..bcfdb8ff87 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_invert.c @@ -0,0 +1,14 @@ +#include "fe.h" + +void fe_invert(fe out,const fe z) +{ + fe t0; + fe t1; + fe t2; + fe t3; + int i; + +#include "pow225521.h" + + return; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_isnegative.c b/libs/libaxolotl/src/curve25519/ed25519/fe_isnegative.c new file mode 100644 index 0000000000..3b2c8b8d52 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_isnegative.c @@ -0,0 +1,16 @@ +#include "fe.h" + +/* +return 1 if f is in {1,3,5,...,q-2} +return 0 if f is in {0,2,4,...,q-1} + +Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +int fe_isnegative(const fe f) +{ + unsigned char s[32]; + fe_tobytes(s,f); + return s[0] & 1; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_isnonzero.c b/libs/libaxolotl/src/curve25519/ed25519/fe_isnonzero.c new file mode 100644 index 0000000000..47568001ce --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_isnonzero.c @@ -0,0 +1,19 @@ +#include "fe.h" +#include "crypto_verify_32.h" + +/* +return 1 if f == 0 +return 0 if f != 0 + +Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +static const unsigned char zero[32]; + +int fe_isnonzero(const fe f) +{ + unsigned char s[32]; + fe_tobytes(s,f); + return crypto_verify_32(s,zero); +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_mul.c b/libs/libaxolotl/src/curve25519/ed25519/fe_mul.c new file mode 100644 index 0000000000..26ca8b3682 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_mul.c @@ -0,0 +1,253 @@ +#include "fe.h" +#include "crypto_int64.h" + +/* +h = f * g +Can overlap h with f or g. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + |g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +Notes on implementation strategy: + +Using schoolbook multiplication. +Karatsuba would save a little in some cost models. + +Most multiplications by 2 and 19 are 32-bit precomputations; +cheaper than 64-bit postcomputations. + +There is one remaining multiplication by 19 in the carry chain; +one *19 precomputation can be merged into this, +but the resulting data flow is considerably less clean. + +There are 12 carries below. +10 of them are 2-way parallelizable and vectorizable. +Can get away with 11 carries, but then data flow is much deeper. + +With tighter constraints on inputs can squeeze carries into int32. +*/ + +void fe_mul(fe h,const fe f,const fe g) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + crypto_int32 g0 = g[0]; + crypto_int32 g1 = g[1]; + crypto_int32 g2 = g[2]; + crypto_int32 g3 = g[3]; + crypto_int32 g4 = g[4]; + crypto_int32 g5 = g[5]; + crypto_int32 g6 = g[6]; + crypto_int32 g7 = g[7]; + crypto_int32 g8 = g[8]; + crypto_int32 g9 = g[9]; + crypto_int32 g1_19 = 19 * g1; /* 1.959375*2^29 */ + crypto_int32 g2_19 = 19 * g2; /* 1.959375*2^30; still ok */ + crypto_int32 g3_19 = 19 * g3; + crypto_int32 g4_19 = 19 * g4; + crypto_int32 g5_19 = 19 * g5; + crypto_int32 g6_19 = 19 * g6; + crypto_int32 g7_19 = 19 * g7; + crypto_int32 g8_19 = 19 * g8; + crypto_int32 g9_19 = 19 * g9; + crypto_int32 f1_2 = 2 * f1; + crypto_int32 f3_2 = 2 * f3; + crypto_int32 f5_2 = 2 * f5; + crypto_int32 f7_2 = 2 * f7; + crypto_int32 f9_2 = 2 * f9; + crypto_int64 f0g0 = f0 * (crypto_int64) g0; + crypto_int64 f0g1 = f0 * (crypto_int64) g1; + crypto_int64 f0g2 = f0 * (crypto_int64) g2; + crypto_int64 f0g3 = f0 * (crypto_int64) g3; + crypto_int64 f0g4 = f0 * (crypto_int64) g4; + crypto_int64 f0g5 = f0 * (crypto_int64) g5; + crypto_int64 f0g6 = f0 * (crypto_int64) g6; + crypto_int64 f0g7 = f0 * (crypto_int64) g7; + crypto_int64 f0g8 = f0 * (crypto_int64) g8; + crypto_int64 f0g9 = f0 * (crypto_int64) g9; + crypto_int64 f1g0 = f1 * (crypto_int64) g0; + crypto_int64 f1g1_2 = f1_2 * (crypto_int64) g1; + crypto_int64 f1g2 = f1 * (crypto_int64) g2; + crypto_int64 f1g3_2 = f1_2 * (crypto_int64) g3; + crypto_int64 f1g4 = f1 * (crypto_int64) g4; + crypto_int64 f1g5_2 = f1_2 * (crypto_int64) g5; + crypto_int64 f1g6 = f1 * (crypto_int64) g6; + crypto_int64 f1g7_2 = f1_2 * (crypto_int64) g7; + crypto_int64 f1g8 = f1 * (crypto_int64) g8; + crypto_int64 f1g9_38 = f1_2 * (crypto_int64) g9_19; + crypto_int64 f2g0 = f2 * (crypto_int64) g0; + crypto_int64 f2g1 = f2 * (crypto_int64) g1; + crypto_int64 f2g2 = f2 * (crypto_int64) g2; + crypto_int64 f2g3 = f2 * (crypto_int64) g3; + crypto_int64 f2g4 = f2 * (crypto_int64) g4; + crypto_int64 f2g5 = f2 * (crypto_int64) g5; + crypto_int64 f2g6 = f2 * (crypto_int64) g6; + crypto_int64 f2g7 = f2 * (crypto_int64) g7; + crypto_int64 f2g8_19 = f2 * (crypto_int64) g8_19; + crypto_int64 f2g9_19 = f2 * (crypto_int64) g9_19; + crypto_int64 f3g0 = f3 * (crypto_int64) g0; + crypto_int64 f3g1_2 = f3_2 * (crypto_int64) g1; + crypto_int64 f3g2 = f3 * (crypto_int64) g2; + crypto_int64 f3g3_2 = f3_2 * (crypto_int64) g3; + crypto_int64 f3g4 = f3 * (crypto_int64) g4; + crypto_int64 f3g5_2 = f3_2 * (crypto_int64) g5; + crypto_int64 f3g6 = f3 * (crypto_int64) g6; + crypto_int64 f3g7_38 = f3_2 * (crypto_int64) g7_19; + crypto_int64 f3g8_19 = f3 * (crypto_int64) g8_19; + crypto_int64 f3g9_38 = f3_2 * (crypto_int64) g9_19; + crypto_int64 f4g0 = f4 * (crypto_int64) g0; + crypto_int64 f4g1 = f4 * (crypto_int64) g1; + crypto_int64 f4g2 = f4 * (crypto_int64) g2; + crypto_int64 f4g3 = f4 * (crypto_int64) g3; + crypto_int64 f4g4 = f4 * (crypto_int64) g4; + crypto_int64 f4g5 = f4 * (crypto_int64) g5; + crypto_int64 f4g6_19 = f4 * (crypto_int64) g6_19; + crypto_int64 f4g7_19 = f4 * (crypto_int64) g7_19; + crypto_int64 f4g8_19 = f4 * (crypto_int64) g8_19; + crypto_int64 f4g9_19 = f4 * (crypto_int64) g9_19; + crypto_int64 f5g0 = f5 * (crypto_int64) g0; + crypto_int64 f5g1_2 = f5_2 * (crypto_int64) g1; + crypto_int64 f5g2 = f5 * (crypto_int64) g2; + crypto_int64 f5g3_2 = f5_2 * (crypto_int64) g3; + crypto_int64 f5g4 = f5 * (crypto_int64) g4; + crypto_int64 f5g5_38 = f5_2 * (crypto_int64) g5_19; + crypto_int64 f5g6_19 = f5 * (crypto_int64) g6_19; + crypto_int64 f5g7_38 = f5_2 * (crypto_int64) g7_19; + crypto_int64 f5g8_19 = f5 * (crypto_int64) g8_19; + crypto_int64 f5g9_38 = f5_2 * (crypto_int64) g9_19; + crypto_int64 f6g0 = f6 * (crypto_int64) g0; + crypto_int64 f6g1 = f6 * (crypto_int64) g1; + crypto_int64 f6g2 = f6 * (crypto_int64) g2; + crypto_int64 f6g3 = f6 * (crypto_int64) g3; + crypto_int64 f6g4_19 = f6 * (crypto_int64) g4_19; + crypto_int64 f6g5_19 = f6 * (crypto_int64) g5_19; + crypto_int64 f6g6_19 = f6 * (crypto_int64) g6_19; + crypto_int64 f6g7_19 = f6 * (crypto_int64) g7_19; + crypto_int64 f6g8_19 = f6 * (crypto_int64) g8_19; + crypto_int64 f6g9_19 = f6 * (crypto_int64) g9_19; + crypto_int64 f7g0 = f7 * (crypto_int64) g0; + crypto_int64 f7g1_2 = f7_2 * (crypto_int64) g1; + crypto_int64 f7g2 = f7 * (crypto_int64) g2; + crypto_int64 f7g3_38 = f7_2 * (crypto_int64) g3_19; + crypto_int64 f7g4_19 = f7 * (crypto_int64) g4_19; + crypto_int64 f7g5_38 = f7_2 * (crypto_int64) g5_19; + crypto_int64 f7g6_19 = f7 * (crypto_int64) g6_19; + crypto_int64 f7g7_38 = f7_2 * (crypto_int64) g7_19; + crypto_int64 f7g8_19 = f7 * (crypto_int64) g8_19; + crypto_int64 f7g9_38 = f7_2 * (crypto_int64) g9_19; + crypto_int64 f8g0 = f8 * (crypto_int64) g0; + crypto_int64 f8g1 = f8 * (crypto_int64) g1; + crypto_int64 f8g2_19 = f8 * (crypto_int64) g2_19; + crypto_int64 f8g3_19 = f8 * (crypto_int64) g3_19; + crypto_int64 f8g4_19 = f8 * (crypto_int64) g4_19; + crypto_int64 f8g5_19 = f8 * (crypto_int64) g5_19; + crypto_int64 f8g6_19 = f8 * (crypto_int64) g6_19; + crypto_int64 f8g7_19 = f8 * (crypto_int64) g7_19; + crypto_int64 f8g8_19 = f8 * (crypto_int64) g8_19; + crypto_int64 f8g9_19 = f8 * (crypto_int64) g9_19; + crypto_int64 f9g0 = f9 * (crypto_int64) g0; + crypto_int64 f9g1_38 = f9_2 * (crypto_int64) g1_19; + crypto_int64 f9g2_19 = f9 * (crypto_int64) g2_19; + crypto_int64 f9g3_38 = f9_2 * (crypto_int64) g3_19; + crypto_int64 f9g4_19 = f9 * (crypto_int64) g4_19; + crypto_int64 f9g5_38 = f9_2 * (crypto_int64) g5_19; + crypto_int64 f9g6_19 = f9 * (crypto_int64) g6_19; + crypto_int64 f9g7_38 = f9_2 * (crypto_int64) g7_19; + crypto_int64 f9g8_19 = f9 * (crypto_int64) g8_19; + crypto_int64 f9g9_38 = f9_2 * (crypto_int64) g9_19; + crypto_int64 h0 = f0g0+f1g9_38+f2g8_19+f3g7_38+f4g6_19+f5g5_38+f6g4_19+f7g3_38+f8g2_19+f9g1_38; + crypto_int64 h1 = f0g1+f1g0 +f2g9_19+f3g8_19+f4g7_19+f5g6_19+f6g5_19+f7g4_19+f8g3_19+f9g2_19; + crypto_int64 h2 = f0g2+f1g1_2 +f2g0 +f3g9_38+f4g8_19+f5g7_38+f6g6_19+f7g5_38+f8g4_19+f9g3_38; + crypto_int64 h3 = f0g3+f1g2 +f2g1 +f3g0 +f4g9_19+f5g8_19+f6g7_19+f7g6_19+f8g5_19+f9g4_19; + crypto_int64 h4 = f0g4+f1g3_2 +f2g2 +f3g1_2 +f4g0 +f5g9_38+f6g8_19+f7g7_38+f8g6_19+f9g5_38; + crypto_int64 h5 = f0g5+f1g4 +f2g3 +f3g2 +f4g1 +f5g0 +f6g9_19+f7g8_19+f8g7_19+f9g6_19; + crypto_int64 h6 = f0g6+f1g5_2 +f2g4 +f3g3_2 +f4g2 +f5g1_2 +f6g0 +f7g9_38+f8g8_19+f9g7_38; + crypto_int64 h7 = f0g7+f1g6 +f2g5 +f3g4 +f4g3 +f5g2 +f6g1 +f7g0 +f8g9_19+f9g8_19; + crypto_int64 h8 = f0g8+f1g7_2 +f2g6 +f3g5_2 +f4g4 +f5g3_2 +f6g2 +f7g1_2 +f8g0 +f9g9_38; + crypto_int64 h9 = f0g9+f1g8 +f2g7 +f3g6 +f4g5 +f5g4 +f6g3 +f7g2 +f8g1 +f9g0 ; + crypto_int64 carry0; + crypto_int64 carry1; + crypto_int64 carry2; + crypto_int64 carry3; + crypto_int64 carry4; + crypto_int64 carry5; + crypto_int64 carry6; + crypto_int64 carry7; + crypto_int64 carry8; + crypto_int64 carry9; + + /* + |h0| <= (1.65*1.65*2^52*(1+19+19+19+19)+1.65*1.65*2^50*(38+38+38+38+38)) + i.e. |h0| <= 1.4*2^60; narrower ranges for h2, h4, h6, h8 + |h1| <= (1.65*1.65*2^51*(1+1+19+19+19+19+19+19+19+19)) + i.e. |h1| <= 1.7*2^59; narrower ranges for h3, h5, h7, h9 + */ + + carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + /* |h0| <= 2^25 */ + /* |h4| <= 2^25 */ + /* |h1| <= 1.71*2^59 */ + /* |h5| <= 1.71*2^59 */ + + carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + /* |h1| <= 2^24; from now on fits into int32 */ + /* |h5| <= 2^24; from now on fits into int32 */ + /* |h2| <= 1.41*2^60 */ + /* |h6| <= 1.41*2^60 */ + + carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + /* |h2| <= 2^25; from now on fits into int32 unchanged */ + /* |h6| <= 2^25; from now on fits into int32 unchanged */ + /* |h3| <= 1.71*2^59 */ + /* |h7| <= 1.71*2^59 */ + + carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + /* |h3| <= 2^24; from now on fits into int32 unchanged */ + /* |h7| <= 2^24; from now on fits into int32 unchanged */ + /* |h4| <= 1.72*2^34 */ + /* |h8| <= 1.41*2^60 */ + + carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + /* |h4| <= 2^25; from now on fits into int32 unchanged */ + /* |h8| <= 2^25; from now on fits into int32 unchanged */ + /* |h5| <= 1.01*2^24 */ + /* |h9| <= 1.71*2^59 */ + + carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + /* |h9| <= 2^24; from now on fits into int32 unchanged */ + /* |h0| <= 1.1*2^39 */ + + carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + /* |h0| <= 2^25; from now on fits into int32 unchanged */ + /* |h1| <= 1.01*2^24 */ + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_neg.c b/libs/libaxolotl/src/curve25519/ed25519/fe_neg.c new file mode 100644 index 0000000000..2078ce5284 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_neg.c @@ -0,0 +1,45 @@ +#include "fe.h" + +/* +h = -f + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +*/ + +void fe_neg(fe h,const fe f) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + crypto_int32 h0 = -f0; + crypto_int32 h1 = -f1; + crypto_int32 h2 = -f2; + crypto_int32 h3 = -f3; + crypto_int32 h4 = -f4; + crypto_int32 h5 = -f5; + crypto_int32 h6 = -f6; + crypto_int32 h7 = -f7; + crypto_int32 h8 = -f8; + crypto_int32 h9 = -f9; + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_pow22523.c b/libs/libaxolotl/src/curve25519/ed25519/fe_pow22523.c new file mode 100644 index 0000000000..56675a5902 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_pow22523.c @@ -0,0 +1,13 @@ +#include "fe.h" + +void fe_pow22523(fe out,const fe z) +{ + fe t0; + fe t1; + fe t2; + int i; + +#include "pow22523.h" + + return; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_sq.c b/libs/libaxolotl/src/curve25519/ed25519/fe_sq.c new file mode 100644 index 0000000000..8dd119841c --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_sq.c @@ -0,0 +1,149 @@ +#include "fe.h" +#include "crypto_int64.h" + +/* +h = f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +See fe_mul.c for discussion of implementation strategy. +*/ + +void fe_sq(fe h,const fe f) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + crypto_int32 f0_2 = 2 * f0; + crypto_int32 f1_2 = 2 * f1; + crypto_int32 f2_2 = 2 * f2; + crypto_int32 f3_2 = 2 * f3; + crypto_int32 f4_2 = 2 * f4; + crypto_int32 f5_2 = 2 * f5; + crypto_int32 f6_2 = 2 * f6; + crypto_int32 f7_2 = 2 * f7; + crypto_int32 f5_38 = 38 * f5; /* 1.959375*2^30 */ + crypto_int32 f6_19 = 19 * f6; /* 1.959375*2^30 */ + crypto_int32 f7_38 = 38 * f7; /* 1.959375*2^30 */ + crypto_int32 f8_19 = 19 * f8; /* 1.959375*2^30 */ + crypto_int32 f9_38 = 38 * f9; /* 1.959375*2^30 */ + crypto_int64 f0f0 = f0 * (crypto_int64) f0; + crypto_int64 f0f1_2 = f0_2 * (crypto_int64) f1; + crypto_int64 f0f2_2 = f0_2 * (crypto_int64) f2; + crypto_int64 f0f3_2 = f0_2 * (crypto_int64) f3; + crypto_int64 f0f4_2 = f0_2 * (crypto_int64) f4; + crypto_int64 f0f5_2 = f0_2 * (crypto_int64) f5; + crypto_int64 f0f6_2 = f0_2 * (crypto_int64) f6; + crypto_int64 f0f7_2 = f0_2 * (crypto_int64) f7; + crypto_int64 f0f8_2 = f0_2 * (crypto_int64) f8; + crypto_int64 f0f9_2 = f0_2 * (crypto_int64) f9; + crypto_int64 f1f1_2 = f1_2 * (crypto_int64) f1; + crypto_int64 f1f2_2 = f1_2 * (crypto_int64) f2; + crypto_int64 f1f3_4 = f1_2 * (crypto_int64) f3_2; + crypto_int64 f1f4_2 = f1_2 * (crypto_int64) f4; + crypto_int64 f1f5_4 = f1_2 * (crypto_int64) f5_2; + crypto_int64 f1f6_2 = f1_2 * (crypto_int64) f6; + crypto_int64 f1f7_4 = f1_2 * (crypto_int64) f7_2; + crypto_int64 f1f8_2 = f1_2 * (crypto_int64) f8; + crypto_int64 f1f9_76 = f1_2 * (crypto_int64) f9_38; + crypto_int64 f2f2 = f2 * (crypto_int64) f2; + crypto_int64 f2f3_2 = f2_2 * (crypto_int64) f3; + crypto_int64 f2f4_2 = f2_2 * (crypto_int64) f4; + crypto_int64 f2f5_2 = f2_2 * (crypto_int64) f5; + crypto_int64 f2f6_2 = f2_2 * (crypto_int64) f6; + crypto_int64 f2f7_2 = f2_2 * (crypto_int64) f7; + crypto_int64 f2f8_38 = f2_2 * (crypto_int64) f8_19; + crypto_int64 f2f9_38 = f2 * (crypto_int64) f9_38; + crypto_int64 f3f3_2 = f3_2 * (crypto_int64) f3; + crypto_int64 f3f4_2 = f3_2 * (crypto_int64) f4; + crypto_int64 f3f5_4 = f3_2 * (crypto_int64) f5_2; + crypto_int64 f3f6_2 = f3_2 * (crypto_int64) f6; + crypto_int64 f3f7_76 = f3_2 * (crypto_int64) f7_38; + crypto_int64 f3f8_38 = f3_2 * (crypto_int64) f8_19; + crypto_int64 f3f9_76 = f3_2 * (crypto_int64) f9_38; + crypto_int64 f4f4 = f4 * (crypto_int64) f4; + crypto_int64 f4f5_2 = f4_2 * (crypto_int64) f5; + crypto_int64 f4f6_38 = f4_2 * (crypto_int64) f6_19; + crypto_int64 f4f7_38 = f4 * (crypto_int64) f7_38; + crypto_int64 f4f8_38 = f4_2 * (crypto_int64) f8_19; + crypto_int64 f4f9_38 = f4 * (crypto_int64) f9_38; + crypto_int64 f5f5_38 = f5 * (crypto_int64) f5_38; + crypto_int64 f5f6_38 = f5_2 * (crypto_int64) f6_19; + crypto_int64 f5f7_76 = f5_2 * (crypto_int64) f7_38; + crypto_int64 f5f8_38 = f5_2 * (crypto_int64) f8_19; + crypto_int64 f5f9_76 = f5_2 * (crypto_int64) f9_38; + crypto_int64 f6f6_19 = f6 * (crypto_int64) f6_19; + crypto_int64 f6f7_38 = f6 * (crypto_int64) f7_38; + crypto_int64 f6f8_38 = f6_2 * (crypto_int64) f8_19; + crypto_int64 f6f9_38 = f6 * (crypto_int64) f9_38; + crypto_int64 f7f7_38 = f7 * (crypto_int64) f7_38; + crypto_int64 f7f8_38 = f7_2 * (crypto_int64) f8_19; + crypto_int64 f7f9_76 = f7_2 * (crypto_int64) f9_38; + crypto_int64 f8f8_19 = f8 * (crypto_int64) f8_19; + crypto_int64 f8f9_38 = f8 * (crypto_int64) f9_38; + crypto_int64 f9f9_38 = f9 * (crypto_int64) f9_38; + crypto_int64 h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38; + crypto_int64 h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38; + crypto_int64 h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19; + crypto_int64 h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38; + crypto_int64 h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38; + crypto_int64 h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38; + crypto_int64 h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19; + crypto_int64 h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38; + crypto_int64 h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38; + crypto_int64 h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2; + crypto_int64 carry0; + crypto_int64 carry1; + crypto_int64 carry2; + crypto_int64 carry3; + crypto_int64 carry4; + crypto_int64 carry5; + crypto_int64 carry6; + crypto_int64 carry7; + crypto_int64 carry8; + crypto_int64 carry9; + + carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + + carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + + carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + + carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + + carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_sq2.c b/libs/libaxolotl/src/curve25519/ed25519/fe_sq2.c new file mode 100644 index 0000000000..026ed3aacf --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_sq2.c @@ -0,0 +1,160 @@ +#include "fe.h" +#include "crypto_int64.h" + +/* +h = 2 * f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +See fe_mul.c for discussion of implementation strategy. +*/ + +void fe_sq2(fe h,const fe f) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + crypto_int32 f0_2 = 2 * f0; + crypto_int32 f1_2 = 2 * f1; + crypto_int32 f2_2 = 2 * f2; + crypto_int32 f3_2 = 2 * f3; + crypto_int32 f4_2 = 2 * f4; + crypto_int32 f5_2 = 2 * f5; + crypto_int32 f6_2 = 2 * f6; + crypto_int32 f7_2 = 2 * f7; + crypto_int32 f5_38 = 38 * f5; /* 1.959375*2^30 */ + crypto_int32 f6_19 = 19 * f6; /* 1.959375*2^30 */ + crypto_int32 f7_38 = 38 * f7; /* 1.959375*2^30 */ + crypto_int32 f8_19 = 19 * f8; /* 1.959375*2^30 */ + crypto_int32 f9_38 = 38 * f9; /* 1.959375*2^30 */ + crypto_int64 f0f0 = f0 * (crypto_int64) f0; + crypto_int64 f0f1_2 = f0_2 * (crypto_int64) f1; + crypto_int64 f0f2_2 = f0_2 * (crypto_int64) f2; + crypto_int64 f0f3_2 = f0_2 * (crypto_int64) f3; + crypto_int64 f0f4_2 = f0_2 * (crypto_int64) f4; + crypto_int64 f0f5_2 = f0_2 * (crypto_int64) f5; + crypto_int64 f0f6_2 = f0_2 * (crypto_int64) f6; + crypto_int64 f0f7_2 = f0_2 * (crypto_int64) f7; + crypto_int64 f0f8_2 = f0_2 * (crypto_int64) f8; + crypto_int64 f0f9_2 = f0_2 * (crypto_int64) f9; + crypto_int64 f1f1_2 = f1_2 * (crypto_int64) f1; + crypto_int64 f1f2_2 = f1_2 * (crypto_int64) f2; + crypto_int64 f1f3_4 = f1_2 * (crypto_int64) f3_2; + crypto_int64 f1f4_2 = f1_2 * (crypto_int64) f4; + crypto_int64 f1f5_4 = f1_2 * (crypto_int64) f5_2; + crypto_int64 f1f6_2 = f1_2 * (crypto_int64) f6; + crypto_int64 f1f7_4 = f1_2 * (crypto_int64) f7_2; + crypto_int64 f1f8_2 = f1_2 * (crypto_int64) f8; + crypto_int64 f1f9_76 = f1_2 * (crypto_int64) f9_38; + crypto_int64 f2f2 = f2 * (crypto_int64) f2; + crypto_int64 f2f3_2 = f2_2 * (crypto_int64) f3; + crypto_int64 f2f4_2 = f2_2 * (crypto_int64) f4; + crypto_int64 f2f5_2 = f2_2 * (crypto_int64) f5; + crypto_int64 f2f6_2 = f2_2 * (crypto_int64) f6; + crypto_int64 f2f7_2 = f2_2 * (crypto_int64) f7; + crypto_int64 f2f8_38 = f2_2 * (crypto_int64) f8_19; + crypto_int64 f2f9_38 = f2 * (crypto_int64) f9_38; + crypto_int64 f3f3_2 = f3_2 * (crypto_int64) f3; + crypto_int64 f3f4_2 = f3_2 * (crypto_int64) f4; + crypto_int64 f3f5_4 = f3_2 * (crypto_int64) f5_2; + crypto_int64 f3f6_2 = f3_2 * (crypto_int64) f6; + crypto_int64 f3f7_76 = f3_2 * (crypto_int64) f7_38; + crypto_int64 f3f8_38 = f3_2 * (crypto_int64) f8_19; + crypto_int64 f3f9_76 = f3_2 * (crypto_int64) f9_38; + crypto_int64 f4f4 = f4 * (crypto_int64) f4; + crypto_int64 f4f5_2 = f4_2 * (crypto_int64) f5; + crypto_int64 f4f6_38 = f4_2 * (crypto_int64) f6_19; + crypto_int64 f4f7_38 = f4 * (crypto_int64) f7_38; + crypto_int64 f4f8_38 = f4_2 * (crypto_int64) f8_19; + crypto_int64 f4f9_38 = f4 * (crypto_int64) f9_38; + crypto_int64 f5f5_38 = f5 * (crypto_int64) f5_38; + crypto_int64 f5f6_38 = f5_2 * (crypto_int64) f6_19; + crypto_int64 f5f7_76 = f5_2 * (crypto_int64) f7_38; + crypto_int64 f5f8_38 = f5_2 * (crypto_int64) f8_19; + crypto_int64 f5f9_76 = f5_2 * (crypto_int64) f9_38; + crypto_int64 f6f6_19 = f6 * (crypto_int64) f6_19; + crypto_int64 f6f7_38 = f6 * (crypto_int64) f7_38; + crypto_int64 f6f8_38 = f6_2 * (crypto_int64) f8_19; + crypto_int64 f6f9_38 = f6 * (crypto_int64) f9_38; + crypto_int64 f7f7_38 = f7 * (crypto_int64) f7_38; + crypto_int64 f7f8_38 = f7_2 * (crypto_int64) f8_19; + crypto_int64 f7f9_76 = f7_2 * (crypto_int64) f9_38; + crypto_int64 f8f8_19 = f8 * (crypto_int64) f8_19; + crypto_int64 f8f9_38 = f8 * (crypto_int64) f9_38; + crypto_int64 f9f9_38 = f9 * (crypto_int64) f9_38; + crypto_int64 h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38; + crypto_int64 h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38; + crypto_int64 h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19; + crypto_int64 h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38; + crypto_int64 h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38; + crypto_int64 h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38; + crypto_int64 h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19; + crypto_int64 h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38; + crypto_int64 h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38; + crypto_int64 h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2; + crypto_int64 carry0; + crypto_int64 carry1; + crypto_int64 carry2; + crypto_int64 carry3; + crypto_int64 carry4; + crypto_int64 carry5; + crypto_int64 carry6; + crypto_int64 carry7; + crypto_int64 carry8; + crypto_int64 carry9; + + h0 += h0; + h1 += h1; + h2 += h2; + h3 += h3; + h4 += h4; + h5 += h5; + h6 += h6; + h7 += h7; + h8 += h8; + h9 += h9; + + carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + + carry1 = (h1 + (crypto_int64) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (crypto_int64) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + + carry2 = (h2 + (crypto_int64) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (crypto_int64) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + + carry3 = (h3 + (crypto_int64) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (crypto_int64) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry4 = (h4 + (crypto_int64) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (crypto_int64) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + carry9 = (h9 + (crypto_int64) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + + carry0 = (h0 + (crypto_int64) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_sub.c b/libs/libaxolotl/src/curve25519/ed25519/fe_sub.c new file mode 100644 index 0000000000..6e26b7df8f --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_sub.c @@ -0,0 +1,57 @@ +#include "fe.h" + +/* +h = f - g +Can overlap h with f or g. + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +void fe_sub(fe h,const fe f,const fe g) +{ + crypto_int32 f0 = f[0]; + crypto_int32 f1 = f[1]; + crypto_int32 f2 = f[2]; + crypto_int32 f3 = f[3]; + crypto_int32 f4 = f[4]; + crypto_int32 f5 = f[5]; + crypto_int32 f6 = f[6]; + crypto_int32 f7 = f[7]; + crypto_int32 f8 = f[8]; + crypto_int32 f9 = f[9]; + crypto_int32 g0 = g[0]; + crypto_int32 g1 = g[1]; + crypto_int32 g2 = g[2]; + crypto_int32 g3 = g[3]; + crypto_int32 g4 = g[4]; + crypto_int32 g5 = g[5]; + crypto_int32 g6 = g[6]; + crypto_int32 g7 = g[7]; + crypto_int32 g8 = g[8]; + crypto_int32 g9 = g[9]; + crypto_int32 h0 = f0 - g0; + crypto_int32 h1 = f1 - g1; + crypto_int32 h2 = f2 - g2; + crypto_int32 h3 = f3 - g3; + crypto_int32 h4 = f4 - g4; + crypto_int32 h5 = f5 - g5; + crypto_int32 h6 = f6 - g6; + crypto_int32 h7 = f7 - g7; + crypto_int32 h8 = f8 - g8; + crypto_int32 h9 = f9 - g9; + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/fe_tobytes.c b/libs/libaxolotl/src/curve25519/ed25519/fe_tobytes.c new file mode 100644 index 0000000000..0a63baf9c1 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/fe_tobytes.c @@ -0,0 +1,119 @@ +#include "fe.h" + +/* +Preconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + +Write p=2^255-19; q=floor(h/p). +Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). + +Proof: + Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. + Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4. + + Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). + Then 0<y<1. + + Write r=h-pq. + Have 0<=r<=p-1=2^255-20. + Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1. + + Write x=r+19(2^-255)r+y. + Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q. + + Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1)) + so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q. +*/ + +void fe_tobytes(unsigned char *s,const fe h) +{ + crypto_int32 h0 = h[0]; + crypto_int32 h1 = h[1]; + crypto_int32 h2 = h[2]; + crypto_int32 h3 = h[3]; + crypto_int32 h4 = h[4]; + crypto_int32 h5 = h[5]; + crypto_int32 h6 = h[6]; + crypto_int32 h7 = h[7]; + crypto_int32 h8 = h[8]; + crypto_int32 h9 = h[9]; + crypto_int32 q; + crypto_int32 carry0; + crypto_int32 carry1; + crypto_int32 carry2; + crypto_int32 carry3; + crypto_int32 carry4; + crypto_int32 carry5; + crypto_int32 carry6; + crypto_int32 carry7; + crypto_int32 carry8; + crypto_int32 carry9; + + q = (19 * h9 + (((crypto_int32) 1) << 24)) >> 25; + q = (h0 + q) >> 26; + q = (h1 + q) >> 25; + q = (h2 + q) >> 26; + q = (h3 + q) >> 25; + q = (h4 + q) >> 26; + q = (h5 + q) >> 25; + q = (h6 + q) >> 26; + q = (h7 + q) >> 25; + q = (h8 + q) >> 26; + q = (h9 + q) >> 25; + + /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */ + h0 += 19 * q; + /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */ + + carry0 = h0 >> 26; h1 += carry0; h0 -= carry0 << 26; + carry1 = h1 >> 25; h2 += carry1; h1 -= carry1 << 25; + carry2 = h2 >> 26; h3 += carry2; h2 -= carry2 << 26; + carry3 = h3 >> 25; h4 += carry3; h3 -= carry3 << 25; + carry4 = h4 >> 26; h5 += carry4; h4 -= carry4 << 26; + carry5 = h5 >> 25; h6 += carry5; h5 -= carry5 << 25; + carry6 = h6 >> 26; h7 += carry6; h6 -= carry6 << 26; + carry7 = h7 >> 25; h8 += carry7; h7 -= carry7 << 25; + carry8 = h8 >> 26; h9 += carry8; h8 -= carry8 << 26; + carry9 = h9 >> 25; h9 -= carry9 << 25; + /* h10 = carry9 */ + + /* + Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. + Have h0+...+2^230 h9 between 0 and 2^255-1; + evidently 2^255 h10-2^255 q = 0. + Goal: Output h0+...+2^230 h9. + */ + + s[0] = h0 >> 0; + s[1] = h0 >> 8; + s[2] = h0 >> 16; + s[3] = (h0 >> 24) | (h1 << 2); + s[4] = h1 >> 6; + s[5] = h1 >> 14; + s[6] = (h1 >> 22) | (h2 << 3); + s[7] = h2 >> 5; + s[8] = h2 >> 13; + s[9] = (h2 >> 21) | (h3 << 5); + s[10] = h3 >> 3; + s[11] = h3 >> 11; + s[12] = (h3 >> 19) | (h4 << 6); + s[13] = h4 >> 2; + s[14] = h4 >> 10; + s[15] = h4 >> 18; + s[16] = h5 >> 0; + s[17] = h5 >> 8; + s[18] = h5 >> 16; + s[19] = (h5 >> 24) | (h6 << 1); + s[20] = h6 >> 7; + s[21] = h6 >> 15; + s[22] = (h6 >> 23) | (h7 << 3); + s[23] = h7 >> 5; + s[24] = h7 >> 13; + s[25] = (h7 >> 21) | (h8 << 4); + s[26] = h8 >> 4; + s[27] = h8 >> 12; + s[28] = (h8 >> 20) | (h9 << 6); + s[29] = h9 >> 2; + s[30] = h9 >> 10; + s[31] = h9 >> 18; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge.h b/libs/libaxolotl/src/curve25519/ed25519/ge.h new file mode 100644 index 0000000000..55e95f95b6 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge.h @@ -0,0 +1,95 @@ +#ifndef GE_H +#define GE_H + +/* +ge means group element. + +Here the group is the set of pairs (x,y) of field elements (see fe.h) +satisfying -x^2 + y^2 = 1 + d x^2y^2 +where d = -121665/121666. + +Representations: + ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z + ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT + ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T + ge_precomp (Duif): (y+x,y-x,2dxy) +*/ + +#include "fe.h" + +typedef struct { + fe X; + fe Y; + fe Z; +} ge_p2; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p3; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p1p1; + +typedef struct { + fe yplusx; + fe yminusx; + fe xy2d; +} ge_precomp; + +typedef struct { + fe YplusX; + fe YminusX; + fe Z; + fe T2d; +} ge_cached; + +#define ge_frombytes_negate_vartime crypto_sign_ed25519_ref10_ge_frombytes_negate_vartime +#define ge_tobytes crypto_sign_ed25519_ref10_ge_tobytes +#define ge_p3_tobytes crypto_sign_ed25519_ref10_ge_p3_tobytes + +#define ge_p2_0 crypto_sign_ed25519_ref10_ge_p2_0 +#define ge_p3_0 crypto_sign_ed25519_ref10_ge_p3_0 +#define ge_precomp_0 crypto_sign_ed25519_ref10_ge_precomp_0 +#define ge_p3_to_p2 crypto_sign_ed25519_ref10_ge_p3_to_p2 +#define ge_p3_to_cached crypto_sign_ed25519_ref10_ge_p3_to_cached +#define ge_p1p1_to_p2 crypto_sign_ed25519_ref10_ge_p1p1_to_p2 +#define ge_p1p1_to_p3 crypto_sign_ed25519_ref10_ge_p1p1_to_p3 +#define ge_p2_dbl crypto_sign_ed25519_ref10_ge_p2_dbl +#define ge_p3_dbl crypto_sign_ed25519_ref10_ge_p3_dbl + +#define ge_madd crypto_sign_ed25519_ref10_ge_madd +#define ge_msub crypto_sign_ed25519_ref10_ge_msub +#define ge_add crypto_sign_ed25519_ref10_ge_add +#define ge_sub crypto_sign_ed25519_ref10_ge_sub +#define ge_scalarmult_base crypto_sign_ed25519_ref10_ge_scalarmult_base +#define ge_double_scalarmult_vartime crypto_sign_ed25519_ref10_ge_double_scalarmult_vartime + +extern void ge_tobytes(unsigned char *,const ge_p2 *); +extern void ge_p3_tobytes(unsigned char *,const ge_p3 *); +extern int ge_frombytes_negate_vartime(ge_p3 *,const unsigned char *); + +extern void ge_p2_0(ge_p2 *); +extern void ge_p3_0(ge_p3 *); +extern void ge_precomp_0(ge_precomp *); +extern void ge_p3_to_p2(ge_p2 *,const ge_p3 *); +extern void ge_p3_to_cached(ge_cached *,const ge_p3 *); +extern void ge_p1p1_to_p2(ge_p2 *,const ge_p1p1 *); +extern void ge_p1p1_to_p3(ge_p3 *,const ge_p1p1 *); +extern void ge_p2_dbl(ge_p1p1 *,const ge_p2 *); +extern void ge_p3_dbl(ge_p1p1 *,const ge_p3 *); + +extern void ge_madd(ge_p1p1 *,const ge_p3 *,const ge_precomp *); +extern void ge_msub(ge_p1p1 *,const ge_p3 *,const ge_precomp *); +extern void ge_add(ge_p1p1 *,const ge_p3 *,const ge_cached *); +extern void ge_sub(ge_p1p1 *,const ge_p3 *,const ge_cached *); +extern void ge_scalarmult_base(ge_p3 *,const unsigned char *); +extern void ge_double_scalarmult_vartime(ge_p2 *,const unsigned char *,const ge_p3 *,const unsigned char *); + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_add.c b/libs/libaxolotl/src/curve25519/ed25519/ge_add.c new file mode 100644 index 0000000000..da7ff5d2eb --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_add.c @@ -0,0 +1,11 @@ +#include "ge.h" + +/* +r = p + q +*/ + +void ge_add(ge_p1p1 *r,const ge_p3 *p,const ge_cached *q) +{ + fe t0; +#include "ge_add.h" +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_add.h b/libs/libaxolotl/src/curve25519/ed25519/ge_add.h new file mode 100644 index 0000000000..7481f8ffbe --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_add.h @@ -0,0 +1,97 @@ + +/* qhasm: enter ge_add */ + +/* qhasm: fe X1 */ + +/* qhasm: fe Y1 */ + +/* qhasm: fe Z1 */ + +/* qhasm: fe Z2 */ + +/* qhasm: fe T1 */ + +/* qhasm: fe ZZ */ + +/* qhasm: fe YpX2 */ + +/* qhasm: fe YmX2 */ + +/* qhasm: fe T2d2 */ + +/* qhasm: fe X3 */ + +/* qhasm: fe Y3 */ + +/* qhasm: fe Z3 */ + +/* qhasm: fe T3 */ + +/* qhasm: fe YpX1 */ + +/* qhasm: fe YmX1 */ + +/* qhasm: fe A */ + +/* qhasm: fe B */ + +/* qhasm: fe C */ + +/* qhasm: fe D */ + +/* qhasm: YpX1 = Y1+X1 */ +/* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */ +/* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */ +fe_add(r->X,p->Y,p->X); + +/* qhasm: YmX1 = Y1-X1 */ +/* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */ +/* asm 2: fe_sub(>YmX1=r->Y,<Y1=p->Y,<X1=p->X); */ +fe_sub(r->Y,p->Y,p->X); + +/* qhasm: A = YpX1*YpX2 */ +/* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<YpX2=fe#15); */ +/* asm 2: fe_mul(>A=r->Z,<YpX1=r->X,<YpX2=q->YplusX); */ +fe_mul(r->Z,r->X,q->YplusX); + +/* qhasm: B = YmX1*YmX2 */ +/* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<YmX2=fe#16); */ +/* asm 2: fe_mul(>B=r->Y,<YmX1=r->Y,<YmX2=q->YminusX); */ +fe_mul(r->Y,r->Y,q->YminusX); + +/* qhasm: C = T2d2*T1 */ +/* asm 1: fe_mul(>C=fe#4,<T2d2=fe#18,<T1=fe#14); */ +/* asm 2: fe_mul(>C=r->T,<T2d2=q->T2d,<T1=p->T); */ +fe_mul(r->T,q->T2d,p->T); + +/* qhasm: ZZ = Z1*Z2 */ +/* asm 1: fe_mul(>ZZ=fe#1,<Z1=fe#13,<Z2=fe#17); */ +/* asm 2: fe_mul(>ZZ=r->X,<Z1=p->Z,<Z2=q->Z); */ +fe_mul(r->X,p->Z,q->Z); + +/* qhasm: D = 2*ZZ */ +/* asm 1: fe_add(>D=fe#5,<ZZ=fe#1,<ZZ=fe#1); */ +/* asm 2: fe_add(>D=t0,<ZZ=r->X,<ZZ=r->X); */ +fe_add(t0,r->X,r->X); + +/* qhasm: X3 = A-B */ +/* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */ +/* asm 2: fe_sub(>X3=r->X,<A=r->Z,<B=r->Y); */ +fe_sub(r->X,r->Z,r->Y); + +/* qhasm: Y3 = A+B */ +/* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */ +/* asm 2: fe_add(>Y3=r->Y,<A=r->Z,<B=r->Y); */ +fe_add(r->Y,r->Z,r->Y); + +/* qhasm: Z3 = D+C */ +/* asm 1: fe_add(>Z3=fe#3,<D=fe#5,<C=fe#4); */ +/* asm 2: fe_add(>Z3=r->Z,<D=t0,<C=r->T); */ +fe_add(r->Z,t0,r->T); + +/* qhasm: T3 = D-C */ +/* asm 1: fe_sub(>T3=fe#4,<D=fe#5,<C=fe#4); */ +/* asm 2: fe_sub(>T3=r->T,<D=t0,<C=r->T); */ +fe_sub(r->T,t0,r->T); + +/* qhasm: return */ diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_double_scalarmult.c b/libs/libaxolotl/src/curve25519/ed25519/ge_double_scalarmult.c new file mode 100644 index 0000000000..f8bf4bf775 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_double_scalarmult.c @@ -0,0 +1,96 @@ +#include "ge.h" + +static void slide(signed char *r,const unsigned char *a) +{ + int i; + int b; + int k; + + for (i = 0;i < 256;++i) + r[i] = 1 & (a[i >> 3] >> (i & 7)); + + for (i = 0;i < 256;++i) + if (r[i]) { + for (b = 1;b <= 6 && i + b < 256;++b) { + if (r[i + b]) { + if (r[i] + (r[i + b] << b) <= 15) { + r[i] += r[i + b] << b; r[i + b] = 0; + } else if (r[i] - (r[i + b] << b) >= -15) { + r[i] -= r[i + b] << b; + for (k = i + b;k < 256;++k) { + if (!r[k]) { + r[k] = 1; + break; + } + r[k] = 0; + } + } else + break; + } + } + } + +} + +static ge_precomp Bi[8] = { +#include "base2.h" +} ; + +/* +r = a * A + b * B +where a = a[0]+256*a[1]+...+256^31 a[31]. +and b = b[0]+256*b[1]+...+256^31 b[31]. +B is the Ed25519 base point (x,4/5) with x positive. +*/ + +void ge_double_scalarmult_vartime(ge_p2 *r,const unsigned char *a,const ge_p3 *A,const unsigned char *b) +{ + signed char aslide[256]; + signed char bslide[256]; + ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ + ge_p1p1 t; + ge_p3 u; + ge_p3 A2; + int i; + + slide(aslide,a); + slide(bslide,b); + + ge_p3_to_cached(&Ai[0],A); + ge_p3_dbl(&t,A); ge_p1p1_to_p3(&A2,&t); + ge_add(&t,&A2,&Ai[0]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[1],&u); + ge_add(&t,&A2,&Ai[1]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[2],&u); + ge_add(&t,&A2,&Ai[2]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[3],&u); + ge_add(&t,&A2,&Ai[3]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[4],&u); + ge_add(&t,&A2,&Ai[4]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[5],&u); + ge_add(&t,&A2,&Ai[5]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[6],&u); + ge_add(&t,&A2,&Ai[6]); ge_p1p1_to_p3(&u,&t); ge_p3_to_cached(&Ai[7],&u); + + ge_p2_0(r); + + for (i = 255;i >= 0;--i) { + if (aslide[i] || bslide[i]) break; + } + + for (;i >= 0;--i) { + ge_p2_dbl(&t,r); + + if (aslide[i] > 0) { + ge_p1p1_to_p3(&u,&t); + ge_add(&t,&u,&Ai[aslide[i]/2]); + } else if (aslide[i] < 0) { + ge_p1p1_to_p3(&u,&t); + ge_sub(&t,&u,&Ai[(-aslide[i])/2]); + } + + if (bslide[i] > 0) { + ge_p1p1_to_p3(&u,&t); + ge_madd(&t,&u,&Bi[bslide[i]/2]); + } else if (bslide[i] < 0) { + ge_p1p1_to_p3(&u,&t); + ge_msub(&t,&u,&Bi[(-bslide[i])/2]); + } + + ge_p1p1_to_p2(r,&t); + } +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_frombytes.c b/libs/libaxolotl/src/curve25519/ed25519/ge_frombytes.c new file mode 100644 index 0000000000..1a059ee93f --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_frombytes.c @@ -0,0 +1,50 @@ +#include "ge.h" + +static const fe d = { +#include "d.h" +} ; + +static const fe sqrtm1 = { +#include "sqrtm1.h" +} ; + +int ge_frombytes_negate_vartime(ge_p3 *h,const unsigned char *s) +{ + fe u; + fe v; + fe v3; + fe vxx; + fe check; + + fe_frombytes(h->Y,s); + fe_1(h->Z); + fe_sq(u,h->Y); + fe_mul(v,u,d); + fe_sub(u,u,h->Z); /* u = y^2-1 */ + fe_add(v,v,h->Z); /* v = dy^2+1 */ + + fe_sq(v3,v); + fe_mul(v3,v3,v); /* v3 = v^3 */ + fe_sq(h->X,v3); + fe_mul(h->X,h->X,v); + fe_mul(h->X,h->X,u); /* x = uv^7 */ + + fe_pow22523(h->X,h->X); /* x = (uv^7)^((q-5)/8) */ + fe_mul(h->X,h->X,v3); + fe_mul(h->X,h->X,u); /* x = uv^3(uv^7)^((q-5)/8) */ + + fe_sq(vxx,h->X); + fe_mul(vxx,vxx,v); + fe_sub(check,vxx,u); /* vx^2-u */ + if (fe_isnonzero(check)) { + fe_add(check,vxx,u); /* vx^2+u */ + if (fe_isnonzero(check)) return -1; + fe_mul(h->X,h->X,sqrtm1); + } + + if (fe_isnegative(h->X) == (s[31] >> 7)) + fe_neg(h->X,h->X); + + fe_mul(h->T,h->X,h->Y); + return 0; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_madd.c b/libs/libaxolotl/src/curve25519/ed25519/ge_madd.c new file mode 100644 index 0000000000..622571774b --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_madd.c @@ -0,0 +1,11 @@ +#include "ge.h" + +/* +r = p + q +*/ + +void ge_madd(ge_p1p1 *r,const ge_p3 *p,const ge_precomp *q) +{ + fe t0; +#include "ge_madd.h" +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_madd.h b/libs/libaxolotl/src/curve25519/ed25519/ge_madd.h new file mode 100644 index 0000000000..ecae84952b --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_madd.h @@ -0,0 +1,88 @@ + +/* qhasm: enter ge_madd */ + +/* qhasm: fe X1 */ + +/* qhasm: fe Y1 */ + +/* qhasm: fe Z1 */ + +/* qhasm: fe T1 */ + +/* qhasm: fe ypx2 */ + +/* qhasm: fe ymx2 */ + +/* qhasm: fe xy2d2 */ + +/* qhasm: fe X3 */ + +/* qhasm: fe Y3 */ + +/* qhasm: fe Z3 */ + +/* qhasm: fe T3 */ + +/* qhasm: fe YpX1 */ + +/* qhasm: fe YmX1 */ + +/* qhasm: fe A */ + +/* qhasm: fe B */ + +/* qhasm: fe C */ + +/* qhasm: fe D */ + +/* qhasm: YpX1 = Y1+X1 */ +/* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */ +/* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */ +fe_add(r->X,p->Y,p->X); + +/* qhasm: YmX1 = Y1-X1 */ +/* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */ +/* asm 2: fe_sub(>YmX1=r->Y,<Y1=p->Y,<X1=p->X); */ +fe_sub(r->Y,p->Y,p->X); + +/* qhasm: A = YpX1*ypx2 */ +/* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<ypx2=fe#15); */ +/* asm 2: fe_mul(>A=r->Z,<YpX1=r->X,<ypx2=q->yplusx); */ +fe_mul(r->Z,r->X,q->yplusx); + +/* qhasm: B = YmX1*ymx2 */ +/* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<ymx2=fe#16); */ +/* asm 2: fe_mul(>B=r->Y,<YmX1=r->Y,<ymx2=q->yminusx); */ +fe_mul(r->Y,r->Y,q->yminusx); + +/* qhasm: C = xy2d2*T1 */ +/* asm 1: fe_mul(>C=fe#4,<xy2d2=fe#17,<T1=fe#14); */ +/* asm 2: fe_mul(>C=r->T,<xy2d2=q->xy2d,<T1=p->T); */ +fe_mul(r->T,q->xy2d,p->T); + +/* qhasm: D = 2*Z1 */ +/* asm 1: fe_add(>D=fe#5,<Z1=fe#13,<Z1=fe#13); */ +/* asm 2: fe_add(>D=t0,<Z1=p->Z,<Z1=p->Z); */ +fe_add(t0,p->Z,p->Z); + +/* qhasm: X3 = A-B */ +/* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */ +/* asm 2: fe_sub(>X3=r->X,<A=r->Z,<B=r->Y); */ +fe_sub(r->X,r->Z,r->Y); + +/* qhasm: Y3 = A+B */ +/* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */ +/* asm 2: fe_add(>Y3=r->Y,<A=r->Z,<B=r->Y); */ +fe_add(r->Y,r->Z,r->Y); + +/* qhasm: Z3 = D+C */ +/* asm 1: fe_add(>Z3=fe#3,<D=fe#5,<C=fe#4); */ +/* asm 2: fe_add(>Z3=r->Z,<D=t0,<C=r->T); */ +fe_add(r->Z,t0,r->T); + +/* qhasm: T3 = D-C */ +/* asm 1: fe_sub(>T3=fe#4,<D=fe#5,<C=fe#4); */ +/* asm 2: fe_sub(>T3=r->T,<D=t0,<C=r->T); */ +fe_sub(r->T,t0,r->T); + +/* qhasm: return */ diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_msub.c b/libs/libaxolotl/src/curve25519/ed25519/ge_msub.c new file mode 100644 index 0000000000..741ecbf113 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_msub.c @@ -0,0 +1,11 @@ +#include "ge.h" + +/* +r = p - q +*/ + +void ge_msub(ge_p1p1 *r,const ge_p3 *p,const ge_precomp *q) +{ + fe t0; +#include "ge_msub.h" +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_msub.h b/libs/libaxolotl/src/curve25519/ed25519/ge_msub.h new file mode 100644 index 0000000000..500f986ba0 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_msub.h @@ -0,0 +1,88 @@ + +/* qhasm: enter ge_msub */ + +/* qhasm: fe X1 */ + +/* qhasm: fe Y1 */ + +/* qhasm: fe Z1 */ + +/* qhasm: fe T1 */ + +/* qhasm: fe ypx2 */ + +/* qhasm: fe ymx2 */ + +/* qhasm: fe xy2d2 */ + +/* qhasm: fe X3 */ + +/* qhasm: fe Y3 */ + +/* qhasm: fe Z3 */ + +/* qhasm: fe T3 */ + +/* qhasm: fe YpX1 */ + +/* qhasm: fe YmX1 */ + +/* qhasm: fe A */ + +/* qhasm: fe B */ + +/* qhasm: fe C */ + +/* qhasm: fe D */ + +/* qhasm: YpX1 = Y1+X1 */ +/* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */ +/* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */ +fe_add(r->X,p->Y,p->X); + +/* qhasm: YmX1 = Y1-X1 */ +/* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */ +/* asm 2: fe_sub(>YmX1=r->Y,<Y1=p->Y,<X1=p->X); */ +fe_sub(r->Y,p->Y,p->X); + +/* qhasm: A = YpX1*ymx2 */ +/* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<ymx2=fe#16); */ +/* asm 2: fe_mul(>A=r->Z,<YpX1=r->X,<ymx2=q->yminusx); */ +fe_mul(r->Z,r->X,q->yminusx); + +/* qhasm: B = YmX1*ypx2 */ +/* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<ypx2=fe#15); */ +/* asm 2: fe_mul(>B=r->Y,<YmX1=r->Y,<ypx2=q->yplusx); */ +fe_mul(r->Y,r->Y,q->yplusx); + +/* qhasm: C = xy2d2*T1 */ +/* asm 1: fe_mul(>C=fe#4,<xy2d2=fe#17,<T1=fe#14); */ +/* asm 2: fe_mul(>C=r->T,<xy2d2=q->xy2d,<T1=p->T); */ +fe_mul(r->T,q->xy2d,p->T); + +/* qhasm: D = 2*Z1 */ +/* asm 1: fe_add(>D=fe#5,<Z1=fe#13,<Z1=fe#13); */ +/* asm 2: fe_add(>D=t0,<Z1=p->Z,<Z1=p->Z); */ +fe_add(t0,p->Z,p->Z); + +/* qhasm: X3 = A-B */ +/* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */ +/* asm 2: fe_sub(>X3=r->X,<A=r->Z,<B=r->Y); */ +fe_sub(r->X,r->Z,r->Y); + +/* qhasm: Y3 = A+B */ +/* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */ +/* asm 2: fe_add(>Y3=r->Y,<A=r->Z,<B=r->Y); */ +fe_add(r->Y,r->Z,r->Y); + +/* qhasm: Z3 = D-C */ +/* asm 1: fe_sub(>Z3=fe#3,<D=fe#5,<C=fe#4); */ +/* asm 2: fe_sub(>Z3=r->Z,<D=t0,<C=r->T); */ +fe_sub(r->Z,t0,r->T); + +/* qhasm: T3 = D+C */ +/* asm 1: fe_add(>T3=fe#4,<D=fe#5,<C=fe#4); */ +/* asm 2: fe_add(>T3=r->T,<D=t0,<C=r->T); */ +fe_add(r->T,t0,r->T); + +/* qhasm: return */ diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_p1p1_to_p2.c b/libs/libaxolotl/src/curve25519/ed25519/ge_p1p1_to_p2.c new file mode 100644 index 0000000000..9bb5013d66 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_p1p1_to_p2.c @@ -0,0 +1,12 @@ +#include "ge.h" + +/* +r = p +*/ + +extern void ge_p1p1_to_p2(ge_p2 *r,const ge_p1p1 *p) +{ + fe_mul(r->X,p->X,p->T); + fe_mul(r->Y,p->Y,p->Z); + fe_mul(r->Z,p->Z,p->T); +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_p1p1_to_p3.c b/libs/libaxolotl/src/curve25519/ed25519/ge_p1p1_to_p3.c new file mode 100644 index 0000000000..2f57b10968 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_p1p1_to_p3.c @@ -0,0 +1,13 @@ +#include "ge.h" + +/* +r = p +*/ + +extern void ge_p1p1_to_p3(ge_p3 *r,const ge_p1p1 *p) +{ + fe_mul(r->X,p->X,p->T); + fe_mul(r->Y,p->Y,p->Z); + fe_mul(r->Z,p->Z,p->T); + fe_mul(r->T,p->X,p->Y); +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_p2_0.c b/libs/libaxolotl/src/curve25519/ed25519/ge_p2_0.c new file mode 100644 index 0000000000..6191d1e6e4 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_p2_0.c @@ -0,0 +1,8 @@ +#include "ge.h" + +void ge_p2_0(ge_p2 *h) +{ + fe_0(h->X); + fe_1(h->Y); + fe_1(h->Z); +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_p2_dbl.c b/libs/libaxolotl/src/curve25519/ed25519/ge_p2_dbl.c new file mode 100644 index 0000000000..2e332b5cee --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_p2_dbl.c @@ -0,0 +1,11 @@ +#include "ge.h" + +/* +r = 2 * p +*/ + +void ge_p2_dbl(ge_p1p1 *r,const ge_p2 *p) +{ + fe t0; +#include "ge_p2_dbl.h" +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_p2_dbl.h b/libs/libaxolotl/src/curve25519/ed25519/ge_p2_dbl.h new file mode 100644 index 0000000000..128efed907 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_p2_dbl.h @@ -0,0 +1,73 @@ + +/* qhasm: enter ge_p2_dbl */ + +/* qhasm: fe X1 */ + +/* qhasm: fe Y1 */ + +/* qhasm: fe Z1 */ + +/* qhasm: fe A */ + +/* qhasm: fe AA */ + +/* qhasm: fe XX */ + +/* qhasm: fe YY */ + +/* qhasm: fe B */ + +/* qhasm: fe X3 */ + +/* qhasm: fe Y3 */ + +/* qhasm: fe Z3 */ + +/* qhasm: fe T3 */ + +/* qhasm: XX=X1^2 */ +/* asm 1: fe_sq(>XX=fe#1,<X1=fe#11); */ +/* asm 2: fe_sq(>XX=r->X,<X1=p->X); */ +fe_sq(r->X,p->X); + +/* qhasm: YY=Y1^2 */ +/* asm 1: fe_sq(>YY=fe#3,<Y1=fe#12); */ +/* asm 2: fe_sq(>YY=r->Z,<Y1=p->Y); */ +fe_sq(r->Z,p->Y); + +/* qhasm: B=2*Z1^2 */ +/* asm 1: fe_sq2(>B=fe#4,<Z1=fe#13); */ +/* asm 2: fe_sq2(>B=r->T,<Z1=p->Z); */ +fe_sq2(r->T,p->Z); + +/* qhasm: A=X1+Y1 */ +/* asm 1: fe_add(>A=fe#2,<X1=fe#11,<Y1=fe#12); */ +/* asm 2: fe_add(>A=r->Y,<X1=p->X,<Y1=p->Y); */ +fe_add(r->Y,p->X,p->Y); + +/* qhasm: AA=A^2 */ +/* asm 1: fe_sq(>AA=fe#5,<A=fe#2); */ +/* asm 2: fe_sq(>AA=t0,<A=r->Y); */ +fe_sq(t0,r->Y); + +/* qhasm: Y3=YY+XX */ +/* asm 1: fe_add(>Y3=fe#2,<YY=fe#3,<XX=fe#1); */ +/* asm 2: fe_add(>Y3=r->Y,<YY=r->Z,<XX=r->X); */ +fe_add(r->Y,r->Z,r->X); + +/* qhasm: Z3=YY-XX */ +/* asm 1: fe_sub(>Z3=fe#3,<YY=fe#3,<XX=fe#1); */ +/* asm 2: fe_sub(>Z3=r->Z,<YY=r->Z,<XX=r->X); */ +fe_sub(r->Z,r->Z,r->X); + +/* qhasm: X3=AA-Y3 */ +/* asm 1: fe_sub(>X3=fe#1,<AA=fe#5,<Y3=fe#2); */ +/* asm 2: fe_sub(>X3=r->X,<AA=t0,<Y3=r->Y); */ +fe_sub(r->X,t0,r->Y); + +/* qhasm: T3=B-Z3 */ +/* asm 1: fe_sub(>T3=fe#4,<B=fe#4,<Z3=fe#3); */ +/* asm 2: fe_sub(>T3=r->T,<B=r->T,<Z3=r->Z); */ +fe_sub(r->T,r->T,r->Z); + +/* qhasm: return */ diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_p3_0.c b/libs/libaxolotl/src/curve25519/ed25519/ge_p3_0.c new file mode 100644 index 0000000000..401b2935a1 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_p3_0.c @@ -0,0 +1,9 @@ +#include "ge.h" + +void ge_p3_0(ge_p3 *h) +{ + fe_0(h->X); + fe_1(h->Y); + fe_1(h->Z); + fe_0(h->T); +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_p3_dbl.c b/libs/libaxolotl/src/curve25519/ed25519/ge_p3_dbl.c new file mode 100644 index 0000000000..0d8a05915d --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_p3_dbl.c @@ -0,0 +1,12 @@ +#include "ge.h" + +/* +r = 2 * p +*/ + +void ge_p3_dbl(ge_p1p1 *r,const ge_p3 *p) +{ + ge_p2 q; + ge_p3_to_p2(&q,p); + ge_p2_dbl(r,&q); +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_p3_to_cached.c b/libs/libaxolotl/src/curve25519/ed25519/ge_p3_to_cached.c new file mode 100644 index 0000000000..bde64228cf --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_p3_to_cached.c @@ -0,0 +1,17 @@ +#include "ge.h" + +/* +r = p +*/ + +static const fe d2 = { +#include "d2.h" +} ; + +extern void ge_p3_to_cached(ge_cached *r,const ge_p3 *p) +{ + fe_add(r->YplusX,p->Y,p->X); + fe_sub(r->YminusX,p->Y,p->X); + fe_copy(r->Z,p->Z); + fe_mul(r->T2d,p->T,d2); +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_p3_to_p2.c b/libs/libaxolotl/src/curve25519/ed25519/ge_p3_to_p2.c new file mode 100644 index 0000000000..e532a9e4cb --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_p3_to_p2.c @@ -0,0 +1,12 @@ +#include "ge.h" + +/* +r = p +*/ + +extern void ge_p3_to_p2(ge_p2 *r,const ge_p3 *p) +{ + fe_copy(r->X,p->X); + fe_copy(r->Y,p->Y); + fe_copy(r->Z,p->Z); +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_p3_tobytes.c b/libs/libaxolotl/src/curve25519/ed25519/ge_p3_tobytes.c new file mode 100644 index 0000000000..21cb2fc656 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_p3_tobytes.c @@ -0,0 +1,14 @@ +#include "ge.h" + +void ge_p3_tobytes(unsigned char *s,const ge_p3 *h) +{ + fe recip; + fe x; + fe y; + + fe_invert(recip,h->Z); + fe_mul(x,h->X,recip); + fe_mul(y,h->Y,recip); + fe_tobytes(s,y); + s[31] ^= fe_isnegative(x) << 7; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_precomp_0.c b/libs/libaxolotl/src/curve25519/ed25519/ge_precomp_0.c new file mode 100644 index 0000000000..2e218861d8 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_precomp_0.c @@ -0,0 +1,8 @@ +#include "ge.h" + +void ge_precomp_0(ge_precomp *h) +{ + fe_1(h->yplusx); + fe_1(h->yminusx); + fe_0(h->xy2d); +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_scalarmult_base.c b/libs/libaxolotl/src/curve25519/ed25519/ge_scalarmult_base.c new file mode 100644 index 0000000000..421e4fa0fb --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_scalarmult_base.c @@ -0,0 +1,105 @@ +#include "ge.h" +#include "crypto_uint32.h" + +static unsigned char equal(signed char b,signed char c) +{ + unsigned char ub = b; + unsigned char uc = c; + unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ + crypto_uint32 y = x; /* 0: yes; 1..255: no */ + y -= 1; /* 4294967295: yes; 0..254: no */ + y >>= 31; /* 1: yes; 0: no */ + return y; +} + +static unsigned char negative(signed char b) +{ + unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ + x >>= 63; /* 1: yes; 0: no */ + return x; +} + +static void cmov(ge_precomp *t,ge_precomp *u,unsigned char b) +{ + fe_cmov(t->yplusx,u->yplusx,b); + fe_cmov(t->yminusx,u->yminusx,b); + fe_cmov(t->xy2d,u->xy2d,b); +} + +/* base[i][j] = (j+1)*256^i*B */ +static ge_precomp base[32][8] = { +#include "base.h" +} ; + +static void select(ge_precomp *t,int pos,signed char b) +{ + ge_precomp minust; + unsigned char bnegative = negative(b); + unsigned char babs = b - (((-bnegative) & b) << 1); + + ge_precomp_0(t); + cmov(t,&base[pos][0],equal(babs,1)); + cmov(t,&base[pos][1],equal(babs,2)); + cmov(t,&base[pos][2],equal(babs,3)); + cmov(t,&base[pos][3],equal(babs,4)); + cmov(t,&base[pos][4],equal(babs,5)); + cmov(t,&base[pos][5],equal(babs,6)); + cmov(t,&base[pos][6],equal(babs,7)); + cmov(t,&base[pos][7],equal(babs,8)); + fe_copy(minust.yplusx,t->yminusx); + fe_copy(minust.yminusx,t->yplusx); + fe_neg(minust.xy2d,t->xy2d); + cmov(t,&minust,bnegative); +} + +/* +h = a * B +where a = a[0]+256*a[1]+...+256^31 a[31] +B is the Ed25519 base point (x,4/5) with x positive. + +Preconditions: + a[31] <= 127 +*/ + +void ge_scalarmult_base(ge_p3 *h,const unsigned char *a) +{ + signed char e[64]; + signed char carry; + ge_p1p1 r; + ge_p2 s; + ge_precomp t; + int i; + + for (i = 0;i < 32;++i) { + e[2 * i + 0] = (a[i] >> 0) & 15; + e[2 * i + 1] = (a[i] >> 4) & 15; + } + /* each e[i] is between 0 and 15 */ + /* e[63] is between 0 and 7 */ + + carry = 0; + for (i = 0;i < 63;++i) { + e[i] += carry; + carry = e[i] + 8; + carry >>= 4; + e[i] -= carry << 4; + } + e[63] += carry; + /* each e[i] is between -8 and 8 */ + + ge_p3_0(h); + for (i = 1;i < 64;i += 2) { + select(&t,i / 2,e[i]); + ge_madd(&r,h,&t); ge_p1p1_to_p3(h,&r); + } + + ge_p3_dbl(&r,h); ge_p1p1_to_p2(&s,&r); + ge_p2_dbl(&r,&s); ge_p1p1_to_p2(&s,&r); + ge_p2_dbl(&r,&s); ge_p1p1_to_p2(&s,&r); + ge_p2_dbl(&r,&s); ge_p1p1_to_p3(h,&r); + + for (i = 0;i < 64;i += 2) { + select(&t,i / 2,e[i]); + ge_madd(&r,h,&t); ge_p1p1_to_p3(h,&r); + } +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_sub.c b/libs/libaxolotl/src/curve25519/ed25519/ge_sub.c new file mode 100644 index 0000000000..69f3d54062 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_sub.c @@ -0,0 +1,11 @@ +#include "ge.h" + +/* +r = p - q +*/ + +void ge_sub(ge_p1p1 *r,const ge_p3 *p,const ge_cached *q) +{ + fe t0; +#include "ge_sub.h" +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_sub.h b/libs/libaxolotl/src/curve25519/ed25519/ge_sub.h new file mode 100644 index 0000000000..b4ef1f5dd0 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_sub.h @@ -0,0 +1,97 @@ + +/* qhasm: enter ge_sub */ + +/* qhasm: fe X1 */ + +/* qhasm: fe Y1 */ + +/* qhasm: fe Z1 */ + +/* qhasm: fe Z2 */ + +/* qhasm: fe T1 */ + +/* qhasm: fe ZZ */ + +/* qhasm: fe YpX2 */ + +/* qhasm: fe YmX2 */ + +/* qhasm: fe T2d2 */ + +/* qhasm: fe X3 */ + +/* qhasm: fe Y3 */ + +/* qhasm: fe Z3 */ + +/* qhasm: fe T3 */ + +/* qhasm: fe YpX1 */ + +/* qhasm: fe YmX1 */ + +/* qhasm: fe A */ + +/* qhasm: fe B */ + +/* qhasm: fe C */ + +/* qhasm: fe D */ + +/* qhasm: YpX1 = Y1+X1 */ +/* asm 1: fe_add(>YpX1=fe#1,<Y1=fe#12,<X1=fe#11); */ +/* asm 2: fe_add(>YpX1=r->X,<Y1=p->Y,<X1=p->X); */ +fe_add(r->X,p->Y,p->X); + +/* qhasm: YmX1 = Y1-X1 */ +/* asm 1: fe_sub(>YmX1=fe#2,<Y1=fe#12,<X1=fe#11); */ +/* asm 2: fe_sub(>YmX1=r->Y,<Y1=p->Y,<X1=p->X); */ +fe_sub(r->Y,p->Y,p->X); + +/* qhasm: A = YpX1*YmX2 */ +/* asm 1: fe_mul(>A=fe#3,<YpX1=fe#1,<YmX2=fe#16); */ +/* asm 2: fe_mul(>A=r->Z,<YpX1=r->X,<YmX2=q->YminusX); */ +fe_mul(r->Z,r->X,q->YminusX); + +/* qhasm: B = YmX1*YpX2 */ +/* asm 1: fe_mul(>B=fe#2,<YmX1=fe#2,<YpX2=fe#15); */ +/* asm 2: fe_mul(>B=r->Y,<YmX1=r->Y,<YpX2=q->YplusX); */ +fe_mul(r->Y,r->Y,q->YplusX); + +/* qhasm: C = T2d2*T1 */ +/* asm 1: fe_mul(>C=fe#4,<T2d2=fe#18,<T1=fe#14); */ +/* asm 2: fe_mul(>C=r->T,<T2d2=q->T2d,<T1=p->T); */ +fe_mul(r->T,q->T2d,p->T); + +/* qhasm: ZZ = Z1*Z2 */ +/* asm 1: fe_mul(>ZZ=fe#1,<Z1=fe#13,<Z2=fe#17); */ +/* asm 2: fe_mul(>ZZ=r->X,<Z1=p->Z,<Z2=q->Z); */ +fe_mul(r->X,p->Z,q->Z); + +/* qhasm: D = 2*ZZ */ +/* asm 1: fe_add(>D=fe#5,<ZZ=fe#1,<ZZ=fe#1); */ +/* asm 2: fe_add(>D=t0,<ZZ=r->X,<ZZ=r->X); */ +fe_add(t0,r->X,r->X); + +/* qhasm: X3 = A-B */ +/* asm 1: fe_sub(>X3=fe#1,<A=fe#3,<B=fe#2); */ +/* asm 2: fe_sub(>X3=r->X,<A=r->Z,<B=r->Y); */ +fe_sub(r->X,r->Z,r->Y); + +/* qhasm: Y3 = A+B */ +/* asm 1: fe_add(>Y3=fe#2,<A=fe#3,<B=fe#2); */ +/* asm 2: fe_add(>Y3=r->Y,<A=r->Z,<B=r->Y); */ +fe_add(r->Y,r->Z,r->Y); + +/* qhasm: Z3 = D-C */ +/* asm 1: fe_sub(>Z3=fe#3,<D=fe#5,<C=fe#4); */ +/* asm 2: fe_sub(>Z3=r->Z,<D=t0,<C=r->T); */ +fe_sub(r->Z,t0,r->T); + +/* qhasm: T3 = D+C */ +/* asm 1: fe_add(>T3=fe#4,<D=fe#5,<C=fe#4); */ +/* asm 2: fe_add(>T3=r->T,<D=t0,<C=r->T); */ +fe_add(r->T,t0,r->T); + +/* qhasm: return */ diff --git a/libs/libaxolotl/src/curve25519/ed25519/ge_tobytes.c b/libs/libaxolotl/src/curve25519/ed25519/ge_tobytes.c new file mode 100644 index 0000000000..31b3d33e09 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/ge_tobytes.c @@ -0,0 +1,14 @@ +#include "ge.h" + +void ge_tobytes(unsigned char *s,const ge_p2 *h) +{ + fe recip; + fe x; + fe y; + + fe_invert(recip,h->Z); + fe_mul(x,h->X,recip); + fe_mul(y,h->Y,recip); + fe_tobytes(s,y); + s[31] ^= fe_isnegative(x) << 7; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/main/main.c b/libs/libaxolotl/src/curve25519/ed25519/main/main.c new file mode 100644 index 0000000000..5fbe39956d --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/main/main.c @@ -0,0 +1,106 @@ +#include <stdio.h> +#include <string.h> +#include "crypto_hash_sha512.h" +#include "curve_sigs.h" + +#define MSG_LEN 200 + +int main(int argc, char* argv[]) +{ + unsigned char privkey[32]; + unsigned char pubkey[32]; + unsigned char signature[64]; + unsigned char msg[MSG_LEN]; + unsigned char random[64]; + + /* Initialize pubkey, privkey, msg */ + memset(msg, 0, MSG_LEN); + memset(privkey, 0, 32); + memset(pubkey, 0, 32); + privkey[0] &= 248; + privkey[31] &= 63; + privkey[31] |= 64; + + privkey[8] = 189; /* just so there's some bits set */ + + + /* SHA512 test */ + unsigned char sha512_input[112] = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"; + unsigned char sha512_correct_output[64] = +{ +0x8E, 0x95, 0x9B, 0x75, 0xDA, 0xE3, 0x13, 0xDA, +0x8C, 0xF4, 0xF7, 0x28, 0x14, 0xFC, 0x14, 0x3F, +0x8F, 0x77, 0x79, 0xC6, 0xEB, 0x9F, 0x7F, 0xA1, +0x72, 0x99, 0xAE, 0xAD, 0xB6, 0x88, 0x90, 0x18, +0x50, 0x1D, 0x28, 0x9E, 0x49, 0x00, 0xF7, 0xE4, +0x33, 0x1B, 0x99, 0xDE, 0xC4, 0xB5, 0x43, 0x3A, +0xC7, 0xD3, 0x29, 0xEE, 0xB6, 0xDD, 0x26, 0x54, +0x5E, 0x96, 0xE5, 0x5B, 0x87, 0x4B, 0xE9, 0x09 +}; + unsigned char sha512_actual_output[64]; + + crypto_hash_sha512(sha512_actual_output, sha512_input, sizeof(sha512_input)); + if (memcmp(sha512_actual_output, sha512_correct_output, 64) != 0) + printf("SHA512 bad #1\n"); + else + printf("SHA512 good #1\n"); + + sha512_input[111] ^= 1; + + crypto_hash_sha512(sha512_actual_output, sha512_input, sizeof(sha512_input)); + if (memcmp(sha512_actual_output, sha512_correct_output, 64) != 0) + printf("SHA512 good #2\n"); + else + printf("SHA512 bad #2\n"); + + /* Signature test */ + curve25519_keygen(pubkey, privkey); + + curve25519_sign(signature, privkey, msg, MSG_LEN, random); + + if (curve25519_verify(signature, pubkey, msg, MSG_LEN) == 0) + printf("Signature good #1\n"); + else + printf("Signature bad #1\n"); + + signature[0] ^= 1; + + if (curve25519_verify(signature, pubkey, msg, MSG_LEN) == 0) + printf("Signature bad #2\n"); + else + printf("Signature good #2\n"); + + + printf("Random testing...\n"); + for (int count = 0; count < 10000; count++) { + unsigned char b[64]; + crypto_hash_sha512(b, privkey, 32); + memmove(privkey, b, 32); + crypto_hash_sha512(b, privkey, 32); + memmove(random, b, 64); + + privkey[0] &= 248; + privkey[31] &= 63; + privkey[31] |= 64; + + curve25519_keygen(pubkey, privkey); + + curve25519_sign(signature, privkey, msg, MSG_LEN, random); + + if (curve25519_verify(signature, pubkey, msg, MSG_LEN) != 0) { + printf("failure #1 %d\n", count); + return -1; + } + + if (b[63] & 1) + signature[count % 64] ^= 1; + else + msg[count % MSG_LEN] ^= 1; + if (curve25519_verify(signature, pubkey, msg, MSG_LEN) == 0) { + printf("failure #2 %d\n", count); + return -1; + } + } + printf("OK\n"); + return 1; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_int32.h b/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_int32.h new file mode 100644 index 0000000000..cae135e6e2 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_int32.h @@ -0,0 +1,6 @@ +#ifndef crypto_int32_h +#define crypto_int32_h + +typedef int crypto_int32; + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_int64.h b/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_int64.h new file mode 100644 index 0000000000..fc92417b62 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_int64.h @@ -0,0 +1,6 @@ +#ifndef crypto_int64_h +#define crypto_int64_h + +typedef long long crypto_int64; + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_sign.h b/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_sign.h new file mode 100644 index 0000000000..8472603e91 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_sign.h @@ -0,0 +1,16 @@ +#ifndef crypto_sign_H +#define crypto_sign_H + +#include "crypto_sign_edwards25519sha512batch.h" + +#define crypto_sign crypto_sign_edwards25519sha512batch +#define crypto_sign_open crypto_sign_edwards25519sha512batch_open +#define crypto_sign_keypair crypto_sign_edwards25519sha512batch_keypair +#define crypto_sign_BYTES crypto_sign_edwards25519sha512batch_BYTES +#define crypto_sign_PUBLICKEYBYTES crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES +#define crypto_sign_SECRETKEYBYTES crypto_sign_edwards25519sha512batch_SECRETKEYBYTES +#define crypto_sign_PRIMITIVE "edwards25519sha512batch" +#define crypto_sign_IMPLEMENTATION crypto_sign_edwards25519sha512batch_IMPLEMENTATION +#define crypto_sign_VERSION crypto_sign_edwards25519sha512batch_VERSION + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_sign_edwards25519sha512batch.h b/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_sign_edwards25519sha512batch.h new file mode 100644 index 0000000000..62fae61163 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_sign_edwards25519sha512batch.h @@ -0,0 +1,33 @@ +#ifndef crypto_sign_edwards25519sha512batch_H +#define crypto_sign_edwards25519sha512batch_H + +#define crypto_sign_edwards25519sha512batch_ref10_SECRETKEYBYTES 64 +#define crypto_sign_edwards25519sha512batch_ref10_PUBLICKEYBYTES 32 +#define crypto_sign_edwards25519sha512batch_ref10_BYTES 64 +#ifdef __cplusplus +#include <string> +extern std::string crypto_sign_edwards25519sha512batch_ref10(const std::string &,const std::string &); +extern std::string crypto_sign_edwards25519sha512batch_ref10_open(const std::string &,const std::string &); +extern std::string crypto_sign_edwards25519sha512batch_ref10_keypair(std::string *); +extern "C" { +#endif +extern int crypto_sign_edwards25519sha512batch_ref10(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *); +extern int crypto_sign_edwards25519sha512batch_ref10_open(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *); +extern int crypto_sign_edwards25519sha512batch_ref10_keypair(unsigned char *,unsigned char *); +#ifdef __cplusplus +} +#endif + +#define crypto_sign_edwards25519sha512batch crypto_sign_edwards25519sha512batch_ref10 +#define crypto_sign_edwards25519sha512batch_open crypto_sign_edwards25519sha512batch_ref10_open +#define crypto_sign_edwards25519sha512batch_keypair crypto_sign_edwards25519sha512batch_ref10_keypair +#define crypto_sign_edwards25519sha512batch_BYTES crypto_sign_edwards25519sha512batch_ref10_BYTES +#define crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES crypto_sign_edwards25519sha512batch_ref10_PUBLICKEYBYTES +#define crypto_sign_edwards25519sha512batch_SECRETKEYBYTES crypto_sign_edwards25519sha512batch_ref10_SECRETKEYBYTES +#define crypto_sign_edwards25519sha512batch_IMPLEMENTATION "crypto_sign/edwards25519sha512batch/ref10" +#ifndef crypto_sign_edwards25519sha512batch_ref10_VERSION +#define crypto_sign_edwards25519sha512batch_ref10_VERSION "-" +#endif +#define crypto_sign_edwards25519sha512batch_VERSION crypto_sign_edwards25519sha512batch_ref10_VERSION + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_uint32.h b/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_uint32.h new file mode 100644 index 0000000000..21020d7b57 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_uint32.h @@ -0,0 +1,6 @@ +#ifndef crypto_uint32_h +#define crypto_uint32_h + +typedef unsigned int crypto_uint32; + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_uint64.h b/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_uint64.h new file mode 100644 index 0000000000..5aa0070375 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_uint64.h @@ -0,0 +1,6 @@ +#ifndef crypto_uint64_h +#define crypto_uint64_h + +typedef unsigned long long crypto_uint64; + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_verify_32.h b/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_verify_32.h new file mode 100644 index 0000000000..978d845222 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/nacl_includes/crypto_verify_32.h @@ -0,0 +1,22 @@ +#ifndef crypto_verify_32_H +#define crypto_verify_32_H + +#define crypto_verify_32_ref_BYTES 32 +#ifdef __cplusplus +#include <string> +extern "C" { +#endif +extern int crypto_verify_32_ref(const unsigned char *,const unsigned char *); +#ifdef __cplusplus +} +#endif + +#define crypto_verify_32 crypto_verify_32_ref +#define crypto_verify_32_BYTES crypto_verify_32_ref_BYTES +#define crypto_verify_32_IMPLEMENTATION "crypto_verify/32/ref" +#ifndef crypto_verify_32_ref_VERSION +#define crypto_verify_32_ref_VERSION "-" +#endif +#define crypto_verify_32_VERSION crypto_verify_32_ref_VERSION + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/nacl_sha512/blocks.c b/libs/libaxolotl/src/curve25519/ed25519/nacl_sha512/blocks.c new file mode 100644 index 0000000000..1c5f23d5cc --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/nacl_sha512/blocks.c @@ -0,0 +1,238 @@ +#include <stdint.h> +typedef uint64_t uint64; + +static uint64 load_bigendian(const unsigned char *x) +{ + return + (uint64) (x[7]) \ + | (((uint64) (x[6])) << 8) \ + | (((uint64) (x[5])) << 16) \ + | (((uint64) (x[4])) << 24) \ + | (((uint64) (x[3])) << 32) \ + | (((uint64) (x[2])) << 40) \ + | (((uint64) (x[1])) << 48) \ + | (((uint64) (x[0])) << 56) + ; +} + +static void store_bigendian(unsigned char *x,uint64 u) +{ + x[7] = u; u >>= 8; + x[6] = u; u >>= 8; + x[5] = u; u >>= 8; + x[4] = u; u >>= 8; + x[3] = u; u >>= 8; + x[2] = u; u >>= 8; + x[1] = u; u >>= 8; + x[0] = u; +} + +#define SHR(x,c) ((x) >> (c)) +#define ROTR(x,c) (((x) >> (c)) | ((x) << (64 - (c)))) + +#define Ch(x,y,z) ((x & y) ^ (~x & z)) +#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z)) +#define Sigma0(x) (ROTR(x,28) ^ ROTR(x,34) ^ ROTR(x,39)) +#define Sigma1(x) (ROTR(x,14) ^ ROTR(x,18) ^ ROTR(x,41)) +#define sigma0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHR(x,7)) +#define sigma1(x) (ROTR(x,19) ^ ROTR(x,61) ^ SHR(x,6)) + +#define M(w0,w14,w9,w1) w0 = sigma1(w14) + w9 + sigma0(w1) + w0; + +#define EXPAND \ + M(w0 ,w14,w9 ,w1 ) \ + M(w1 ,w15,w10,w2 ) \ + M(w2 ,w0 ,w11,w3 ) \ + M(w3 ,w1 ,w12,w4 ) \ + M(w4 ,w2 ,w13,w5 ) \ + M(w5 ,w3 ,w14,w6 ) \ + M(w6 ,w4 ,w15,w7 ) \ + M(w7 ,w5 ,w0 ,w8 ) \ + M(w8 ,w6 ,w1 ,w9 ) \ + M(w9 ,w7 ,w2 ,w10) \ + M(w10,w8 ,w3 ,w11) \ + M(w11,w9 ,w4 ,w12) \ + M(w12,w10,w5 ,w13) \ + M(w13,w11,w6 ,w14) \ + M(w14,w12,w7 ,w15) \ + M(w15,w13,w8 ,w0 ) + +#define F(w,k) \ + T1 = h + Sigma1(e) + Ch(e,f,g) + k + w; \ + T2 = Sigma0(a) + Maj(a,b,c); \ + h = g; \ + g = f; \ + f = e; \ + e = d + T1; \ + d = c; \ + c = b; \ + b = a; \ + a = T1 + T2; + +int crypto_hashblocks_sha512(unsigned char *statebytes,const unsigned char *in,unsigned long long inlen) +{ + uint64 state[8]; + uint64 a; + uint64 b; + uint64 c; + uint64 d; + uint64 e; + uint64 f; + uint64 g; + uint64 h; + uint64 T1; + uint64 T2; + + a = load_bigendian(statebytes + 0); state[0] = a; + b = load_bigendian(statebytes + 8); state[1] = b; + c = load_bigendian(statebytes + 16); state[2] = c; + d = load_bigendian(statebytes + 24); state[3] = d; + e = load_bigendian(statebytes + 32); state[4] = e; + f = load_bigendian(statebytes + 40); state[5] = f; + g = load_bigendian(statebytes + 48); state[6] = g; + h = load_bigendian(statebytes + 56); state[7] = h; + + while (inlen >= 128) { + uint64 w0 = load_bigendian(in + 0); + uint64 w1 = load_bigendian(in + 8); + uint64 w2 = load_bigendian(in + 16); + uint64 w3 = load_bigendian(in + 24); + uint64 w4 = load_bigendian(in + 32); + uint64 w5 = load_bigendian(in + 40); + uint64 w6 = load_bigendian(in + 48); + uint64 w7 = load_bigendian(in + 56); + uint64 w8 = load_bigendian(in + 64); + uint64 w9 = load_bigendian(in + 72); + uint64 w10 = load_bigendian(in + 80); + uint64 w11 = load_bigendian(in + 88); + uint64 w12 = load_bigendian(in + 96); + uint64 w13 = load_bigendian(in + 104); + uint64 w14 = load_bigendian(in + 112); + uint64 w15 = load_bigendian(in + 120); + + F(w0 ,0x428a2f98d728ae22ULL) + F(w1 ,0x7137449123ef65cdULL) + F(w2 ,0xb5c0fbcfec4d3b2fULL) + F(w3 ,0xe9b5dba58189dbbcULL) + F(w4 ,0x3956c25bf348b538ULL) + F(w5 ,0x59f111f1b605d019ULL) + F(w6 ,0x923f82a4af194f9bULL) + F(w7 ,0xab1c5ed5da6d8118ULL) + F(w8 ,0xd807aa98a3030242ULL) + F(w9 ,0x12835b0145706fbeULL) + F(w10,0x243185be4ee4b28cULL) + F(w11,0x550c7dc3d5ffb4e2ULL) + F(w12,0x72be5d74f27b896fULL) + F(w13,0x80deb1fe3b1696b1ULL) + F(w14,0x9bdc06a725c71235ULL) + F(w15,0xc19bf174cf692694ULL) + + EXPAND + + F(w0 ,0xe49b69c19ef14ad2ULL) + F(w1 ,0xefbe4786384f25e3ULL) + F(w2 ,0x0fc19dc68b8cd5b5ULL) + F(w3 ,0x240ca1cc77ac9c65ULL) + F(w4 ,0x2de92c6f592b0275ULL) + F(w5 ,0x4a7484aa6ea6e483ULL) + F(w6 ,0x5cb0a9dcbd41fbd4ULL) + F(w7 ,0x76f988da831153b5ULL) + F(w8 ,0x983e5152ee66dfabULL) + F(w9 ,0xa831c66d2db43210ULL) + F(w10,0xb00327c898fb213fULL) + F(w11,0xbf597fc7beef0ee4ULL) + F(w12,0xc6e00bf33da88fc2ULL) + F(w13,0xd5a79147930aa725ULL) + F(w14,0x06ca6351e003826fULL) + F(w15,0x142929670a0e6e70ULL) + + EXPAND + + F(w0 ,0x27b70a8546d22ffcULL) + F(w1 ,0x2e1b21385c26c926ULL) + F(w2 ,0x4d2c6dfc5ac42aedULL) + F(w3 ,0x53380d139d95b3dfULL) + F(w4 ,0x650a73548baf63deULL) + F(w5 ,0x766a0abb3c77b2a8ULL) + F(w6 ,0x81c2c92e47edaee6ULL) + F(w7 ,0x92722c851482353bULL) + F(w8 ,0xa2bfe8a14cf10364ULL) + F(w9 ,0xa81a664bbc423001ULL) + F(w10,0xc24b8b70d0f89791ULL) + F(w11,0xc76c51a30654be30ULL) + F(w12,0xd192e819d6ef5218ULL) + F(w13,0xd69906245565a910ULL) + F(w14,0xf40e35855771202aULL) + F(w15,0x106aa07032bbd1b8ULL) + + EXPAND + + F(w0 ,0x19a4c116b8d2d0c8ULL) + F(w1 ,0x1e376c085141ab53ULL) + F(w2 ,0x2748774cdf8eeb99ULL) + F(w3 ,0x34b0bcb5e19b48a8ULL) + F(w4 ,0x391c0cb3c5c95a63ULL) + F(w5 ,0x4ed8aa4ae3418acbULL) + F(w6 ,0x5b9cca4f7763e373ULL) + F(w7 ,0x682e6ff3d6b2b8a3ULL) + F(w8 ,0x748f82ee5defb2fcULL) + F(w9 ,0x78a5636f43172f60ULL) + F(w10,0x84c87814a1f0ab72ULL) + F(w11,0x8cc702081a6439ecULL) + F(w12,0x90befffa23631e28ULL) + F(w13,0xa4506cebde82bde9ULL) + F(w14,0xbef9a3f7b2c67915ULL) + F(w15,0xc67178f2e372532bULL) + + EXPAND + + F(w0 ,0xca273eceea26619cULL) + F(w1 ,0xd186b8c721c0c207ULL) + F(w2 ,0xeada7dd6cde0eb1eULL) + F(w3 ,0xf57d4f7fee6ed178ULL) + F(w4 ,0x06f067aa72176fbaULL) + F(w5 ,0x0a637dc5a2c898a6ULL) + F(w6 ,0x113f9804bef90daeULL) + F(w7 ,0x1b710b35131c471bULL) + F(w8 ,0x28db77f523047d84ULL) + F(w9 ,0x32caab7b40c72493ULL) + F(w10,0x3c9ebe0a15c9bebcULL) + F(w11,0x431d67c49c100d4cULL) + F(w12,0x4cc5d4becb3e42b6ULL) + F(w13,0x597f299cfc657e2aULL) + F(w14,0x5fcb6fab3ad6faecULL) + F(w15,0x6c44198c4a475817ULL) + + a += state[0]; + b += state[1]; + c += state[2]; + d += state[3]; + e += state[4]; + f += state[5]; + g += state[6]; + h += state[7]; + + state[0] = a; + state[1] = b; + state[2] = c; + state[3] = d; + state[4] = e; + state[5] = f; + state[6] = g; + state[7] = h; + + in += 128; + inlen -= 128; + } + + store_bigendian(statebytes + 0,state[0]); + store_bigendian(statebytes + 8,state[1]); + store_bigendian(statebytes + 16,state[2]); + store_bigendian(statebytes + 24,state[3]); + store_bigendian(statebytes + 32,state[4]); + store_bigendian(statebytes + 40,state[5]); + store_bigendian(statebytes + 48,state[6]); + store_bigendian(statebytes + 56,state[7]); + + return 0; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/nacl_sha512/hash.c b/libs/libaxolotl/src/curve25519/ed25519/nacl_sha512/hash.c new file mode 100644 index 0000000000..8934cc18a8 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/nacl_sha512/hash.c @@ -0,0 +1,71 @@ +/* +20080913 +D. J. Bernstein +Public domain. +*/ + +#include <stdint.h> +typedef uint64_t uint64; + +extern int crypto_hashblocks_sha512(unsigned char *statebytes,const unsigned char *in,unsigned long long inlen); + +#define blocks crypto_hashblocks_sha512 + +static const unsigned char iv[64] = { + 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08, + 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b, + 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b, + 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1, + 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1, + 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f, + 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b, + 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79 +} ; + +int crypto_hash_sha512(unsigned char *out,const unsigned char *in,unsigned long long inlen) +{ + unsigned char h[64]; + unsigned char padded[256]; + int i; + unsigned long long bytes = inlen; + + for (i = 0;i < 64;++i) h[i] = iv[i]; + + blocks(h,in,inlen); + in += inlen; + inlen &= 127; + in -= inlen; + + for (i = 0;i < inlen;++i) padded[i] = in[i]; + padded[inlen] = 0x80; + + if (inlen < 112) { + for (i = inlen + 1;i < 119;++i) padded[i] = 0; + padded[119] = bytes >> 61; + padded[120] = bytes >> 53; + padded[121] = bytes >> 45; + padded[122] = bytes >> 37; + padded[123] = bytes >> 29; + padded[124] = bytes >> 21; + padded[125] = bytes >> 13; + padded[126] = bytes >> 5; + padded[127] = bytes << 3; + blocks(h,padded,128); + } else { + for (i = inlen + 1;i < 247;++i) padded[i] = 0; + padded[247] = bytes >> 61; + padded[248] = bytes >> 53; + padded[249] = bytes >> 45; + padded[250] = bytes >> 37; + padded[251] = bytes >> 29; + padded[252] = bytes >> 21; + padded[253] = bytes >> 13; + padded[254] = bytes >> 5; + padded[255] = bytes << 3; + blocks(h,padded,256); + } + + for (i = 0;i < 64;++i) out[i] = h[i]; + + return 0; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/open.c b/libs/libaxolotl/src/curve25519/ed25519/open.c new file mode 100644 index 0000000000..1ec4cd2bfd --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/open.c @@ -0,0 +1,48 @@ +#include <string.h> +#include "crypto_sign.h" +#include "crypto_hash_sha512.h" +#include "crypto_verify_32.h" +#include "ge.h" +#include "sc.h" + +int crypto_sign_open( + unsigned char *m,unsigned long long *mlen, + const unsigned char *sm,unsigned long long smlen, + const unsigned char *pk +) +{ + unsigned char pkcopy[32]; + unsigned char rcopy[32]; + unsigned char scopy[32]; + unsigned char h[64]; + unsigned char rcheck[32]; + ge_p3 A; + ge_p2 R; + + if (smlen < 64) goto badsig; + if (sm[63] & 224) goto badsig; + if (ge_frombytes_negate_vartime(&A,pk) != 0) goto badsig; + + memmove(pkcopy,pk,32); + memmove(rcopy,sm,32); + memmove(scopy,sm + 32,32); + + memmove(m,sm,smlen); + memmove(m + 32,pkcopy,32); + crypto_hash_sha512(h,m,smlen); + sc_reduce(h); + + ge_double_scalarmult_vartime(&R,h,&A,scopy); + ge_tobytes(rcheck,&R); + if (crypto_verify_32(rcheck,rcopy) == 0) { + memmove(m,m + 64,smlen - 64); + memset(m + smlen - 64,0,64); + *mlen = smlen - 64; + return 0; + } + +badsig: + *mlen = -1; + memset(m,0,smlen); + return -1; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/pow22523.h b/libs/libaxolotl/src/curve25519/ed25519/pow22523.h new file mode 100644 index 0000000000..60ffe0d34c --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/pow22523.h @@ -0,0 +1,160 @@ + +/* qhasm: fe z1 */ + +/* qhasm: fe z2 */ + +/* qhasm: fe z8 */ + +/* qhasm: fe z9 */ + +/* qhasm: fe z11 */ + +/* qhasm: fe z22 */ + +/* qhasm: fe z_5_0 */ + +/* qhasm: fe z_10_5 */ + +/* qhasm: fe z_10_0 */ + +/* qhasm: fe z_20_10 */ + +/* qhasm: fe z_20_0 */ + +/* qhasm: fe z_40_20 */ + +/* qhasm: fe z_40_0 */ + +/* qhasm: fe z_50_10 */ + +/* qhasm: fe z_50_0 */ + +/* qhasm: fe z_100_50 */ + +/* qhasm: fe z_100_0 */ + +/* qhasm: fe z_200_100 */ + +/* qhasm: fe z_200_0 */ + +/* qhasm: fe z_250_50 */ + +/* qhasm: fe z_250_0 */ + +/* qhasm: fe z_252_2 */ + +/* qhasm: fe z_252_3 */ + +/* qhasm: enter pow22523 */ + +/* qhasm: z2 = z1^2^1 */ +/* asm 1: fe_sq(>z2=fe#1,<z1=fe#11); for (i = 1;i < 1;++i) fe_sq(>z2=fe#1,>z2=fe#1); */ +/* asm 2: fe_sq(>z2=t0,<z1=z); for (i = 1;i < 1;++i) fe_sq(>z2=t0,>z2=t0); */ +fe_sq(t0,z); for (i = 1;i < 1;++i) fe_sq(t0,t0); + +/* qhasm: z8 = z2^2^2 */ +/* asm 1: fe_sq(>z8=fe#2,<z2=fe#1); for (i = 1;i < 2;++i) fe_sq(>z8=fe#2,>z8=fe#2); */ +/* asm 2: fe_sq(>z8=t1,<z2=t0); for (i = 1;i < 2;++i) fe_sq(>z8=t1,>z8=t1); */ +fe_sq(t1,t0); for (i = 1;i < 2;++i) fe_sq(t1,t1); + +/* qhasm: z9 = z1*z8 */ +/* asm 1: fe_mul(>z9=fe#2,<z1=fe#11,<z8=fe#2); */ +/* asm 2: fe_mul(>z9=t1,<z1=z,<z8=t1); */ +fe_mul(t1,z,t1); + +/* qhasm: z11 = z2*z9 */ +/* asm 1: fe_mul(>z11=fe#1,<z2=fe#1,<z9=fe#2); */ +/* asm 2: fe_mul(>z11=t0,<z2=t0,<z9=t1); */ +fe_mul(t0,t0,t1); + +/* qhasm: z22 = z11^2^1 */ +/* asm 1: fe_sq(>z22=fe#1,<z11=fe#1); for (i = 1;i < 1;++i) fe_sq(>z22=fe#1,>z22=fe#1); */ +/* asm 2: fe_sq(>z22=t0,<z11=t0); for (i = 1;i < 1;++i) fe_sq(>z22=t0,>z22=t0); */ +fe_sq(t0,t0); for (i = 1;i < 1;++i) fe_sq(t0,t0); + +/* qhasm: z_5_0 = z9*z22 */ +/* asm 1: fe_mul(>z_5_0=fe#1,<z9=fe#2,<z22=fe#1); */ +/* asm 2: fe_mul(>z_5_0=t0,<z9=t1,<z22=t0); */ +fe_mul(t0,t1,t0); + +/* qhasm: z_10_5 = z_5_0^2^5 */ +/* asm 1: fe_sq(>z_10_5=fe#2,<z_5_0=fe#1); for (i = 1;i < 5;++i) fe_sq(>z_10_5=fe#2,>z_10_5=fe#2); */ +/* asm 2: fe_sq(>z_10_5=t1,<z_5_0=t0); for (i = 1;i < 5;++i) fe_sq(>z_10_5=t1,>z_10_5=t1); */ +fe_sq(t1,t0); for (i = 1;i < 5;++i) fe_sq(t1,t1); + +/* qhasm: z_10_0 = z_10_5*z_5_0 */ +/* asm 1: fe_mul(>z_10_0=fe#1,<z_10_5=fe#2,<z_5_0=fe#1); */ +/* asm 2: fe_mul(>z_10_0=t0,<z_10_5=t1,<z_5_0=t0); */ +fe_mul(t0,t1,t0); + +/* qhasm: z_20_10 = z_10_0^2^10 */ +/* asm 1: fe_sq(>z_20_10=fe#2,<z_10_0=fe#1); for (i = 1;i < 10;++i) fe_sq(>z_20_10=fe#2,>z_20_10=fe#2); */ +/* asm 2: fe_sq(>z_20_10=t1,<z_10_0=t0); for (i = 1;i < 10;++i) fe_sq(>z_20_10=t1,>z_20_10=t1); */ +fe_sq(t1,t0); for (i = 1;i < 10;++i) fe_sq(t1,t1); + +/* qhasm: z_20_0 = z_20_10*z_10_0 */ +/* asm 1: fe_mul(>z_20_0=fe#2,<z_20_10=fe#2,<z_10_0=fe#1); */ +/* asm 2: fe_mul(>z_20_0=t1,<z_20_10=t1,<z_10_0=t0); */ +fe_mul(t1,t1,t0); + +/* qhasm: z_40_20 = z_20_0^2^20 */ +/* asm 1: fe_sq(>z_40_20=fe#3,<z_20_0=fe#2); for (i = 1;i < 20;++i) fe_sq(>z_40_20=fe#3,>z_40_20=fe#3); */ +/* asm 2: fe_sq(>z_40_20=t2,<z_20_0=t1); for (i = 1;i < 20;++i) fe_sq(>z_40_20=t2,>z_40_20=t2); */ +fe_sq(t2,t1); for (i = 1;i < 20;++i) fe_sq(t2,t2); + +/* qhasm: z_40_0 = z_40_20*z_20_0 */ +/* asm 1: fe_mul(>z_40_0=fe#2,<z_40_20=fe#3,<z_20_0=fe#2); */ +/* asm 2: fe_mul(>z_40_0=t1,<z_40_20=t2,<z_20_0=t1); */ +fe_mul(t1,t2,t1); + +/* qhasm: z_50_10 = z_40_0^2^10 */ +/* asm 1: fe_sq(>z_50_10=fe#2,<z_40_0=fe#2); for (i = 1;i < 10;++i) fe_sq(>z_50_10=fe#2,>z_50_10=fe#2); */ +/* asm 2: fe_sq(>z_50_10=t1,<z_40_0=t1); for (i = 1;i < 10;++i) fe_sq(>z_50_10=t1,>z_50_10=t1); */ +fe_sq(t1,t1); for (i = 1;i < 10;++i) fe_sq(t1,t1); + +/* qhasm: z_50_0 = z_50_10*z_10_0 */ +/* asm 1: fe_mul(>z_50_0=fe#1,<z_50_10=fe#2,<z_10_0=fe#1); */ +/* asm 2: fe_mul(>z_50_0=t0,<z_50_10=t1,<z_10_0=t0); */ +fe_mul(t0,t1,t0); + +/* qhasm: z_100_50 = z_50_0^2^50 */ +/* asm 1: fe_sq(>z_100_50=fe#2,<z_50_0=fe#1); for (i = 1;i < 50;++i) fe_sq(>z_100_50=fe#2,>z_100_50=fe#2); */ +/* asm 2: fe_sq(>z_100_50=t1,<z_50_0=t0); for (i = 1;i < 50;++i) fe_sq(>z_100_50=t1,>z_100_50=t1); */ +fe_sq(t1,t0); for (i = 1;i < 50;++i) fe_sq(t1,t1); + +/* qhasm: z_100_0 = z_100_50*z_50_0 */ +/* asm 1: fe_mul(>z_100_0=fe#2,<z_100_50=fe#2,<z_50_0=fe#1); */ +/* asm 2: fe_mul(>z_100_0=t1,<z_100_50=t1,<z_50_0=t0); */ +fe_mul(t1,t1,t0); + +/* qhasm: z_200_100 = z_100_0^2^100 */ +/* asm 1: fe_sq(>z_200_100=fe#3,<z_100_0=fe#2); for (i = 1;i < 100;++i) fe_sq(>z_200_100=fe#3,>z_200_100=fe#3); */ +/* asm 2: fe_sq(>z_200_100=t2,<z_100_0=t1); for (i = 1;i < 100;++i) fe_sq(>z_200_100=t2,>z_200_100=t2); */ +fe_sq(t2,t1); for (i = 1;i < 100;++i) fe_sq(t2,t2); + +/* qhasm: z_200_0 = z_200_100*z_100_0 */ +/* asm 1: fe_mul(>z_200_0=fe#2,<z_200_100=fe#3,<z_100_0=fe#2); */ +/* asm 2: fe_mul(>z_200_0=t1,<z_200_100=t2,<z_100_0=t1); */ +fe_mul(t1,t2,t1); + +/* qhasm: z_250_50 = z_200_0^2^50 */ +/* asm 1: fe_sq(>z_250_50=fe#2,<z_200_0=fe#2); for (i = 1;i < 50;++i) fe_sq(>z_250_50=fe#2,>z_250_50=fe#2); */ +/* asm 2: fe_sq(>z_250_50=t1,<z_200_0=t1); for (i = 1;i < 50;++i) fe_sq(>z_250_50=t1,>z_250_50=t1); */ +fe_sq(t1,t1); for (i = 1;i < 50;++i) fe_sq(t1,t1); + +/* qhasm: z_250_0 = z_250_50*z_50_0 */ +/* asm 1: fe_mul(>z_250_0=fe#1,<z_250_50=fe#2,<z_50_0=fe#1); */ +/* asm 2: fe_mul(>z_250_0=t0,<z_250_50=t1,<z_50_0=t0); */ +fe_mul(t0,t1,t0); + +/* qhasm: z_252_2 = z_250_0^2^2 */ +/* asm 1: fe_sq(>z_252_2=fe#1,<z_250_0=fe#1); for (i = 1;i < 2;++i) fe_sq(>z_252_2=fe#1,>z_252_2=fe#1); */ +/* asm 2: fe_sq(>z_252_2=t0,<z_250_0=t0); for (i = 1;i < 2;++i) fe_sq(>z_252_2=t0,>z_252_2=t0); */ +fe_sq(t0,t0); for (i = 1;i < 2;++i) fe_sq(t0,t0); + +/* qhasm: z_252_3 = z_252_2*z1 */ +/* asm 1: fe_mul(>z_252_3=fe#12,<z_252_2=fe#1,<z1=fe#11); */ +/* asm 2: fe_mul(>z_252_3=out,<z_252_2=t0,<z1=z); */ +fe_mul(out,t0,z); + +/* qhasm: return */ diff --git a/libs/libaxolotl/src/curve25519/ed25519/pow225521.h b/libs/libaxolotl/src/curve25519/ed25519/pow225521.h new file mode 100644 index 0000000000..109df779a2 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/pow225521.h @@ -0,0 +1,160 @@ + +/* qhasm: fe z1 */ + +/* qhasm: fe z2 */ + +/* qhasm: fe z8 */ + +/* qhasm: fe z9 */ + +/* qhasm: fe z11 */ + +/* qhasm: fe z22 */ + +/* qhasm: fe z_5_0 */ + +/* qhasm: fe z_10_5 */ + +/* qhasm: fe z_10_0 */ + +/* qhasm: fe z_20_10 */ + +/* qhasm: fe z_20_0 */ + +/* qhasm: fe z_40_20 */ + +/* qhasm: fe z_40_0 */ + +/* qhasm: fe z_50_10 */ + +/* qhasm: fe z_50_0 */ + +/* qhasm: fe z_100_50 */ + +/* qhasm: fe z_100_0 */ + +/* qhasm: fe z_200_100 */ + +/* qhasm: fe z_200_0 */ + +/* qhasm: fe z_250_50 */ + +/* qhasm: fe z_250_0 */ + +/* qhasm: fe z_255_5 */ + +/* qhasm: fe z_255_21 */ + +/* qhasm: enter pow225521 */ + +/* qhasm: z2 = z1^2^1 */ +/* asm 1: fe_sq(>z2=fe#1,<z1=fe#11); for (i = 1;i < 1;++i) fe_sq(>z2=fe#1,>z2=fe#1); */ +/* asm 2: fe_sq(>z2=t0,<z1=z); for (i = 1;i < 1;++i) fe_sq(>z2=t0,>z2=t0); */ +fe_sq(t0,z); for (i = 1;i < 1;++i) fe_sq(t0,t0); + +/* qhasm: z8 = z2^2^2 */ +/* asm 1: fe_sq(>z8=fe#2,<z2=fe#1); for (i = 1;i < 2;++i) fe_sq(>z8=fe#2,>z8=fe#2); */ +/* asm 2: fe_sq(>z8=t1,<z2=t0); for (i = 1;i < 2;++i) fe_sq(>z8=t1,>z8=t1); */ +fe_sq(t1,t0); for (i = 1;i < 2;++i) fe_sq(t1,t1); + +/* qhasm: z9 = z1*z8 */ +/* asm 1: fe_mul(>z9=fe#2,<z1=fe#11,<z8=fe#2); */ +/* asm 2: fe_mul(>z9=t1,<z1=z,<z8=t1); */ +fe_mul(t1,z,t1); + +/* qhasm: z11 = z2*z9 */ +/* asm 1: fe_mul(>z11=fe#1,<z2=fe#1,<z9=fe#2); */ +/* asm 2: fe_mul(>z11=t0,<z2=t0,<z9=t1); */ +fe_mul(t0,t0,t1); + +/* qhasm: z22 = z11^2^1 */ +/* asm 1: fe_sq(>z22=fe#3,<z11=fe#1); for (i = 1;i < 1;++i) fe_sq(>z22=fe#3,>z22=fe#3); */ +/* asm 2: fe_sq(>z22=t2,<z11=t0); for (i = 1;i < 1;++i) fe_sq(>z22=t2,>z22=t2); */ +fe_sq(t2,t0); for (i = 1;i < 1;++i) fe_sq(t2,t2); + +/* qhasm: z_5_0 = z9*z22 */ +/* asm 1: fe_mul(>z_5_0=fe#2,<z9=fe#2,<z22=fe#3); */ +/* asm 2: fe_mul(>z_5_0=t1,<z9=t1,<z22=t2); */ +fe_mul(t1,t1,t2); + +/* qhasm: z_10_5 = z_5_0^2^5 */ +/* asm 1: fe_sq(>z_10_5=fe#3,<z_5_0=fe#2); for (i = 1;i < 5;++i) fe_sq(>z_10_5=fe#3,>z_10_5=fe#3); */ +/* asm 2: fe_sq(>z_10_5=t2,<z_5_0=t1); for (i = 1;i < 5;++i) fe_sq(>z_10_5=t2,>z_10_5=t2); */ +fe_sq(t2,t1); for (i = 1;i < 5;++i) fe_sq(t2,t2); + +/* qhasm: z_10_0 = z_10_5*z_5_0 */ +/* asm 1: fe_mul(>z_10_0=fe#2,<z_10_5=fe#3,<z_5_0=fe#2); */ +/* asm 2: fe_mul(>z_10_0=t1,<z_10_5=t2,<z_5_0=t1); */ +fe_mul(t1,t2,t1); + +/* qhasm: z_20_10 = z_10_0^2^10 */ +/* asm 1: fe_sq(>z_20_10=fe#3,<z_10_0=fe#2); for (i = 1;i < 10;++i) fe_sq(>z_20_10=fe#3,>z_20_10=fe#3); */ +/* asm 2: fe_sq(>z_20_10=t2,<z_10_0=t1); for (i = 1;i < 10;++i) fe_sq(>z_20_10=t2,>z_20_10=t2); */ +fe_sq(t2,t1); for (i = 1;i < 10;++i) fe_sq(t2,t2); + +/* qhasm: z_20_0 = z_20_10*z_10_0 */ +/* asm 1: fe_mul(>z_20_0=fe#3,<z_20_10=fe#3,<z_10_0=fe#2); */ +/* asm 2: fe_mul(>z_20_0=t2,<z_20_10=t2,<z_10_0=t1); */ +fe_mul(t2,t2,t1); + +/* qhasm: z_40_20 = z_20_0^2^20 */ +/* asm 1: fe_sq(>z_40_20=fe#4,<z_20_0=fe#3); for (i = 1;i < 20;++i) fe_sq(>z_40_20=fe#4,>z_40_20=fe#4); */ +/* asm 2: fe_sq(>z_40_20=t3,<z_20_0=t2); for (i = 1;i < 20;++i) fe_sq(>z_40_20=t3,>z_40_20=t3); */ +fe_sq(t3,t2); for (i = 1;i < 20;++i) fe_sq(t3,t3); + +/* qhasm: z_40_0 = z_40_20*z_20_0 */ +/* asm 1: fe_mul(>z_40_0=fe#3,<z_40_20=fe#4,<z_20_0=fe#3); */ +/* asm 2: fe_mul(>z_40_0=t2,<z_40_20=t3,<z_20_0=t2); */ +fe_mul(t2,t3,t2); + +/* qhasm: z_50_10 = z_40_0^2^10 */ +/* asm 1: fe_sq(>z_50_10=fe#3,<z_40_0=fe#3); for (i = 1;i < 10;++i) fe_sq(>z_50_10=fe#3,>z_50_10=fe#3); */ +/* asm 2: fe_sq(>z_50_10=t2,<z_40_0=t2); for (i = 1;i < 10;++i) fe_sq(>z_50_10=t2,>z_50_10=t2); */ +fe_sq(t2,t2); for (i = 1;i < 10;++i) fe_sq(t2,t2); + +/* qhasm: z_50_0 = z_50_10*z_10_0 */ +/* asm 1: fe_mul(>z_50_0=fe#2,<z_50_10=fe#3,<z_10_0=fe#2); */ +/* asm 2: fe_mul(>z_50_0=t1,<z_50_10=t2,<z_10_0=t1); */ +fe_mul(t1,t2,t1); + +/* qhasm: z_100_50 = z_50_0^2^50 */ +/* asm 1: fe_sq(>z_100_50=fe#3,<z_50_0=fe#2); for (i = 1;i < 50;++i) fe_sq(>z_100_50=fe#3,>z_100_50=fe#3); */ +/* asm 2: fe_sq(>z_100_50=t2,<z_50_0=t1); for (i = 1;i < 50;++i) fe_sq(>z_100_50=t2,>z_100_50=t2); */ +fe_sq(t2,t1); for (i = 1;i < 50;++i) fe_sq(t2,t2); + +/* qhasm: z_100_0 = z_100_50*z_50_0 */ +/* asm 1: fe_mul(>z_100_0=fe#3,<z_100_50=fe#3,<z_50_0=fe#2); */ +/* asm 2: fe_mul(>z_100_0=t2,<z_100_50=t2,<z_50_0=t1); */ +fe_mul(t2,t2,t1); + +/* qhasm: z_200_100 = z_100_0^2^100 */ +/* asm 1: fe_sq(>z_200_100=fe#4,<z_100_0=fe#3); for (i = 1;i < 100;++i) fe_sq(>z_200_100=fe#4,>z_200_100=fe#4); */ +/* asm 2: fe_sq(>z_200_100=t3,<z_100_0=t2); for (i = 1;i < 100;++i) fe_sq(>z_200_100=t3,>z_200_100=t3); */ +fe_sq(t3,t2); for (i = 1;i < 100;++i) fe_sq(t3,t3); + +/* qhasm: z_200_0 = z_200_100*z_100_0 */ +/* asm 1: fe_mul(>z_200_0=fe#3,<z_200_100=fe#4,<z_100_0=fe#3); */ +/* asm 2: fe_mul(>z_200_0=t2,<z_200_100=t3,<z_100_0=t2); */ +fe_mul(t2,t3,t2); + +/* qhasm: z_250_50 = z_200_0^2^50 */ +/* asm 1: fe_sq(>z_250_50=fe#3,<z_200_0=fe#3); for (i = 1;i < 50;++i) fe_sq(>z_250_50=fe#3,>z_250_50=fe#3); */ +/* asm 2: fe_sq(>z_250_50=t2,<z_200_0=t2); for (i = 1;i < 50;++i) fe_sq(>z_250_50=t2,>z_250_50=t2); */ +fe_sq(t2,t2); for (i = 1;i < 50;++i) fe_sq(t2,t2); + +/* qhasm: z_250_0 = z_250_50*z_50_0 */ +/* asm 1: fe_mul(>z_250_0=fe#2,<z_250_50=fe#3,<z_50_0=fe#2); */ +/* asm 2: fe_mul(>z_250_0=t1,<z_250_50=t2,<z_50_0=t1); */ +fe_mul(t1,t2,t1); + +/* qhasm: z_255_5 = z_250_0^2^5 */ +/* asm 1: fe_sq(>z_255_5=fe#2,<z_250_0=fe#2); for (i = 1;i < 5;++i) fe_sq(>z_255_5=fe#2,>z_255_5=fe#2); */ +/* asm 2: fe_sq(>z_255_5=t1,<z_250_0=t1); for (i = 1;i < 5;++i) fe_sq(>z_255_5=t1,>z_255_5=t1); */ +fe_sq(t1,t1); for (i = 1;i < 5;++i) fe_sq(t1,t1); + +/* qhasm: z_255_21 = z_255_5*z11 */ +/* asm 1: fe_mul(>z_255_21=fe#12,<z_255_5=fe#2,<z11=fe#1); */ +/* asm 2: fe_mul(>z_255_21=out,<z_255_5=t1,<z11=t0); */ +fe_mul(out,t1,t0); + +/* qhasm: return */ diff --git a/libs/libaxolotl/src/curve25519/ed25519/sc.h b/libs/libaxolotl/src/curve25519/ed25519/sc.h new file mode 100644 index 0000000000..d32ed2e8ca --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/sc.h @@ -0,0 +1,15 @@ +#ifndef SC_H +#define SC_H + +/* +The set of scalars is \Z/l +where l = 2^252 + 27742317777372353535851937790883648493. +*/ + +#define sc_reduce crypto_sign_ed25519_ref10_sc_reduce +#define sc_muladd crypto_sign_ed25519_ref10_sc_muladd + +extern void sc_reduce(unsigned char *); +extern void sc_muladd(unsigned char *,const unsigned char *,const unsigned char *,const unsigned char *); + +#endif diff --git a/libs/libaxolotl/src/curve25519/ed25519/sc_muladd.c b/libs/libaxolotl/src/curve25519/ed25519/sc_muladd.c new file mode 100644 index 0000000000..6f1e9d02d6 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/sc_muladd.c @@ -0,0 +1,368 @@ +#include "sc.h" +#include "crypto_int64.h" +#include "crypto_uint32.h" +#include "crypto_uint64.h" + +static crypto_uint64 load_3(const unsigned char *in) +{ + crypto_uint64 result; + result = (crypto_uint64) in[0]; + result |= ((crypto_uint64) in[1]) << 8; + result |= ((crypto_uint64) in[2]) << 16; + return result; +} + +static crypto_uint64 load_4(const unsigned char *in) +{ + crypto_uint64 result; + result = (crypto_uint64) in[0]; + result |= ((crypto_uint64) in[1]) << 8; + result |= ((crypto_uint64) in[2]) << 16; + result |= ((crypto_uint64) in[3]) << 24; + return result; +} + +/* +Input: + a[0]+256*a[1]+...+256^31*a[31] = a + b[0]+256*b[1]+...+256^31*b[31] = b + c[0]+256*c[1]+...+256^31*c[31] = c + +Output: + s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l + where l = 2^252 + 27742317777372353535851937790883648493. +*/ + +void sc_muladd(unsigned char *s,const unsigned char *a,const unsigned char *b,const unsigned char *c) +{ + crypto_int64 a0 = 2097151 & load_3(a); + crypto_int64 a1 = 2097151 & (load_4(a + 2) >> 5); + crypto_int64 a2 = 2097151 & (load_3(a + 5) >> 2); + crypto_int64 a3 = 2097151 & (load_4(a + 7) >> 7); + crypto_int64 a4 = 2097151 & (load_4(a + 10) >> 4); + crypto_int64 a5 = 2097151 & (load_3(a + 13) >> 1); + crypto_int64 a6 = 2097151 & (load_4(a + 15) >> 6); + crypto_int64 a7 = 2097151 & (load_3(a + 18) >> 3); + crypto_int64 a8 = 2097151 & load_3(a + 21); + crypto_int64 a9 = 2097151 & (load_4(a + 23) >> 5); + crypto_int64 a10 = 2097151 & (load_3(a + 26) >> 2); + crypto_int64 a11 = (load_4(a + 28) >> 7); + crypto_int64 b0 = 2097151 & load_3(b); + crypto_int64 b1 = 2097151 & (load_4(b + 2) >> 5); + crypto_int64 b2 = 2097151 & (load_3(b + 5) >> 2); + crypto_int64 b3 = 2097151 & (load_4(b + 7) >> 7); + crypto_int64 b4 = 2097151 & (load_4(b + 10) >> 4); + crypto_int64 b5 = 2097151 & (load_3(b + 13) >> 1); + crypto_int64 b6 = 2097151 & (load_4(b + 15) >> 6); + crypto_int64 b7 = 2097151 & (load_3(b + 18) >> 3); + crypto_int64 b8 = 2097151 & load_3(b + 21); + crypto_int64 b9 = 2097151 & (load_4(b + 23) >> 5); + crypto_int64 b10 = 2097151 & (load_3(b + 26) >> 2); + crypto_int64 b11 = (load_4(b + 28) >> 7); + crypto_int64 c0 = 2097151 & load_3(c); + crypto_int64 c1 = 2097151 & (load_4(c + 2) >> 5); + crypto_int64 c2 = 2097151 & (load_3(c + 5) >> 2); + crypto_int64 c3 = 2097151 & (load_4(c + 7) >> 7); + crypto_int64 c4 = 2097151 & (load_4(c + 10) >> 4); + crypto_int64 c5 = 2097151 & (load_3(c + 13) >> 1); + crypto_int64 c6 = 2097151 & (load_4(c + 15) >> 6); + crypto_int64 c7 = 2097151 & (load_3(c + 18) >> 3); + crypto_int64 c8 = 2097151 & load_3(c + 21); + crypto_int64 c9 = 2097151 & (load_4(c + 23) >> 5); + crypto_int64 c10 = 2097151 & (load_3(c + 26) >> 2); + crypto_int64 c11 = (load_4(c + 28) >> 7); + crypto_int64 s0; + crypto_int64 s1; + crypto_int64 s2; + crypto_int64 s3; + crypto_int64 s4; + crypto_int64 s5; + crypto_int64 s6; + crypto_int64 s7; + crypto_int64 s8; + crypto_int64 s9; + crypto_int64 s10; + crypto_int64 s11; + crypto_int64 s12; + crypto_int64 s13; + crypto_int64 s14; + crypto_int64 s15; + crypto_int64 s16; + crypto_int64 s17; + crypto_int64 s18; + crypto_int64 s19; + crypto_int64 s20; + crypto_int64 s21; + crypto_int64 s22; + crypto_int64 s23; + crypto_int64 carry0; + crypto_int64 carry1; + crypto_int64 carry2; + crypto_int64 carry3; + crypto_int64 carry4; + crypto_int64 carry5; + crypto_int64 carry6; + crypto_int64 carry7; + crypto_int64 carry8; + crypto_int64 carry9; + crypto_int64 carry10; + crypto_int64 carry11; + crypto_int64 carry12; + crypto_int64 carry13; + crypto_int64 carry14; + crypto_int64 carry15; + crypto_int64 carry16; + crypto_int64 carry17; + crypto_int64 carry18; + crypto_int64 carry19; + crypto_int64 carry20; + crypto_int64 carry21; + crypto_int64 carry22; + + s0 = c0 + a0*b0; + s1 = c1 + a0*b1 + a1*b0; + s2 = c2 + a0*b2 + a1*b1 + a2*b0; + s3 = c3 + a0*b3 + a1*b2 + a2*b1 + a3*b0; + s4 = c4 + a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0; + s5 = c5 + a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0; + s6 = c6 + a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0; + s7 = c7 + a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0; + s8 = c8 + a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0; + s9 = c9 + a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0; + s10 = c10 + a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0; + s11 = c11 + a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0; + s12 = a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1; + s13 = a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2; + s14 = a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3; + s15 = a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4; + s16 = a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5; + s17 = a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6; + s18 = a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7; + s19 = a8*b11 + a9*b10 + a10*b9 + a11*b8; + s20 = a9*b11 + a10*b10 + a11*b9; + s21 = a10*b11 + a11*b10; + s22 = a11*b11; + s23 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + carry18 = (s18 + (1<<20)) >> 21; s19 += carry18; s18 -= carry18 << 21; + carry20 = (s20 + (1<<20)) >> 21; s21 += carry20; s20 -= carry20 << 21; + carry22 = (s22 + (1<<20)) >> 21; s23 += carry22; s22 -= carry22 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + carry17 = (s17 + (1<<20)) >> 21; s18 += carry17; s17 -= carry17 << 21; + carry19 = (s19 + (1<<20)) >> 21; s20 += carry19; s19 -= carry19 << 21; + carry21 = (s21 + (1<<20)) >> 21; s22 += carry21; s21 -= carry21 << 21; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + s[0] = s0 >> 0; + s[1] = s0 >> 8; + s[2] = (s0 >> 16) | (s1 << 5); + s[3] = s1 >> 3; + s[4] = s1 >> 11; + s[5] = (s1 >> 19) | (s2 << 2); + s[6] = s2 >> 6; + s[7] = (s2 >> 14) | (s3 << 7); + s[8] = s3 >> 1; + s[9] = s3 >> 9; + s[10] = (s3 >> 17) | (s4 << 4); + s[11] = s4 >> 4; + s[12] = s4 >> 12; + s[13] = (s4 >> 20) | (s5 << 1); + s[14] = s5 >> 7; + s[15] = (s5 >> 15) | (s6 << 6); + s[16] = s6 >> 2; + s[17] = s6 >> 10; + s[18] = (s6 >> 18) | (s7 << 3); + s[19] = s7 >> 5; + s[20] = s7 >> 13; + s[21] = s8 >> 0; + s[22] = s8 >> 8; + s[23] = (s8 >> 16) | (s9 << 5); + s[24] = s9 >> 3; + s[25] = s9 >> 11; + s[26] = (s9 >> 19) | (s10 << 2); + s[27] = s10 >> 6; + s[28] = (s10 >> 14) | (s11 << 7); + s[29] = s11 >> 1; + s[30] = s11 >> 9; + s[31] = s11 >> 17; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/sc_reduce.c b/libs/libaxolotl/src/curve25519/ed25519/sc_reduce.c new file mode 100644 index 0000000000..d01f5a5737 --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/sc_reduce.c @@ -0,0 +1,275 @@ +#include "sc.h" +#include "crypto_int64.h" +#include "crypto_uint32.h" +#include "crypto_uint64.h" + +static crypto_uint64 load_3(const unsigned char *in) +{ + crypto_uint64 result; + result = (crypto_uint64) in[0]; + result |= ((crypto_uint64) in[1]) << 8; + result |= ((crypto_uint64) in[2]) << 16; + return result; +} + +static crypto_uint64 load_4(const unsigned char *in) +{ + crypto_uint64 result; + result = (crypto_uint64) in[0]; + result |= ((crypto_uint64) in[1]) << 8; + result |= ((crypto_uint64) in[2]) << 16; + result |= ((crypto_uint64) in[3]) << 24; + return result; +} + +/* +Input: + s[0]+256*s[1]+...+256^63*s[63] = s + +Output: + s[0]+256*s[1]+...+256^31*s[31] = s mod l + where l = 2^252 + 27742317777372353535851937790883648493. + Overwrites s in place. +*/ + +void sc_reduce(unsigned char *s) +{ + crypto_int64 s0 = 2097151 & load_3(s); + crypto_int64 s1 = 2097151 & (load_4(s + 2) >> 5); + crypto_int64 s2 = 2097151 & (load_3(s + 5) >> 2); + crypto_int64 s3 = 2097151 & (load_4(s + 7) >> 7); + crypto_int64 s4 = 2097151 & (load_4(s + 10) >> 4); + crypto_int64 s5 = 2097151 & (load_3(s + 13) >> 1); + crypto_int64 s6 = 2097151 & (load_4(s + 15) >> 6); + crypto_int64 s7 = 2097151 & (load_3(s + 18) >> 3); + crypto_int64 s8 = 2097151 & load_3(s + 21); + crypto_int64 s9 = 2097151 & (load_4(s + 23) >> 5); + crypto_int64 s10 = 2097151 & (load_3(s + 26) >> 2); + crypto_int64 s11 = 2097151 & (load_4(s + 28) >> 7); + crypto_int64 s12 = 2097151 & (load_4(s + 31) >> 4); + crypto_int64 s13 = 2097151 & (load_3(s + 34) >> 1); + crypto_int64 s14 = 2097151 & (load_4(s + 36) >> 6); + crypto_int64 s15 = 2097151 & (load_3(s + 39) >> 3); + crypto_int64 s16 = 2097151 & load_3(s + 42); + crypto_int64 s17 = 2097151 & (load_4(s + 44) >> 5); + crypto_int64 s18 = 2097151 & (load_3(s + 47) >> 2); + crypto_int64 s19 = 2097151 & (load_4(s + 49) >> 7); + crypto_int64 s20 = 2097151 & (load_4(s + 52) >> 4); + crypto_int64 s21 = 2097151 & (load_3(s + 55) >> 1); + crypto_int64 s22 = 2097151 & (load_4(s + 57) >> 6); + crypto_int64 s23 = (load_4(s + 60) >> 3); + crypto_int64 carry0; + crypto_int64 carry1; + crypto_int64 carry2; + crypto_int64 carry3; + crypto_int64 carry4; + crypto_int64 carry5; + crypto_int64 carry6; + crypto_int64 carry7; + crypto_int64 carry8; + crypto_int64 carry9; + crypto_int64 carry10; + crypto_int64 carry11; + crypto_int64 carry12; + crypto_int64 carry13; + crypto_int64 carry14; + crypto_int64 carry15; + crypto_int64 carry16; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + s[0] = s0 >> 0; + s[1] = s0 >> 8; + s[2] = (s0 >> 16) | (s1 << 5); + s[3] = s1 >> 3; + s[4] = s1 >> 11; + s[5] = (s1 >> 19) | (s2 << 2); + s[6] = s2 >> 6; + s[7] = (s2 >> 14) | (s3 << 7); + s[8] = s3 >> 1; + s[9] = s3 >> 9; + s[10] = (s3 >> 17) | (s4 << 4); + s[11] = s4 >> 4; + s[12] = s4 >> 12; + s[13] = (s4 >> 20) | (s5 << 1); + s[14] = s5 >> 7; + s[15] = (s5 >> 15) | (s6 << 6); + s[16] = s6 >> 2; + s[17] = s6 >> 10; + s[18] = (s6 >> 18) | (s7 << 3); + s[19] = s7 >> 5; + s[20] = s7 >> 13; + s[21] = s8 >> 0; + s[22] = s8 >> 8; + s[23] = (s8 >> 16) | (s9 << 5); + s[24] = s9 >> 3; + s[25] = s9 >> 11; + s[26] = (s9 >> 19) | (s10 << 2); + s[27] = s10 >> 6; + s[28] = (s10 >> 14) | (s11 << 7); + s[29] = s11 >> 1; + s[30] = s11 >> 9; + s[31] = s11 >> 17; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/sign.c b/libs/libaxolotl/src/curve25519/ed25519/sign.c new file mode 100644 index 0000000000..de53742a6c --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/sign.c @@ -0,0 +1,41 @@ +#include <string.h> +#include "crypto_sign.h" +#include "crypto_hash_sha512.h" +#include "ge.h" +#include "sc.h" + +int crypto_sign( + unsigned char *sm,unsigned long long *smlen, + const unsigned char *m,unsigned long long mlen, + const unsigned char *sk +) +{ + unsigned char pk[32]; + unsigned char az[64]; + unsigned char nonce[64]; + unsigned char hram[64]; + ge_p3 R; + + memmove(pk,sk + 32,32); + + crypto_hash_sha512(az,sk,32); + az[0] &= 248; + az[31] &= 63; + az[31] |= 64; + + *smlen = mlen + 64; + memmove(sm + 64,m,mlen); + memmove(sm + 32,az + 32,32); + crypto_hash_sha512(nonce,sm + 32,mlen + 32); + memmove(sm + 32,pk,32); + + sc_reduce(nonce); + ge_scalarmult_base(&R,nonce); + ge_p3_tobytes(sm,&R); + + crypto_hash_sha512(hram,sm,mlen + 64); + sc_reduce(hram); + sc_muladd(sm + 32,hram,az,nonce); + + return 0; +} diff --git a/libs/libaxolotl/src/curve25519/ed25519/sqrtm1.h b/libs/libaxolotl/src/curve25519/ed25519/sqrtm1.h new file mode 100644 index 0000000000..d8caa23b6a --- /dev/null +++ b/libs/libaxolotl/src/curve25519/ed25519/sqrtm1.h @@ -0,0 +1 @@ +-32595792,-7943725,9377950,3500415,12389472,-272473,-25146209,-2005654,326686,11406482 diff --git a/libs/libaxolotl/src/fingerprint.c b/libs/libaxolotl/src/fingerprint.c new file mode 100644 index 0000000000..f5a00248b2 --- /dev/null +++ b/libs/libaxolotl/src/fingerprint.c @@ -0,0 +1,629 @@ +#include "fingerprint.h" + +#include <assert.h> +#include <string.h> + +#include "axolotl_internal.h" +#include "FingerprintProtocol.pb-c.h" + +#define VERSION 0 + +struct fingerprint +{ + axolotl_type_base base; + displayable_fingerprint *displayable; + scannable_fingerprint *scannable; +}; + +struct displayable_fingerprint +{ + axolotl_type_base base; + char *local_fingerprint; + char *remote_fingerprint; + char *display_text; +}; + +struct scannable_fingerprint +{ + axolotl_type_base base; + uint32_t version; + char *local_stable_identifier; + ec_public_key *local_identity_key; + char *remote_stable_identifier; + ec_public_key *remote_identity_key; +}; + +struct fingerprint_generator +{ + int iterations; + axolotl_context *global_context; +}; + +static int fingerprint_generator_create_display_string(fingerprint_generator *generator, char **display_string, + const char *local_stable_identifier, ec_public_key *identity_key); + +int fingerprint_generator_create(fingerprint_generator **generator, int iterations, axolotl_context *global_context) +{ + fingerprint_generator *result_generator; + + assert(global_context); + + result_generator = malloc(sizeof(fingerprint_generator)); + if(!result_generator) { + return AX_ERR_NOMEM; + } + memset(result_generator, 0, sizeof(fingerprint_generator)); + + result_generator->iterations = iterations; + result_generator->global_context = global_context; + + *generator = result_generator; + return 0; +} + +int fingerprint_generator_create_for(fingerprint_generator *generator, + const char *local_stable_identifier, ec_public_key *local_identity_key, + const char *remote_stable_identifier, ec_public_key *remote_identity_key, + fingerprint **fingerprint_val) +{ + int result = 0; + fingerprint *result_fingerprint = 0; + displayable_fingerprint *displayable = 0; + char *displayable_local = 0; + char *displayable_remote = 0; + scannable_fingerprint *scannable = 0; + + result = fingerprint_generator_create_display_string(generator, &displayable_local, + local_stable_identifier, local_identity_key); + if(result < 0) { + goto complete; + } + + result = fingerprint_generator_create_display_string(generator, &displayable_remote, + remote_stable_identifier, remote_identity_key); + if(result < 0) { + goto complete; + } + + result = displayable_fingerprint_create(&displayable, displayable_local, displayable_remote); + if(result < 0) { + goto complete; + } + + result = scannable_fingerprint_create(&scannable, VERSION, + local_stable_identifier, local_identity_key, + remote_stable_identifier, remote_identity_key); + if(result < 0) { + goto complete; + } + + result = fingerprint_create(&result_fingerprint, displayable, scannable); + +complete: + if(displayable_local) { + free(displayable_local); + } + if(displayable_remote) { + free(displayable_remote); + } + AXOLOTL_UNREF(displayable); + AXOLOTL_UNREF(scannable); + if(result >= 0) { + *fingerprint_val = result_fingerprint; + } + return result; +} + +int fingerprint_generator_create_display_string(fingerprint_generator *generator, char **display_string, + const char *stable_identifier, ec_public_key *identity_key) +{ + int result = 0; + char *result_string = 0; + axolotl_buffer *identity_buffer = 0; + axolotl_buffer *hash_buffer = 0; + axolotl_buffer *hash_out_buffer = 0; + uint8_t *data = 0; + size_t len = 0; + int i = 0; + + assert(generator); + assert(stable_identifier); + assert(identity_key); + assert(generator->global_context->crypto_provider.sha512_digest_func); + + result = ec_public_key_serialize(&identity_buffer, identity_key); + if(result < 0) { + goto complete; + } + + len = 2 + axolotl_buffer_len(identity_buffer) + strlen(stable_identifier); + + hash_buffer = axolotl_buffer_alloc(len); + if(!hash_buffer) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(hash_buffer); + + memset(data, 0, len); + + data[0] = 0; + data[1] = (uint8_t)VERSION; + memcpy(data + 2, axolotl_buffer_data(identity_buffer), axolotl_buffer_len(identity_buffer)); + memcpy(data + 2 + axolotl_buffer_len(identity_buffer), stable_identifier, strlen(stable_identifier)); + + for(i = 0; i < generator->iterations; i++) { + result = axolotl_sha512_digest(generator->global_context, + &hash_out_buffer, + axolotl_buffer_data(hash_buffer), + axolotl_buffer_len(hash_buffer)); + if(result < 0) { + goto complete; + } + + axolotl_buffer_free(hash_buffer); + hash_buffer = hash_out_buffer; + hash_out_buffer = 0; + } + + data = axolotl_buffer_data(hash_buffer); + len = axolotl_buffer_len(hash_buffer); + + if(len < 30) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + result_string = malloc(31); + if(!result_string) { + result = AX_ERR_NOMEM; + goto complete; + } + + for(i = 0; i < 30; i += 5) { + uint64_t chunk = ((uint64_t)data[i] & 0xFFL) << 32 | + ((uint64_t)data[i + 1] & 0xFFL) << 24 | + ((uint64_t)data[i + 2] & 0xFFL) << 16 | + ((uint64_t)data[i + 3] & 0xFFL) << 8 | + ((uint64_t)data[i + 4] & 0xFFL); +#if _WINDOWS + sprintf_s(result_string + i, 6, "%05d", (int)(chunk % 100000)); +#else + snprintf(result_string + i, 6, "%05d", (int)(chunk % 100000)); +#endif + } + +complete: + axolotl_buffer_free(identity_buffer); + axolotl_buffer_free(hash_buffer); + axolotl_buffer_free(hash_out_buffer); + if(result >= 0) { + *display_string = result_string; + } + return result; +} + +void fingerprint_generator_free(fingerprint_generator *generator) +{ + if(generator) { + free(generator); + } +} + +int fingerprint_create(fingerprint **fingerprint_val, displayable_fingerprint *displayable, scannable_fingerprint *scannable) +{ + fingerprint *result = malloc(sizeof(fingerprint)); + if(!result) { + return AX_ERR_NOMEM; + } + + memset(result, 0, sizeof(fingerprint)); + AXOLOTL_INIT(result, fingerprint_destroy); + if(displayable) { + result->displayable = displayable; + AXOLOTL_REF(displayable); + } + if(scannable) { + result->scannable = scannable; + AXOLOTL_REF(scannable); + } + + *fingerprint_val = result; + + return 0; +} + +displayable_fingerprint *fingerprint_get_displayable(fingerprint *fingerprint_val) +{ + assert(fingerprint_val); + return fingerprint_val->displayable; +} + +scannable_fingerprint *fingerprint_get_scannable(fingerprint *fingerprint_val) +{ + assert(fingerprint_val); + return fingerprint_val->scannable; +} + +void fingerprint_destroy(axolotl_type_base *type) +{ + fingerprint *fingerprint_val = (fingerprint *)type; + AXOLOTL_UNREF(fingerprint_val->displayable); + AXOLOTL_UNREF(fingerprint_val->scannable); + free(fingerprint_val); +} + +int displayable_fingerprint_create(displayable_fingerprint **displayable, const char *local_fingerprint, const char *remote_fingerprint) +{ + int result = 0; + size_t local_len = 0; + size_t remote_len = 0; + displayable_fingerprint *result_displayable = 0; + char *display_text = 0; + + if(!local_fingerprint || !remote_fingerprint) { + return AX_ERR_INVAL; + } + + result_displayable = malloc(sizeof(displayable_fingerprint)); + if(!result_displayable) { + return AX_ERR_NOMEM; + } + + memset(result_displayable, 0, sizeof(displayable_fingerprint)); + AXOLOTL_INIT(result_displayable, displayable_fingerprint_destroy); + + result_displayable->local_fingerprint = strdup(local_fingerprint); + if(!result_displayable->local_fingerprint) { + result = AX_ERR_NOMEM; + goto complete; + } + + result_displayable->remote_fingerprint = strdup(remote_fingerprint); + if(!result_displayable->remote_fingerprint) { + result = AX_ERR_NOMEM; + goto complete; + } + + local_len = strlen(local_fingerprint); + remote_len = strlen(remote_fingerprint); + + display_text = malloc(local_len + remote_len + 1); + if(!display_text) { + result = AX_ERR_NOMEM; + goto complete; + } + + if(strcmp(local_fingerprint, remote_fingerprint) <= 0) { + memcpy(display_text, local_fingerprint, local_len); + memcpy(display_text + local_len, remote_fingerprint, remote_len + 1); + } + else { + memcpy(display_text, remote_fingerprint, remote_len); + memcpy(display_text + remote_len, local_fingerprint, local_len + 1); + } + + result_displayable->display_text = display_text; + +complete: + if(result < 0) { + AXOLOTL_UNREF(result_displayable); + } + else { + *displayable = result_displayable; + } + + return result; +} + +const char *displayable_fingerprint_local(displayable_fingerprint *displayable) +{ + assert(displayable); + return displayable->local_fingerprint; +} + +const char *displayable_fingerprint_remote(displayable_fingerprint *displayable) +{ + assert(displayable); + return displayable->remote_fingerprint; +} + +const char *displayable_fingerprint_text(displayable_fingerprint *displayable) +{ + assert(displayable); + return displayable->display_text; +} + +void displayable_fingerprint_destroy(axolotl_type_base *type) +{ + displayable_fingerprint *displayable = (displayable_fingerprint *)type; + if(displayable->local_fingerprint) { + free(displayable->local_fingerprint); + } + if(displayable->remote_fingerprint) { + free(displayable->remote_fingerprint); + } + if(displayable->display_text) { + free(displayable->display_text); + } + free(displayable); +} + +int scannable_fingerprint_create(scannable_fingerprint **scannable, + uint32_t version, + const char *local_stable_identifier, ec_public_key *local_identity_key, + const char *remote_stable_identifier, ec_public_key *remote_identity_key) +{ + int result = 0; + scannable_fingerprint *result_scannable = 0; + + if(!local_stable_identifier || !local_identity_key || + !remote_stable_identifier || !remote_identity_key) { + return AX_ERR_INVAL; + } + + result_scannable = malloc(sizeof(scannable_fingerprint)); + if(!result_scannable) { + return AX_ERR_NOMEM; + } + + memset(result_scannable, 0, sizeof(scannable_fingerprint)); + AXOLOTL_INIT(result_scannable, scannable_fingerprint_destroy); + + result_scannable->version = version; + + result_scannable->local_stable_identifier = strdup(local_stable_identifier); + if(!result_scannable->local_stable_identifier) { + result = AX_ERR_NOMEM; + goto complete; + } + + result_scannable->local_identity_key = local_identity_key; + AXOLOTL_REF(local_identity_key); + + result_scannable->remote_stable_identifier = strdup(remote_stable_identifier); + if(!result_scannable->remote_stable_identifier) { + result = AX_ERR_NOMEM; + goto complete; + } + + result_scannable->remote_identity_key = remote_identity_key; + AXOLOTL_REF(remote_identity_key); + +complete: + if(result < 0) { + AXOLOTL_UNREF(result_scannable); + } + else { + *scannable = result_scannable; + } + + return result; +} + +int scannable_fingerprint_serialize(axolotl_buffer **buffer, const scannable_fingerprint *scannable) +{ + int result = 0; + size_t result_size = 0; + axolotl_buffer *result_buf = 0; + Textsecure__CombinedFingerprint combined_fingerprint = TEXTSECURE__COMBINED_FINGERPRINT__INIT; + Textsecure__FingerprintData local_fingerprint = TEXTSECURE__FINGERPRINT_DATA__INIT; + Textsecure__FingerprintData remote_fingerprint = TEXTSECURE__FINGERPRINT_DATA__INIT; + size_t len = 0; + uint8_t *data = 0; + + combined_fingerprint.version = scannable->version; + combined_fingerprint.has_version = 1; + + if(scannable->local_stable_identifier && scannable->local_identity_key) { + axolotl_str_serialize_protobuf(&local_fingerprint.identifier, scannable->local_stable_identifier); + local_fingerprint.has_identifier = 1; + + result = ec_public_key_serialize_protobuf(&local_fingerprint.publickey, scannable->local_identity_key); + if(result < 0) { + goto complete; + } + local_fingerprint.has_publickey = 1; + + combined_fingerprint.localfingerprint = &local_fingerprint; + } + + if(scannable->remote_stable_identifier && scannable->remote_identity_key) { + axolotl_str_serialize_protobuf(&remote_fingerprint.identifier, scannable->remote_stable_identifier); + remote_fingerprint.has_identifier = 1; + + result = ec_public_key_serialize_protobuf(&remote_fingerprint.publickey, scannable->remote_identity_key); + if(result < 0) { + goto complete; + } + remote_fingerprint.has_publickey = 1; + + combined_fingerprint.remotefingerprint = &remote_fingerprint; + } + + len = textsecure__combined_fingerprint__get_packed_size(&combined_fingerprint); + + result_buf = axolotl_buffer_alloc(len); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(result_buf); + result_size = textsecure__combined_fingerprint__pack(&combined_fingerprint, data); + if(result_size != len) { + axolotl_buffer_free(result_buf); + result = AX_ERR_INVALID_PROTO_BUF; + result_buf = 0; + goto complete; + } + +complete: + if(local_fingerprint.publickey.data) { + free(local_fingerprint.publickey.data); + } + if(remote_fingerprint.publickey.data) { + free(remote_fingerprint.publickey.data); + } + if(result >= 0) { + *buffer = result_buf; + } + return result; +} + +int scannable_fingerprint_deserialize(scannable_fingerprint **scannable, const uint8_t *data, size_t len, axolotl_context *global_context) +{ + int result = 0; + Textsecure__CombinedFingerprint *combined_fingerprint = 0; + uint32_t version = 0; + char *local_stable_identifier = 0; + ec_public_key *local_identity_key = 0; + char *remote_stable_identifier = 0; + ec_public_key *remote_identity_key = 0; + + combined_fingerprint = textsecure__combined_fingerprint__unpack(0, len, data); + if(!combined_fingerprint) { + result = AX_ERR_INVALID_PROTO_BUF; + goto complete; + } + + if(combined_fingerprint->has_version) { + version = combined_fingerprint->version; + } + + if(combined_fingerprint->localfingerprint) { + if(combined_fingerprint->localfingerprint->has_identifier) { + local_stable_identifier = axolotl_str_deserialize_protobuf(&combined_fingerprint->localfingerprint->identifier); + if(!local_stable_identifier) { + result = AX_ERR_NOMEM; + goto complete; + } + } + if(combined_fingerprint->localfingerprint->has_publickey) { + result = curve_decode_point(&local_identity_key, + combined_fingerprint->localfingerprint->publickey.data, + combined_fingerprint->localfingerprint->publickey.len, + global_context); + if(result < 0) { + goto complete; + } + } + } + + if(combined_fingerprint->remotefingerprint) { + if(combined_fingerprint->remotefingerprint->has_identifier) { + remote_stable_identifier = axolotl_str_deserialize_protobuf(&combined_fingerprint->remotefingerprint->identifier); + if(!remote_stable_identifier) { + result = AX_ERR_NOMEM; + goto complete; + } + } + if(combined_fingerprint->remotefingerprint->has_publickey) { + result = curve_decode_point(&remote_identity_key, + combined_fingerprint->remotefingerprint->publickey.data, + combined_fingerprint->remotefingerprint->publickey.len, + global_context); + if(result < 0) { + goto complete; + } + } + } + + result = scannable_fingerprint_create(scannable, version, + local_stable_identifier, local_identity_key, + remote_stable_identifier, remote_identity_key); + +complete: + if(combined_fingerprint) { + textsecure__combined_fingerprint__free_unpacked(combined_fingerprint, 0); + } + if(local_stable_identifier) { + free(local_stable_identifier); + } + if(local_identity_key) { + AXOLOTL_UNREF(local_identity_key); + } + if(remote_stable_identifier) { + free(remote_stable_identifier); + } + if(remote_identity_key) { + AXOLOTL_UNREF(remote_identity_key); + } + return result; +} + +uint32_t scannable_fingerprint_get_version(scannable_fingerprint *scannable) +{ + assert(scannable); + return scannable->version; +} + +const char *scannable_fingerprint_get_local_stable_identifier(scannable_fingerprint *scannable) +{ + assert(scannable); + return scannable->local_stable_identifier; +} + +ec_public_key *scannable_fingerprint_get_local_identity_key(scannable_fingerprint *scannable) +{ + assert(scannable); + return scannable->local_identity_key; +} + +const char *scannable_fingerprint_get_remote_stable_identifier(scannable_fingerprint *scannable) +{ + assert(scannable); + return scannable->remote_stable_identifier; +} + +ec_public_key *scannable_fingerprint_get_remote_identity_key(scannable_fingerprint *scannable) +{ + assert(scannable); + return scannable->remote_identity_key; +} + +int scannable_fingerprint_compare(scannable_fingerprint *scannable, const scannable_fingerprint *other_scannable) +{ + if(!other_scannable->remote_identity_key || !other_scannable->local_identity_key || + other_scannable->version != scannable->version) { + return AX_ERR_FP_VERSION_MISMATCH; + } + + if(strcmp(scannable->local_stable_identifier, other_scannable->remote_stable_identifier) != 0) { + return AX_ERR_FP_IDENT_MISMATCH; + } + + if(strcmp(scannable->remote_stable_identifier, other_scannable->local_stable_identifier) != 0) { + return AX_ERR_FP_IDENT_MISMATCH; + } + + if(ec_public_key_compare(scannable->local_identity_key, other_scannable->remote_identity_key) != 0) { + return 0; + } + + if(ec_public_key_compare(scannable->remote_identity_key, other_scannable->local_identity_key) != 0) { + return 0; + } + + return 1; +} + +void scannable_fingerprint_destroy(axolotl_type_base *type) +{ + scannable_fingerprint *scannable = (scannable_fingerprint *)type; + + if(scannable->local_stable_identifier) { + free(scannable->local_stable_identifier); + } + + AXOLOTL_UNREF(scannable->local_identity_key); + + if(scannable->remote_stable_identifier) { + free(scannable->remote_stable_identifier); + } + + AXOLOTL_UNREF(scannable->remote_identity_key); + + free(scannable); +} diff --git a/libs/libaxolotl/src/fingerprint.h b/libs/libaxolotl/src/fingerprint.h new file mode 100644 index 0000000000..25d80a4e77 --- /dev/null +++ b/libs/libaxolotl/src/fingerprint.h @@ -0,0 +1,86 @@ +#ifndef FINGERPRINT_H +#define FINGERPRINT_H + +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Construct a fingerprint generator for 60 digit numerics. + * + * @param generator set to a freshly allocated generator instance + * @param iterations The number of internal iterations to perform in the process of + * generating a fingerprint. This needs to be constant, and synchronized + * across all clients. + * + * The higher the iteration count, the higher the security level: + * + * - 1024 ~ 109.7 bits + * - 1400 > 110 bits + * - 5200 > 112 bits + * @param global_context the global library context + * @return 0 on success, or negative on failure + */ +int fingerprint_generator_create(fingerprint_generator **generator, int iterations, axolotl_context *global_context); + +/** + * Generate a scannable and displayble fingerprint. + * + * @param local_stable_identifier The client's "stable" identifier. + * @param local_identity_key The client's identity key. + * @param remote_stable_identifier The remote party's "stable" identifier. + * @param remote_identity_key The remote party's identity key. + * @param fingerprint Set to a freshly allocated unique fingerprint for this conversation + * @return 0 on success, or negative on failure + */ +int fingerprint_generator_create_for(fingerprint_generator *generator, + const char *local_stable_identifier, ec_public_key *local_identity_key, + const char *remote_stable_identifier, ec_public_key *remote_identity_key, + fingerprint **fingerprint_val); + +void fingerprint_generator_free(fingerprint_generator *generator); + +int fingerprint_create(fingerprint **fingerprint_val, displayable_fingerprint *displayable, scannable_fingerprint *scannable); +displayable_fingerprint *fingerprint_get_displayable(fingerprint *fingerprint_val); +scannable_fingerprint *fingerprint_get_scannable(fingerprint *fingerprint_val); +void fingerprint_destroy(axolotl_type_base *type); + +int displayable_fingerprint_create(displayable_fingerprint **displayable, const char *local_fingerprint, const char *remote_fingerprint); +const char *displayable_fingerprint_local(displayable_fingerprint *displayable); +const char *displayable_fingerprint_remote(displayable_fingerprint *displayable); +const char *displayable_fingerprint_text(displayable_fingerprint *displayable); +void displayable_fingerprint_destroy(axolotl_type_base *type); + +int scannable_fingerprint_create(scannable_fingerprint **scannable, + uint32_t version, + const char *local_stable_identifier, ec_public_key *local_identity_key, + const char *remote_stable_identifier, ec_public_key *remote_identity_key); + +int scannable_fingerprint_serialize(axolotl_buffer **buffer, const scannable_fingerprint *scannable); +int scannable_fingerprint_deserialize(scannable_fingerprint **scannable, const uint8_t *data, size_t len, axolotl_context *global_context); +uint32_t scannable_fingerprint_get_version(scannable_fingerprint *scannable); +const char *scannable_fingerprint_get_local_stable_identifier(scannable_fingerprint *scannable); +ec_public_key *scannable_fingerprint_get_local_identity_key(scannable_fingerprint *scannable); +const char *scannable_fingerprint_get_remote_stable_identifier(scannable_fingerprint *scannable); +ec_public_key *scannable_fingerprint_get_remote_identity_key(scannable_fingerprint *scannable); + +/** + * Compare a scanned QR code with what we expect. + * @param scannable The local scannable data + * @param other_scannable The data from the scanned code + * @retval 1 if the scannable codes match + * @retval 0 if the scannable codes do not match + * @retval AX_ERR_FP_VERSION_MISMATCH if the scanned fingerprint is the wrong version + * @retval AX_ERR_FP_IDENT_MISMATCH if the scanned fingerprint is for the wrong stable identifier + */ +int scannable_fingerprint_compare(scannable_fingerprint *scannable, const scannable_fingerprint *other_scannable); + +void scannable_fingerprint_destroy(axolotl_type_base *type); + +#ifdef __cplusplus +} +#endif + +#endif /* FINGERPRINT_H */ diff --git a/libs/libaxolotl/src/group_cipher.c b/libs/libaxolotl/src/group_cipher.c new file mode 100644 index 0000000000..752c2e16d4 --- /dev/null +++ b/libs/libaxolotl/src/group_cipher.c @@ -0,0 +1,326 @@ +#include "group_cipher.h" + +#include <assert.h> +#include <string.h> +#include "axolotl_internal.h" +#include "protocol.h" +#include "sender_key.h" +#include "sender_key_record.h" +#include "sender_key_state.h" + +struct group_cipher +{ + axolotl_store_context *store; + const axolotl_sender_key_name *sender_key_id; + axolotl_context *global_context; + int (*decrypt_callback)(group_cipher *cipher, axolotl_buffer *plaintext, void *decrypt_context); + int inside_callback; + void *user_data; +}; + +static int group_cipher_get_sender_key(group_cipher *cipher, sender_message_key **sender_key, sender_key_state *state, uint32_t iteration); +static int group_cipher_decrypt_callback(group_cipher *cipher, axolotl_buffer *plaintext, void *decrypt_context); + +int group_cipher_create(group_cipher **cipher, + axolotl_store_context *store, const axolotl_sender_key_name *sender_key_id, + axolotl_context *global_context) +{ + group_cipher *result_cipher; + + assert(store); + assert(global_context); + + result_cipher = malloc(sizeof(group_cipher)); + if(!result_cipher) { + return AX_ERR_NOMEM; + } + memset(result_cipher, 0, sizeof(group_cipher)); + + result_cipher->store = store; + result_cipher->sender_key_id = sender_key_id; + result_cipher->global_context = global_context; + + *cipher = result_cipher; + return 0; +} + +void group_cipher_set_user_data(group_cipher *cipher, void *user_data) +{ + assert(cipher); + cipher->user_data = user_data; +} + +void *group_cipher_get_user_data(group_cipher *cipher) +{ + assert(cipher); + return cipher->user_data; +} + +void group_cipher_set_decryption_callback(group_cipher *cipher, + int (*callback)(group_cipher *cipher, axolotl_buffer *plaintext, void *decrypt_context)) +{ + assert(cipher); + cipher->decrypt_callback = callback; +} + +int group_cipher_encrypt(group_cipher *cipher, + const uint8_t *padded_plaintext, size_t padded_plaintext_len, + ciphertext_message **encrypted_message) +{ + int result = 0; + sender_key_message *result_message = 0; + sender_key_record *record = 0; + sender_key_state *state = 0; + sender_message_key *sender_key = 0; + sender_chain_key *next_chain_key = 0; + axolotl_buffer *sender_cipher_key = 0; + axolotl_buffer *sender_cipher_iv = 0; + axolotl_buffer *ciphertext = 0; + + assert(cipher); + axolotl_lock(cipher->global_context); + + if(cipher->inside_callback == 1) { + result = AX_ERR_INVAL; + goto complete; + } + + result = axolotl_sender_key_load_key(cipher->store, &record, cipher->sender_key_id); + if(result < 0) { + goto complete; + } + + result = sender_key_record_get_sender_key_state(record, &state); + if(result < 0) { + goto complete; + } + + result = sender_chain_key_create_message_key(sender_key_state_get_chain_key(state), &sender_key); + if(result < 0) { + goto complete; + } + + sender_cipher_key = sender_message_key_get_cipher_key(sender_key); + sender_cipher_iv = sender_message_key_get_iv(sender_key); + + result = axolotl_encrypt(cipher->global_context, &ciphertext, AX_CIPHER_AES_CBC_PKCS5, + axolotl_buffer_data(sender_cipher_key), axolotl_buffer_len(sender_cipher_key), + axolotl_buffer_data(sender_cipher_iv), axolotl_buffer_len(sender_cipher_iv), + padded_plaintext, padded_plaintext_len); + if(result < 0) { + goto complete; + } + + result = sender_key_message_create(&result_message, + sender_key_state_get_key_id(state), + sender_message_key_get_iteration(sender_key), + axolotl_buffer_data(ciphertext), axolotl_buffer_len(ciphertext), + sender_key_state_get_signing_key_private(state), + cipher->global_context); + if(result < 0) { + goto complete; + } + + result = sender_chain_key_create_next(sender_key_state_get_chain_key(state), &next_chain_key); + if(result < 0) { + goto complete; + } + + sender_key_state_set_chain_key(state, next_chain_key); + + result = axolotl_sender_key_store_key(cipher->store, cipher->sender_key_id, record); + +complete: + if(result >= 0) { + *encrypted_message = (ciphertext_message *)result_message; + } + else { + if(result == AX_ERR_INVALID_KEY_ID) { + result = AX_ERR_NO_SESSION; + } + AXOLOTL_UNREF(result_message); + } + axolotl_buffer_free(ciphertext); + AXOLOTL_UNREF(next_chain_key); + AXOLOTL_UNREF(sender_key); + AXOLOTL_UNREF(record); + axolotl_unlock(cipher->global_context); + return result; +} + +int group_cipher_decrypt(group_cipher *cipher, + sender_key_message *ciphertext, void *decrypt_context, + axolotl_buffer **plaintext) +{ + int result = 0; + axolotl_buffer *result_buf = 0; + sender_key_record *record = 0; + sender_key_state *state = 0; + sender_message_key *sender_key = 0; + axolotl_buffer *sender_cipher_key = 0; + axolotl_buffer *sender_cipher_iv = 0; + axolotl_buffer *ciphertext_body = 0; + + assert(cipher); + axolotl_lock(cipher->global_context); + + if(cipher->inside_callback == 1) { + result = AX_ERR_INVAL; + goto complete; + } + + result = axolotl_sender_key_load_key(cipher->store, &record, cipher->sender_key_id); + if(result < 0) { + goto complete; + } + + if(sender_key_record_is_empty(record)) { + result = AX_ERR_NO_SESSION; + axolotl_log(cipher->global_context, AX_LOG_WARNING, "No sender key for: %s::%s::%d", + cipher->sender_key_id->group_id, + cipher->sender_key_id->sender.name, + cipher->sender_key_id->sender.device_id); + goto complete; + } + + result = sender_key_record_get_sender_key_state_by_id(record, &state, sender_key_message_get_key_id(ciphertext)); + if(result < 0) { + goto complete; + } + + result = sender_key_message_verify_signature(ciphertext, sender_key_state_get_signing_key_public(state)); + if(result < 0) { + goto complete; + } + + result = group_cipher_get_sender_key(cipher, &sender_key, state, sender_key_message_get_iteration(ciphertext)); + if(result < 0) { + goto complete; + } + + sender_cipher_key = sender_message_key_get_cipher_key(sender_key); + sender_cipher_iv = sender_message_key_get_iv(sender_key); + ciphertext_body = sender_key_message_get_ciphertext(ciphertext); + + result = axolotl_decrypt(cipher->global_context, &result_buf, AX_CIPHER_AES_CBC_PKCS5, + axolotl_buffer_data(sender_cipher_key), axolotl_buffer_len(sender_cipher_key), + axolotl_buffer_data(sender_cipher_iv), axolotl_buffer_len(sender_cipher_iv), + axolotl_buffer_data(ciphertext_body), axolotl_buffer_len(ciphertext_body)); + if(result < 0) { + goto complete; + } + + result = group_cipher_decrypt_callback(cipher, result_buf, decrypt_context); + if(result < 0) { + goto complete; + } + + result = axolotl_sender_key_store_key(cipher->store, cipher->sender_key_id, record); + +complete: + AXOLOTL_UNREF(sender_key); + AXOLOTL_UNREF(record); + if(result >= 0) { + *plaintext = result_buf; + } + else { + if(result == AX_ERR_INVALID_KEY || result == AX_ERR_INVALID_KEY_ID) { + result = AX_ERR_INVALID_MESSAGE; + } + axolotl_buffer_free(result_buf); + } + axolotl_unlock(cipher->global_context); + return result; +} + +int group_cipher_get_sender_key(group_cipher *cipher, sender_message_key **sender_key, sender_key_state *state, uint32_t iteration) +{ + int result = 0; + sender_message_key *result_key = 0; + sender_chain_key *chain_key = 0; + sender_chain_key *next_chain_key = 0; + sender_message_key *message_key = 0; + + chain_key = sender_key_state_get_chain_key(state); + AXOLOTL_REF(chain_key); + + if(sender_chain_key_get_iteration(chain_key) > iteration) { + if(sender_key_state_has_sender_message_key(state, iteration)) { + result_key = sender_key_state_remove_sender_message_key(state, iteration); + if(!result_key) { + result = AX_ERR_UNKNOWN; + } + goto complete; + } + else { + result = AX_ERR_DUPLICATE_MESSAGE; + axolotl_log(cipher->global_context, AX_LOG_WARNING, + "Received message with old counter: %d, %d", + sender_chain_key_get_iteration(chain_key), iteration); + goto complete; + } + } + + if(iteration - sender_chain_key_get_iteration(chain_key) > 2000) { + result = AX_ERR_INVALID_MESSAGE; + axolotl_log(cipher->global_context, AX_LOG_WARNING, "Over 2000 messages into the future!"); + goto complete; + } + + while(sender_chain_key_get_iteration(chain_key) < iteration) { + result = sender_chain_key_create_message_key(chain_key, &message_key); + if(result < 0) { + goto complete; + } + + result = sender_key_state_add_sender_message_key(state, message_key); + if(result < 0) { + goto complete; + } + AXOLOTL_UNREF(message_key); + + result = sender_chain_key_create_next(chain_key, &next_chain_key); + if(result < 0) { + goto complete; + } + + AXOLOTL_UNREF(chain_key); + chain_key = next_chain_key; + next_chain_key = 0; + } + + result = sender_chain_key_create_next(chain_key, &next_chain_key); + if(result < 0) { + goto complete; + } + + sender_key_state_set_chain_key(state, next_chain_key); + result = sender_chain_key_create_message_key(chain_key, &result_key); + +complete: + AXOLOTL_UNREF(message_key); + AXOLOTL_UNREF(chain_key); + AXOLOTL_UNREF(next_chain_key); + if(result >= 0) { + *sender_key = result_key; + } + return result; +} + +static int group_cipher_decrypt_callback(group_cipher *cipher, axolotl_buffer *plaintext, void *decrypt_context) +{ + int result = 0; + if(cipher->decrypt_callback) { + cipher->inside_callback = 1; + result = cipher->decrypt_callback(cipher, plaintext, decrypt_context); + cipher->inside_callback = 0; + } + return result; +} + +void group_cipher_free(group_cipher *cipher) +{ + if(cipher) { + free(cipher); + } +} diff --git a/libs/libaxolotl/src/group_cipher.h b/libs/libaxolotl/src/group_cipher.h new file mode 100644 index 0000000000..f78a574bbe --- /dev/null +++ b/libs/libaxolotl/src/group_cipher.h @@ -0,0 +1,113 @@ +#ifndef GROUP_CIPHER_H +#define GROUP_CIPHER_H + +#include <stdint.h> +#include <stddef.h> +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The main entry point for Axolotl group encrypt/decrypt operations. + * + * Once a session has been established with group_session_builder and a + * sender_key_distribution_message has been distributed to each member of + * the group, this class can be used for all subsequent encrypt/decrypt + * operations within that session (i.e. until group membership changes). + */ + +/** + * Construct a group cipher for encrypt/decrypt operations. + * + * The store and global contexts must remain valid for the lifetime of the + * group cipher. + * + * When finished, free the returned instance by calling group_cipher_free(). + * + * @param cipher set to a freshly allocated group cipher instance + * @param store the axolotl_store_context to store all state information in + * @param sender_key_id the sender that messages will be encrypted to or decrypted from + * @param global_context the global library context + * @return 0 on success, or negative on failure + */ +int group_cipher_create(group_cipher **cipher, + axolotl_store_context *store, const axolotl_sender_key_name *sender_key_id, + axolotl_context *global_context); + +/** + * Set the optional user data pointer for the group cipher. + * + * This is to give callback functions a way of accessing app specific + * context information for this cipher. + */ +void group_cipher_set_user_data(group_cipher *cipher, void *user_data); + +/** + * Get the optional user data pointer for the group cipher. + * + * This is to give callback functions a way of accessing app specific + * context information for this cipher. + */ +void *group_cipher_get_user_data(group_cipher *cipher); + +/** + * Set the callback function that is called during the decrypt process. + * + * The callback function is called from within group_cipher_decrypt() after + * decryption is complete but before the updated session state has been + * committed to the session store. If the callback function returns a + * negative value, then the decrypt function will immediately fail with + * an error. + * + * This a callback allows some implementations to store the committed plaintext + * to their local message store first, in case they are concerned with a crash + * or write error happening between the time the session state is updated but + * before they're able to successfully store the plaintext to disk. + * + * @param callback the callback function to set + * @param user_data user data pointer provided to the callback + */ +void group_cipher_set_decryption_callback(group_cipher *cipher, + int (*callback)(group_cipher *cipher, axolotl_buffer *plaintext, void *decrypt_context)); + +/** + * Encrypt a message. + * + * @param padded_message The plaintext message bytes, optionally padded to a constant multiple. + * @param padded_message_len The length of the data pointed to by padded_message + * @param encrypted_message Set to a ciphertext message encrypted to the group+sender+device tuple. + * + * @return AX_SUCCESS on success, negative on error + */ +int group_cipher_encrypt(group_cipher *cipher, + const uint8_t *padded_plaintext, size_t padded_plaintext_len, + ciphertext_message **encrypted_message); + +/** + * Decrypt a message. + * + * @param ciphertext The sender_key_message to decrypt. + * @param decrypt_context Optional context pointer associated with the + * ciphertext, which is passed to the decryption callback function + * @param plaintext Set to a newly allocated buffer containing the plaintext. + * + * @retval AX_SUCCESS Success + * @retval AX_ERR_INVALID_MESSAGE if the input is not valid ciphertext. + * @retval AX_ERR_DUPLICATE_MESSAGE if the input is a message that has already been received. + * @retval AX_ERR_LEGACY_MESSAGE if the input is a message formatted by a protocol version that + * is no longer supported. + * @retval AX_ERR_NO_SESSION if there is no established session for this contact. + */ +int group_cipher_decrypt(group_cipher *cipher, + sender_key_message *ciphertext, void *decrypt_context, + axolotl_buffer **plaintext); + +void group_cipher_free(group_cipher *cipher); + +#ifdef __cplusplus +} +#endif + +#endif /* GROUP_CIPHER_H */ diff --git a/libs/libaxolotl/src/group_session_builder.c b/libs/libaxolotl/src/group_session_builder.c new file mode 100644 index 0000000000..4ac825b142 --- /dev/null +++ b/libs/libaxolotl/src/group_session_builder.c @@ -0,0 +1,149 @@ +#include "group_session_builder.h" + +#include <assert.h> +#include <string.h> +#include "axolotl_internal.h" +#include "sender_key_record.h" +#include "sender_key_state.h" +#include "sender_key.h" +#include "protocol.h" +#include "key_helper.h" + +struct group_session_builder +{ + axolotl_store_context *store; + axolotl_context *global_context; +}; + +int group_session_builder_create(group_session_builder **builder, + axolotl_store_context *store, axolotl_context *global_context) +{ + group_session_builder *result = 0; + + assert(store); + assert(global_context); + + result = malloc(sizeof(group_session_builder)); + if(!result) { + return AX_ERR_NOMEM; + } + memset(result, 0, sizeof(group_session_builder)); + + result->store = store; + result->global_context = global_context; + + *builder = result; + return 0; +} + +int group_session_builder_process_session(group_session_builder *builder, + const axolotl_sender_key_name *sender_key_name, + sender_key_distribution_message *distribution_message) +{ + int result = 0; + sender_key_record *record = 0; + + assert(builder); + assert(builder->store); + axolotl_lock(builder->global_context); + + result = axolotl_sender_key_load_key(builder->store, &record, sender_key_name); + if(result < 0) { + goto complete; + } + + result = sender_key_record_add_sender_key_state(record, + sender_key_distribution_message_get_id(distribution_message), + sender_key_distribution_message_get_iteration(distribution_message), + sender_key_distribution_message_get_chain_key(distribution_message), + sender_key_distribution_message_get_signature_key(distribution_message)); + if(result < 0) { + goto complete; + } + + result = axolotl_sender_key_store_key(builder->store, sender_key_name, record); + +complete: + AXOLOTL_UNREF(record); + axolotl_unlock(builder->global_context); + return result; +} + +int group_session_builder_create_session(group_session_builder *builder, + sender_key_distribution_message **distribution_message, + const axolotl_sender_key_name *sender_key_name) +{ + int result = 0; + sender_key_record *record = 0; + sender_key_state *state = 0; + uint32_t sender_key_id = 0; + axolotl_buffer *sender_key = 0; + ec_key_pair *sender_signing_key = 0; + sender_chain_key *chain_key = 0; + axolotl_buffer *seed = 0; + + assert(builder); + assert(builder->store); + axolotl_lock(builder->global_context); + + result = axolotl_sender_key_load_key(builder->store, &record, sender_key_name); + if(result < 0) { + goto complete; + } + + if(sender_key_record_is_empty(record)) { + result = axolotl_key_helper_generate_sender_key_id(&sender_key_id, builder->global_context); + if(result < 0) { + goto complete; + } + + result = axolotl_key_helper_generate_sender_key(&sender_key, builder->global_context); + if(result < 0) { + goto complete; + } + + result = axolotl_key_helper_generate_sender_signing_key(&sender_signing_key, builder->global_context); + if(result < 0) { + goto complete; + } + + result = sender_key_record_set_sender_key_state(record, sender_key_id, 0, sender_key, sender_signing_key); + if(result < 0) { + goto complete; + } + + result = axolotl_sender_key_store_key(builder->store, sender_key_name, record); + if(result < 0) { + goto complete; + } + } + + result = sender_key_record_get_sender_key_state(record, &state); + if(result < 0) { + goto complete; + } + + chain_key = sender_key_state_get_chain_key(state); + seed = sender_chain_key_get_seed(chain_key); + + result = sender_key_distribution_message_create(distribution_message, + sender_key_state_get_key_id(state), + sender_chain_key_get_iteration(chain_key), + axolotl_buffer_data(seed), axolotl_buffer_len(seed), + sender_key_state_get_signing_key_public(state), + builder->global_context); + +complete: + axolotl_buffer_free(sender_key); + AXOLOTL_UNREF(sender_signing_key); + AXOLOTL_UNREF(record); + axolotl_unlock(builder->global_context); + return result; +} + +void group_session_builder_free(group_session_builder *builder) +{ + if(builder) { + free(builder); + } +} diff --git a/libs/libaxolotl/src/group_session_builder.h b/libs/libaxolotl/src/group_session_builder.h new file mode 100644 index 0000000000..b54807427e --- /dev/null +++ b/libs/libaxolotl/src/group_session_builder.h @@ -0,0 +1,70 @@ +#ifndef GROUP_SESSION_BUILDER_H +#define GROUP_SESSION_BUILDER_H + +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Group session builder is responsible for setting up group sender key encrypted sessions. + * + * Once a session has been established, group_cipher can be used to + * encrypt/decrypt messages in that session. + * <p> + * The built sessions are unidirectional: they can be used either for sending + * or for receiving, but not both. + * + * Sessions are constructed per (groupId + senderId + deviceId) tuple. Remote logical users + * are identified by their senderId, and each logical recipientId can have multiple physical + * devices. + */ + +/** + * Constructs a group session builder. + * + * The store and global contexts must remain valid for the lifetime of the + * session builder. + * + * When finished, free the returned instance by calling group_session_builder_free(). + * + * @param builder set to a freshly allocated group session builder instance + * @param store the axolotl_store_context to store all state information in + * @param global_context the global library context + * @return 0 on success, or negative on failure + */ +int group_session_builder_create(group_session_builder **builder, + axolotl_store_context *store, axolotl_context *global_context); + +/** + * Construct a group session for receiving messages from senderKeyName. + * + * @param sender_key_name the (groupId, senderId, deviceId) tuple associated + * with the sender_key_distribution_message + * @param distribution_message a received sender_key_distribution_message + * @return 0 on success, or negative on failure + */ +int group_session_builder_process_session(group_session_builder *builder, + const axolotl_sender_key_name *sender_key_name, + sender_key_distribution_message *distribution_message); + +/** + * Construct a group session for sending messages. + * + * @param distribution_message a distribution message to be allocated and populated + * @param sender_key_name the (groupId, senderId, deviceId) tuple. In this + * case, the sender should be the caller + * @return 0 on success, or negative on failure + */ +int group_session_builder_create_session(group_session_builder *builder, + sender_key_distribution_message **distribution_message, + const axolotl_sender_key_name *sender_key_name); + +void group_session_builder_free(group_session_builder *builder); + +#ifdef __cplusplus +} +#endif + +#endif /* GROUP_SESSION_BUILDER_H */ diff --git a/libs/libaxolotl/src/hkdf.c b/libs/libaxolotl/src/hkdf.c new file mode 100644 index 0000000000..63b35ddaa4 --- /dev/null +++ b/libs/libaxolotl/src/hkdf.c @@ -0,0 +1,256 @@ +#include "hkdf.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <assert.h> + +#include "axolotl_internal.h" + +#define HASH_OUTPUT_SIZE 32 + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +struct hkdf_context +{ + axolotl_type_base base; + axolotl_context *global_context; + int iteration_start_offset; +}; + +int hkdf_create(hkdf_context **context, int message_version, axolotl_context *global_context) +{ + assert(global_context); + *context = malloc(sizeof(hkdf_context)); + if(!(*context)) { + return AX_ERR_NOMEM; + } + + memset(*context, 0, sizeof(hkdf_context)); + + AXOLOTL_INIT(*context, hkdf_destroy); + (*context)->global_context = global_context; + + if(message_version == 2) { + (*context)->iteration_start_offset = 0; + } + else if(message_version == 3) { + (*context)->iteration_start_offset = 1; + } + else { + free(*context); + return AX_ERR_INVAL; + } + + return 0; +} + +ssize_t hkdf_extract(hkdf_context *context, + uint8_t **output, + const uint8_t *salt, size_t salt_len, + const uint8_t *input_key_material, size_t input_key_material_len) +{ + int result = 0; + axolotl_buffer *mac_buffer = 0; + uint8_t *mac = 0; + size_t mac_len = 0; + void *hmac_context; + + assert(context); + + result = axolotl_hmac_sha256_init(context->global_context, + &hmac_context, salt, salt_len); + if(result < 0) { + goto complete; + } + + result = axolotl_hmac_sha256_update(context->global_context, + hmac_context, input_key_material, input_key_material_len); + if(result < 0) { + goto complete; + } + + result = axolotl_hmac_sha256_final(context->global_context, + hmac_context, &mac_buffer); + if(result < 0) { + goto complete; + } + + mac_len = axolotl_buffer_len(mac_buffer); + mac = malloc(mac_len); + if(!mac) { + result = AX_ERR_NOMEM; + goto complete; + } + + memcpy(mac, axolotl_buffer_data(mac_buffer), mac_len); + +complete: + axolotl_hmac_sha256_cleanup(context->global_context, hmac_context); + axolotl_buffer_free(mac_buffer); + + if(result >= 0) { + *output = mac; + return (ssize_t)mac_len; + } + else { + return result; + } +} + +ssize_t hkdf_expand(hkdf_context *context, + uint8_t **output, + const uint8_t *prk, size_t prk_len, + const uint8_t *info, size_t info_len, + size_t output_len) +{ + int iterations = (int)ceil((double)output_len / (double)HASH_OUTPUT_SIZE); + size_t remaining_len = output_len; + axolotl_buffer *step_buffer = 0; + size_t step_size = 0; + uint8_t *result_buf = 0; + size_t result_buf_len = 0; + void *hmac_context = 0; + int result = 0; + uint8_t i; + + assert(context); + + for(i = context->iteration_start_offset; i < iterations + context->iteration_start_offset; i++) { + result = axolotl_hmac_sha256_init(context->global_context, + &hmac_context, prk, prk_len); + if(result < 0) { + goto complete; + } + + if(step_buffer) { + result = axolotl_hmac_sha256_update(context->global_context, + hmac_context, + axolotl_buffer_data(step_buffer), + axolotl_buffer_len(step_buffer)); + if(result < 0) { + goto complete; + } + axolotl_buffer_free(step_buffer); + step_buffer = 0; + } + + if(info) { + result = axolotl_hmac_sha256_update(context->global_context, + hmac_context, info, info_len); + if(result < 0) { + goto complete; + } + } + + result = axolotl_hmac_sha256_update(context->global_context, + hmac_context, &i, sizeof(uint8_t)); + if(result < 0) { + goto complete; + } + + result = axolotl_hmac_sha256_final(context->global_context, + hmac_context, &step_buffer); + if(result < 0) { + goto complete; + } + + axolotl_hmac_sha256_cleanup(context->global_context, hmac_context); + hmac_context = 0; + + step_size = MIN(remaining_len, axolotl_buffer_len(step_buffer)); + + if(!result_buf) { + result_buf = malloc(step_size); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + memcpy(result_buf, axolotl_buffer_data(step_buffer), step_size); + result_buf_len = step_size; + } + else { + uint8_t *tmp_buf = realloc(result_buf, result_buf_len + step_size); + if(!tmp_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + result_buf = tmp_buf; + memcpy(result_buf + result_buf_len, axolotl_buffer_data(step_buffer), step_size); + result_buf_len += step_size; + } + remaining_len -= step_size; + } + +complete: + if(hmac_context) { + axolotl_hmac_sha256_cleanup(context->global_context, hmac_context); + } + axolotl_buffer_free(step_buffer); + if(result < 0) { + free(result_buf); + return result; + } + else { + *output = result_buf; + return (ssize_t)result_buf_len; + } +} + +ssize_t hkdf_derive_secrets(hkdf_context *context, + uint8_t **output, + const uint8_t *input_key_material, size_t input_key_material_len, + const uint8_t *salt, size_t salt_len, + const uint8_t *info, size_t info_len, + size_t output_len) +{ + ssize_t result = 0; + uint8_t *prk = 0; + ssize_t prk_len = 0; + + assert(context); + + prk_len = hkdf_extract(context, &prk, salt, salt_len, input_key_material, input_key_material_len); + if(prk_len < 0) { + axolotl_log(context->global_context, AX_LOG_ERROR, "hkdf_extract error: %d", prk_len); + return prk_len; + } + + result = hkdf_expand(context, output, prk, (size_t)prk_len, info, info_len, output_len); + + if(prk) { + free(prk); + } + + return result; +} + +int hkdf_compare(const hkdf_context *context1, const hkdf_context *context2) +{ + if(context1 == context2) { + return 0; + } + else if(context1 == 0 && context2 != 0) { + return -1; + } + else if(context1 != 0 && context2 == 0) { + return 1; + } + else if(context1->iteration_start_offset < context2->iteration_start_offset) { + return -1; + } + else if(context1->iteration_start_offset > context2->iteration_start_offset) { + return 1; + } + else { + return 0; + } +} + +void hkdf_destroy(axolotl_type_base *type) +{ + hkdf_context *context = (hkdf_context *)type; + free(context); +} diff --git a/libs/libaxolotl/src/hkdf.h b/libs/libaxolotl/src/hkdf.h new file mode 100644 index 0000000000..8e939485c3 --- /dev/null +++ b/libs/libaxolotl/src/hkdf.h @@ -0,0 +1,29 @@ +#ifndef HKDF_H +#define HKDF_H + +#include <stdint.h> +#include <stddef.h> +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int hkdf_create(hkdf_context **context, int message_version, axolotl_context *global_context); + +ssize_t hkdf_derive_secrets(hkdf_context *context, + uint8_t **output, + const uint8_t *input_key_material, size_t input_key_material_len, + const uint8_t *salt, size_t salt_len, + const uint8_t *info, size_t info_len, + size_t output_len); + +int hkdf_compare(const hkdf_context *context1, const hkdf_context *context2); + +void hkdf_destroy(axolotl_type_base *type); + +#ifdef __cplusplus +} +#endif + +#endif /* HKDF_H */ diff --git a/libs/libaxolotl/src/key_helper.c b/libs/libaxolotl/src/key_helper.c new file mode 100644 index 0000000000..a1403a517f --- /dev/null +++ b/libs/libaxolotl/src/key_helper.c @@ -0,0 +1,330 @@ +#include "key_helper.h" + +#include <assert.h> + +#include "axolotl_internal.h" +#include "session_pre_key.h" +#include "ratchet.h" +#include "curve.h" +#include "utlist.h" + +struct axolotl_key_helper_pre_key_list_node +{ + session_pre_key *element; + struct axolotl_key_helper_pre_key_list_node *next; +}; + +int axolotl_key_helper_generate_identity_key_pair(ratchet_identity_key_pair **key_pair, axolotl_context *global_context) +{ + int result = 0; + ratchet_identity_key_pair *result_pair = 0; + ec_key_pair *ec_pair = 0; + ec_public_key *public_key = 0; + ec_private_key *private_key = 0; + + assert(global_context); + + result = curve_generate_key_pair(global_context, &ec_pair); + if(result < 0) { + goto complete; + } + + public_key = ec_key_pair_get_public(ec_pair); + private_key = ec_key_pair_get_private(ec_pair); + + result = ratchet_identity_key_pair_create( + &result_pair, public_key, private_key); + +complete: + if(result >= 0) { + *key_pair = result_pair; + } + AXOLOTL_UNREF(ec_pair); + return result; +} + +int axolotl_key_helper_generate_registration_id(uint32_t *registration_id, int extended_range, axolotl_context *global_context) +{ + uint32_t range; + uint32_t id_value; + int result = 0; + + assert(global_context); + assert(global_context->crypto_provider.random_func); + + if(extended_range == 0) { + range = 16380; + } + else if(extended_range == 1) { + range = INT32_MAX - 1; + } + else { + return AX_ERR_INVAL; + } + + result = global_context->crypto_provider.random_func( + (uint8_t *)(&id_value), sizeof(id_value), + global_context->crypto_provider.user_data); + if(result < 0) { + return result; + } + + id_value = (id_value % range) + 1; + + *registration_id = id_value; + + return 0; +} + +int axolotl_key_helper_get_random_sequence(int *value, int max, axolotl_context *global_context) +{ + int result = 0; + int32_t result_value; + + assert(global_context); + assert(global_context->crypto_provider.random_func); + + result = global_context->crypto_provider.random_func( + (uint8_t *)(&result_value), sizeof(result_value), + global_context->crypto_provider.user_data); + if(result < 0) { + return result; + } + + result_value = ((result_value & 0x7FFFFFFF) % max); + + *value = result_value; + + return 0; +} + +int axolotl_key_helper_generate_pre_keys(axolotl_key_helper_pre_key_list_node **head, + unsigned int start, unsigned int count, + axolotl_context *global_context) +{ + int result = 0; + ec_key_pair *ec_pair = 0; + session_pre_key *pre_key = 0; + axolotl_key_helper_pre_key_list_node *result_head = 0; + axolotl_key_helper_pre_key_list_node *cur_node = 0; + axolotl_key_helper_pre_key_list_node *node = 0; + unsigned int start_index = start - 1; + unsigned int i; + + assert(global_context); + + for(i = 0; i < count; i++) { + uint32_t id = 0; + result = curve_generate_key_pair(global_context, &ec_pair); + if(result < 0) { + goto complete; + } + + id = ((start_index + i) % (PRE_KEY_MEDIUM_MAX_VALUE - 1)) + 1; + + result = session_pre_key_create(&pre_key, id, ec_pair); + if(result < 0) { + goto complete; + } + + AXOLOTL_UNREF(ec_pair); + ec_pair = 0; + + node = malloc(sizeof(axolotl_key_helper_pre_key_list_node)); + if(!node) { + result = AX_ERR_NOMEM; + goto complete; + } + node->element = pre_key; + node->next = 0; + if(!result_head) { + result_head = node; + cur_node = node; + } + else { + cur_node->next = node; + cur_node = node; + } + pre_key = 0; + node = 0; + } + +complete: + if(ec_pair) { + AXOLOTL_UNREF(ec_pair); + } + if(pre_key) { + AXOLOTL_UNREF(pre_key); + } + if(node) { + free(node); + } + if(result < 0) { + if(result_head) { + axolotl_key_helper_pre_key_list_node *tmp_node; + LL_FOREACH_SAFE(result_head, cur_node, tmp_node) { + LL_DELETE(result_head, cur_node); + AXOLOTL_UNREF(cur_node->element); + free(cur_node); + } + } + } + else { + *head = result_head; + } + return result; +} + +session_pre_key *axolotl_key_helper_key_list_element(const axolotl_key_helper_pre_key_list_node *node) +{ + assert(node); + assert(node->element); + return node->element; +} + +axolotl_key_helper_pre_key_list_node *axolotl_key_helper_key_list_next(const axolotl_key_helper_pre_key_list_node *node) +{ + assert(node); + return node->next; +} + +void axolotl_key_helper_key_list_free(axolotl_key_helper_pre_key_list_node *head) +{ + if(head) { + axolotl_key_helper_pre_key_list_node *cur_node; + axolotl_key_helper_pre_key_list_node *tmp_node; + LL_FOREACH_SAFE(head, cur_node, tmp_node) { + LL_DELETE(head, cur_node); + AXOLOTL_UNREF(cur_node->element); + free(cur_node); + } + } +} + +int axolotl_key_helper_generate_last_resort_pre_key(session_pre_key **pre_key, axolotl_context *global_context) +{ + int result = 0; + session_pre_key *result_pre_key = 0; + ec_key_pair *ec_pair = 0; + + assert(global_context); + + result = curve_generate_key_pair(global_context, &ec_pair); + if(result < 0) { + goto complete; + } + + result = session_pre_key_create(&result_pre_key, PRE_KEY_MEDIUM_MAX_VALUE, ec_pair); + +complete: + AXOLOTL_UNREF(ec_pair); + if(result >= 0) { + *pre_key = result_pre_key; + } + return result; +} + +int axolotl_key_helper_generate_signed_pre_key(session_signed_pre_key **signed_pre_key, + const ratchet_identity_key_pair *identity_key_pair, + uint32_t signed_pre_key_id, + uint64_t timestamp, + axolotl_context *global_context) +{ + int result = 0; + session_signed_pre_key *result_signed_pre_key = 0; + ec_key_pair *ec_pair = 0; + axolotl_buffer *public_buf = 0; + axolotl_buffer *signature_buf = 0; + ec_public_key *public_key = 0; + ec_private_key *private_key = 0; + + assert(global_context); + + result = curve_generate_key_pair(global_context, &ec_pair); + if(result < 0) { + goto complete; + } + + public_key = ec_key_pair_get_public(ec_pair); + result = ec_public_key_serialize(&public_buf, public_key); + if(result < 0) { + goto complete; + } + + private_key = ratchet_identity_key_pair_get_private(identity_key_pair); + + result = curve_calculate_signature(global_context, + &signature_buf, + private_key, + axolotl_buffer_data(public_buf), + axolotl_buffer_len(public_buf)); + if(result < 0) { + goto complete; + } + + result = session_signed_pre_key_create(&result_signed_pre_key, + signed_pre_key_id, timestamp, ec_pair, + axolotl_buffer_data(signature_buf), + axolotl_buffer_len(signature_buf)); + +complete: + AXOLOTL_UNREF(ec_pair); + axolotl_buffer_free(public_buf); + axolotl_buffer_free(signature_buf); + if(result >= 0) { + *signed_pre_key = result_signed_pre_key; + } + return result; +} + +int axolotl_key_helper_generate_sender_signing_key(ec_key_pair **key_pair, axolotl_context *global_context) +{ + int result; + + assert(global_context); + + result = curve_generate_key_pair(global_context, key_pair); + + return result; +} + +int axolotl_key_helper_generate_sender_key(axolotl_buffer **key_buffer, axolotl_context *global_context) +{ + int result = 0; + axolotl_buffer *result_buffer = 0; + + assert(global_context); + + result_buffer = axolotl_buffer_alloc(32); + if(!result_buffer) { + result = AX_ERR_NOMEM; + goto complete; + } + + result = axolotl_crypto_random(global_context, + axolotl_buffer_data(result_buffer), + axolotl_buffer_len(result_buffer)); + +complete: + if(result < 0) { + axolotl_buffer_free(result_buffer); + } + else { + *key_buffer = result_buffer; + result = 0; + } + return result; +} + +int axolotl_key_helper_generate_sender_key_id(uint32_t *key_id, axolotl_context *global_context) +{ + int result; + int value; + + result = axolotl_key_helper_get_random_sequence(&value, INT32_MAX, global_context); + + if(result >= 0) { + *key_id = (uint32_t)value; + } + return result; +} diff --git a/libs/libaxolotl/src/key_helper.h b/libs/libaxolotl/src/key_helper.h new file mode 100644 index 0000000000..1a7175b620 --- /dev/null +++ b/libs/libaxolotl/src/key_helper.h @@ -0,0 +1,139 @@ +#ifndef KEY_HELPER_H +#define KEY_HELPER_H + +#include <stdint.h> +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* Generate an identity key pair. Clients should only do this once, +* at install time. +* +* @param key_pair the generated identity key pair +* @return 0 on success, or negative on failure +*/ +int axolotl_key_helper_generate_identity_key_pair(ratchet_identity_key_pair **key_pair, axolotl_context *global_context); + +/** + * Generate a registration ID. Clients should only do this once, + * at install time. + * + * @param registration_id set to the generated registration ID + * @param extendedRange By default (0), the generated registration + * ID is sized to require the minimal possible protobuf + * encoding overhead. Specify true (1) if the caller needs + * the full range of MAX_INT at the cost of slightly + * higher encoding overhead. + * @return 0 on success, or negative on failure + */ +int axolotl_key_helper_generate_registration_id(uint32_t *registration_id, int extended_range, axolotl_context *global_context); + +/** + * Generate a random number bounded by the provided maximum + * + * @param value set to the next random number + * @param max the maximum value of the random number + * @return 0 on success, or negative on failure + */ +int axolotl_key_helper_get_random_sequence(int *value, int max, axolotl_context *global_context); + +/** + * Generate a list of PreKeys. Clients should do this at install time, and + * subsequently any time the list of PreKeys stored on the server runs low. + * + * Pre key IDs are shorts, so they will eventually be repeated. Clients should + * store pre keys in a circular buffer, so that they are repeated as infrequently + * as possible. + * + * When finished with this list, the caller should free it by calling + * axolotl_key_helper_key_list_free(). + * + * @param head pointer to the head of the key list + * @param start the starting pre key ID, inclusive. + * @param count the number of pre keys to generate. + * @return 0 on success, or negative on failure + */ +int axolotl_key_helper_generate_pre_keys(axolotl_key_helper_pre_key_list_node **head, + unsigned int start, unsigned int count, + axolotl_context *global_context); + +/** + * Get the pre key element for the current node in the key list. + * + * @param current list node + * @return pre key element + */ +session_pre_key *axolotl_key_helper_key_list_element(const axolotl_key_helper_pre_key_list_node *node); + +/** + * Get the next element in the ket list. + * + * @param current list node + * @return next list node, or 0 if at the end of the list + */ +axolotl_key_helper_pre_key_list_node *axolotl_key_helper_key_list_next(const axolotl_key_helper_pre_key_list_node *node); + +/** + * Free the key list. + * + * @param head pointer to the head of the list to free + */ +void axolotl_key_helper_key_list_free(axolotl_key_helper_pre_key_list_node *head); + +/** + * Generate the last resort pre key. Clients should do this only once, at + * install time, and durably store it for the length of the install. + * + * @param pre_key set to the generated pre key + * @return 0 on success, or negative on failure + */ +int axolotl_key_helper_generate_last_resort_pre_key(session_pre_key **pre_key, axolotl_context *global_context); + +/** + * Generate a signed pre key + * + * @param signed_pre_key set to the generated pre key + * @param identity_key_pair the local client's identity key pair. + * @param signed_pre_key_id the pre key ID to assign the generated signed pre key + * @param timestamp the current time in milliseconds since the UNIX epoch + * + * @return 0 on success, or negative on failure + */ +int axolotl_key_helper_generate_signed_pre_key(session_signed_pre_key **signed_pre_key, + const ratchet_identity_key_pair *identity_key_pair, + uint32_t signed_pre_key_id, + uint64_t timestamp, + axolotl_context *global_context); + +/* + * Generate a sender signing key pair + * +* @param key_pair the generated key pair +* @return 0 on success, or negative on failure + */ +int axolotl_key_helper_generate_sender_signing_key(ec_key_pair **key_pair, axolotl_context *global_context); + +/* + * Generate a sender key + * + * @param key_buffer buffer to be allocated and populated with the result + * @return 0 on success, or negative on failure + */ +int axolotl_key_helper_generate_sender_key(axolotl_buffer **key_buffer, axolotl_context *global_context); + +/* + * Generate a sender key ID + * + * @param key_id assigned to the generated ID + * @return 0 on success, or negative on failure + */ +int axolotl_key_helper_generate_sender_key_id(uint32_t *key_id, axolotl_context *global_context); + +#ifdef __cplusplus +} +#endif + +#endif /* KEY_HELPER_H */ diff --git a/libs/libaxolotl/src/protobuf-c/CMakeLists.txt b/libs/libaxolotl/src/protobuf-c/CMakeLists.txt new file mode 100644 index 0000000000..63f18db8ae --- /dev/null +++ b/libs/libaxolotl/src/protobuf-c/CMakeLists.txt @@ -0,0 +1,17 @@ +IF(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-shadow") +ENDIF(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") + +IF(CMAKE_COMPILER_IS_GNUCC) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-sign-compare -Wno-sign-conversion") +ENDIF(CMAKE_COMPILER_IS_GNUCC) + +IF(CMAKE_C_COMPILER_ID MATCHES "Clang") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-shorten-64-to-32") +ENDIF(CMAKE_C_COMPILER_ID MATCHES "Clang") + +set(protobuf_SRCS + protobuf-c.c +) + +add_library(protobuf-c OBJECT ${protobuf_SRCS}) diff --git a/libs/libaxolotl/src/protobuf-c/protobuf-c.c b/libs/libaxolotl/src/protobuf-c/protobuf-c.c new file mode 100644 index 0000000000..4c1438b44a --- /dev/null +++ b/libs/libaxolotl/src/protobuf-c/protobuf-c.c @@ -0,0 +1,3280 @@ +/* + * Copyright (c) 2008-2014, Dave Benson and the protobuf-c authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/*! \file + * Support library for `protoc-c` generated code. + * + * This file implements the public API used by the code generated + * by `protoc-c`. + * + * \authors Dave Benson and the protobuf-c authors + * + * \copyright 2008-2014. Licensed under the terms of the [BSD-2-Clause] license. + */ + +/** + * \todo 64-BIT OPTIMIZATION: certain implementations use 32-bit math + * even on 64-bit platforms (uint64_size, uint64_pack, parse_uint64). + * + * \todo Use size_t consistently. + */ + +#include <stdlib.h> /* for malloc, free */ +#include <string.h> /* for strcmp, strlen, memcpy, memmove, memset */ + +#include "protobuf-c.h" + +#if defined(_MSC_VER) && !defined(__cplusplus) +#define inline __inline +#endif + +#define TRUE 1 +#define FALSE 0 + +#define PROTOBUF_C__ASSERT_NOT_REACHED() assert(0) + +/** + * \defgroup internal Internal functions and macros + * + * These are not exported by the library but are useful to developers working + * on `libprotobuf-c` itself. + */ + +/** + * \defgroup macros Utility macros for manipulating structures + * + * Macros and constants used to manipulate the base "classes" generated by + * `protobuf-c`. They also define limits and check correctness. + * + * \ingroup internal + * @{ + */ + +/** The maximum length of a 64-bit integer in varint encoding. */ +#define MAX_UINT64_ENCODED_SIZE 10 + +#ifndef PROTOBUF_C_UNPACK_ERROR +# define PROTOBUF_C_UNPACK_ERROR(...) +#endif + +/** + * Internal `ProtobufCMessage` manipulation macro. + * + * Base macro for manipulating a `ProtobufCMessage`. Used by STRUCT_MEMBER() and + * STRUCT_MEMBER_PTR(). + */ +#define STRUCT_MEMBER_P(struct_p, struct_offset) \ + ((void *) ((uint8_t *) (struct_p) + (struct_offset))) + +/** + * Return field in a `ProtobufCMessage` based on offset. + * + * Take a pointer to a `ProtobufCMessage` and find the field at the offset. + * Cast it to the passed type. + */ +#define STRUCT_MEMBER(member_type, struct_p, struct_offset) \ + (*(member_type *) STRUCT_MEMBER_P((struct_p), (struct_offset))) + +/** + * Return field in a `ProtobufCMessage` based on offset. + * + * Take a pointer to a `ProtobufCMessage` and find the field at the offset. Cast + * it to a pointer to the passed type. + */ +#define STRUCT_MEMBER_PTR(member_type, struct_p, struct_offset) \ + ((member_type *) STRUCT_MEMBER_P((struct_p), (struct_offset))) + +/* Assertions for magic numbers. */ + +#define ASSERT_IS_ENUM_DESCRIPTOR(desc) \ + assert((desc)->magic == PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC) + +#define ASSERT_IS_MESSAGE_DESCRIPTOR(desc) \ + assert((desc)->magic == PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC) + +#define ASSERT_IS_MESSAGE(message) \ + ASSERT_IS_MESSAGE_DESCRIPTOR((message)->descriptor) + +#define ASSERT_IS_SERVICE_DESCRIPTOR(desc) \ + assert((desc)->magic == PROTOBUF_C__SERVICE_DESCRIPTOR_MAGIC) + +/**@}*/ + +/* --- version --- */ + +const char * +protobuf_c_version(void) +{ + return PROTOBUF_C_VERSION; +} + +uint32_t +protobuf_c_version_number(void) +{ + return PROTOBUF_C_VERSION_NUMBER; +} + +/* --- allocator --- */ + +static void * +system_alloc(void *allocator_data, size_t size) +{ + return malloc(size); +} + +static void +system_free(void *allocator_data, void *data) +{ + free(data); +} + +static inline void * +do_alloc(ProtobufCAllocator *allocator, size_t size) +{ + return allocator->alloc(allocator->allocator_data, size); +} + +static inline void +do_free(ProtobufCAllocator *allocator, void *data) +{ + if (data != NULL) + allocator->free(allocator->allocator_data, data); +} + +/* + * This allocator uses the system's malloc() and free(). It is the default + * allocator used if NULL is passed as the ProtobufCAllocator to an exported + * function. + */ +static ProtobufCAllocator protobuf_c__allocator = { + &system_alloc, + &system_free, + NULL +}; + +/* === buffer-simple === */ + +void +protobuf_c_buffer_simple_append(ProtobufCBuffer *buffer, + size_t len, const uint8_t *data) +{ + ProtobufCBufferSimple *simp = (ProtobufCBufferSimple *) buffer; + size_t new_len = simp->len + len; + + if (new_len > simp->alloced) { + ProtobufCAllocator *allocator = simp->allocator; + size_t new_alloced = simp->alloced * 2; + uint8_t *new_data; + + if (allocator == NULL) + allocator = &protobuf_c__allocator; + while (new_alloced < new_len) + new_alloced += new_alloced; + new_data = do_alloc(allocator, new_alloced); + if (!new_data) + return; + memcpy(new_data, simp->data, simp->len); + if (simp->must_free_data) + do_free(allocator, simp->data); + else + simp->must_free_data = TRUE; + simp->data = new_data; + simp->alloced = new_alloced; + } + memcpy(simp->data + simp->len, data, len); + simp->len = new_len; +} + +/** + * \defgroup packedsz protobuf_c_message_get_packed_size() implementation + * + * Routines mainly used by protobuf_c_message_get_packed_size(). + * + * \ingroup internal + * @{ + */ + +/** + * Return the number of bytes required to store the tag for the field. Includes + * 3 bits for the wire-type, and a single bit that denotes the end-of-tag. + * + * \param number + * Field tag to encode. + * \return + * Number of bytes required. + */ +static inline size_t +get_tag_size(unsigned number) +{ + if (number < (1 << 4)) { + return 1; + } else if (number < (1 << 11)) { + return 2; + } else if (number < (1 << 18)) { + return 3; + } else if (number < (1 << 25)) { + return 4; + } else { + return 5; + } +} + +/** + * Return the number of bytes required to store a variable-length unsigned + * 32-bit integer in base-128 varint encoding. + * + * \param v + * Value to encode. + * \return + * Number of bytes required. + */ +static inline size_t +uint32_size(uint32_t v) +{ + if (v < (1 << 7)) { + return 1; + } else if (v < (1 << 14)) { + return 2; + } else if (v < (1 << 21)) { + return 3; + } else if (v < (1 << 28)) { + return 4; + } else { + return 5; + } +} + +/** + * Return the number of bytes required to store a variable-length signed 32-bit + * integer in base-128 varint encoding. + * + * \param v + * Value to encode. + * \return + * Number of bytes required. + */ +static inline size_t +int32_size(int32_t v) +{ + if (v < 0) { + return 10; + } else if (v < (1 << 7)) { + return 1; + } else if (v < (1 << 14)) { + return 2; + } else if (v < (1 << 21)) { + return 3; + } else if (v < (1 << 28)) { + return 4; + } else { + return 5; + } +} + +/** + * Return the ZigZag-encoded 32-bit unsigned integer form of a 32-bit signed + * integer. + * + * \param v + * Value to encode. + * \return + * ZigZag encoded integer. + */ +static inline uint32_t +zigzag32(int32_t v) +{ + if (v < 0) + return ((uint32_t) (-v)) * 2 - 1; + else + return v * 2; +} + +/** + * Return the number of bytes required to store a signed 32-bit integer, + * converted to an unsigned 32-bit integer with ZigZag encoding, using base-128 + * varint encoding. + * + * \param v + * Value to encode. + * \return + * Number of bytes required. + */ +static inline size_t +sint32_size(int32_t v) +{ + return uint32_size(zigzag32(v)); +} + +/** + * Return the number of bytes required to store a 64-bit unsigned integer in + * base-128 varint encoding. + * + * \param v + * Value to encode. + * \return + * Number of bytes required. + */ +static inline size_t +uint64_size(uint64_t v) +{ + uint32_t upper_v = (uint32_t) (v >> 32); + + if (upper_v == 0) { + return uint32_size((uint32_t) v); + } else if (upper_v < (1 << 3)) { + return 5; + } else if (upper_v < (1 << 10)) { + return 6; + } else if (upper_v < (1 << 17)) { + return 7; + } else if (upper_v < (1 << 24)) { + return 8; + } else if (upper_v < (1U << 31)) { + return 9; + } else { + return 10; + } +} + +/** + * Return the ZigZag-encoded 64-bit unsigned integer form of a 64-bit signed + * integer. + * + * \param v + * Value to encode. + * \return + * ZigZag encoded integer. + */ +static inline uint64_t +zigzag64(int64_t v) +{ + if (v < 0) + return ((uint64_t) (-v)) * 2 - 1; + else + return v * 2; +} + +/** + * Return the number of bytes required to store a signed 64-bit integer, + * converted to an unsigned 64-bit integer with ZigZag encoding, using base-128 + * varint encoding. + * + * \param v + * Value to encode. + * \return + * Number of bytes required. + */ +static inline size_t +sint64_size(int64_t v) +{ + return uint64_size(zigzag64(v)); +} + +/** + * Calculate the serialized size of a single required message field, including + * the space needed by the preceding tag. + * + * \param field + * Field descriptor for member. + * \param member + * Field to encode. + * \return + * Number of bytes required. + */ +static size_t +required_field_get_packed_size(const ProtobufCFieldDescriptor *field, + const void *member) +{ + size_t rv = get_tag_size(field->id); + + switch (field->type) { + case PROTOBUF_C_TYPE_SINT32: + return rv + sint32_size(*(const int32_t *) member); + case PROTOBUF_C_TYPE_INT32: + return rv + int32_size(*(const uint32_t *) member); + case PROTOBUF_C_TYPE_UINT32: + return rv + uint32_size(*(const uint32_t *) member); + case PROTOBUF_C_TYPE_SINT64: + return rv + sint64_size(*(const int64_t *) member); + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: + return rv + uint64_size(*(const uint64_t *) member); + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + return rv + 4; + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + return rv + 8; + case PROTOBUF_C_TYPE_BOOL: + return rv + 1; + case PROTOBUF_C_TYPE_FLOAT: + return rv + 4; + case PROTOBUF_C_TYPE_DOUBLE: + return rv + 8; + case PROTOBUF_C_TYPE_ENUM: + /* \todo Is this correct for negative-valued enums? */ + return rv + uint32_size(*(const uint32_t *) member); + case PROTOBUF_C_TYPE_STRING: { + const char *str = *(char * const *) member; + size_t len = str ? strlen(str) : 0; + return rv + uint32_size(len) + len; + } + case PROTOBUF_C_TYPE_BYTES: { + size_t len = ((const ProtobufCBinaryData *) member)->len; + return rv + uint32_size(len) + len; + } + case PROTOBUF_C_TYPE_MESSAGE: { + const ProtobufCMessage *msg = *(ProtobufCMessage * const *) member; + size_t subrv = msg ? protobuf_c_message_get_packed_size(msg) : 0; + return rv + uint32_size(subrv) + subrv; + } + } + PROTOBUF_C__ASSERT_NOT_REACHED(); + return 0; +} + +/** + * Calculate the serialized size of a single optional message field, including + * the space needed by the preceding tag. Returns 0 if the optional field isn't + * set. + * + * \param field + * Field descriptor for member. + * \param has + * True if the field exists, false if not. + * \param member + * Field to encode. + * \return + * Number of bytes required. + */ +static size_t +optional_field_get_packed_size(const ProtobufCFieldDescriptor *field, + const protobuf_c_boolean *has, + const void *member) +{ + if (field->type == PROTOBUF_C_TYPE_MESSAGE || + field->type == PROTOBUF_C_TYPE_STRING) + { + const void *ptr = *(const void * const *) member; + if (ptr == NULL || ptr == field->default_value) + return 0; + } else { + if (!*has) + return 0; + } + return required_field_get_packed_size(field, member); +} + +/** + * Calculate the serialized size of repeated message fields, which may consist + * of any number of values (including 0). Includes the space needed by the + * preceding tags (as needed). + * + * \param field + * Field descriptor for member. + * \param count + * Number of repeated field members. + * \param member + * Field to encode. + * \return + * Number of bytes required. + */ +static size_t +repeated_field_get_packed_size(const ProtobufCFieldDescriptor *field, + size_t count, const void *member) +{ + size_t header_size; + size_t rv = 0; + unsigned i; + void *array = *(void * const *) member; + + if (count == 0) + return 0; + header_size = get_tag_size(field->id); + if (0 == (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED)) + header_size *= count; + + switch (field->type) { + case PROTOBUF_C_TYPE_SINT32: + for (i = 0; i < count; i++) + rv += sint32_size(((int32_t *) array)[i]); + break; + case PROTOBUF_C_TYPE_INT32: + for (i = 0; i < count; i++) + rv += int32_size(((uint32_t *) array)[i]); + break; + case PROTOBUF_C_TYPE_UINT32: + case PROTOBUF_C_TYPE_ENUM: + for (i = 0; i < count; i++) + rv += uint32_size(((uint32_t *) array)[i]); + break; + case PROTOBUF_C_TYPE_SINT64: + for (i = 0; i < count; i++) + rv += sint64_size(((int64_t *) array)[i]); + break; + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: + for (i = 0; i < count; i++) + rv += uint64_size(((uint64_t *) array)[i]); + break; + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: + rv += 4 * count; + break; + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: + rv += 8 * count; + break; + case PROTOBUF_C_TYPE_BOOL: + rv += count; + break; + case PROTOBUF_C_TYPE_STRING: + for (i = 0; i < count; i++) { + size_t len = strlen(((char **) array)[i]); + rv += uint32_size(len) + len; + } + break; + case PROTOBUF_C_TYPE_BYTES: + for (i = 0; i < count; i++) { + size_t len = ((ProtobufCBinaryData *) array)[i].len; + rv += uint32_size(len) + len; + } + break; + case PROTOBUF_C_TYPE_MESSAGE: + for (i = 0; i < count; i++) { + size_t len = protobuf_c_message_get_packed_size( + ((ProtobufCMessage **) array)[i]); + rv += uint32_size(len) + len; + } + break; + } + + if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED)) + header_size += uint32_size(rv); + return header_size + rv; +} + +/** + * Calculate the serialized size of an unknown field, i.e. one that is passed + * through mostly uninterpreted. This is required for forward compatibility if + * new fields are added to the message descriptor. + * + * \param field + * Unknown field type. + * \return + * Number of bytes required. + */ +static inline size_t +unknown_field_get_packed_size(const ProtobufCMessageUnknownField *field) +{ + return get_tag_size(field->tag) + field->len; +} + +/**@}*/ + +/* + * Calculate the serialized size of the message. + */ +size_t protobuf_c_message_get_packed_size(const ProtobufCMessage *message) +{ + unsigned i; + size_t rv = 0; + + ASSERT_IS_MESSAGE(message); + for (i = 0; i < message->descriptor->n_fields; i++) { + const ProtobufCFieldDescriptor *field = + message->descriptor->fields + i; + const void *member = + ((const char *) message) + field->offset; + const void *qmember = + ((const char *) message) + field->quantifier_offset; + + if (field->label == PROTOBUF_C_LABEL_REQUIRED) { + rv += required_field_get_packed_size(field, member); + } else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) { + rv += optional_field_get_packed_size(field, qmember, member); + } else { + rv += repeated_field_get_packed_size( + field, + *(const size_t *) qmember, + member + ); + } + } + for (i = 0; i < message->n_unknown_fields; i++) + rv += unknown_field_get_packed_size(&message->unknown_fields[i]); + return rv; +} + +/** + * \defgroup pack protobuf_c_message_pack() implementation + * + * Routines mainly used by protobuf_c_message_pack(). + * + * \ingroup internal + * @{ + */ + +/** + * Pack an unsigned 32-bit integer in base-128 varint encoding and return the + * number of bytes written, which must be 5 or less. + * + * \param value + * Value to encode. + * \param[out] out + * Packed value. + * \return + * Number of bytes written to `out`. + */ +static inline size_t +uint32_pack(uint32_t value, uint8_t *out) +{ + unsigned rv = 0; + + if (value >= 0x80) { + out[rv++] = value | 0x80; + value >>= 7; + if (value >= 0x80) { + out[rv++] = value | 0x80; + value >>= 7; + if (value >= 0x80) { + out[rv++] = value | 0x80; + value >>= 7; + if (value >= 0x80) { + out[rv++] = value | 0x80; + value >>= 7; + } + } + } + } + /* assert: value<128 */ + out[rv++] = value; + return rv; +} + +/** + * Pack a signed 32-bit integer and return the number of bytes written. + * Negative numbers are encoded as two's complement 64-bit integers. + * + * \param value + * Value to encode. + * \param[out] out + * Packed value. + * \return + * Number of bytes written to `out`. + */ +static inline size_t +int32_pack(int32_t value, uint8_t *out) +{ + if (value < 0) { + out[0] = value | 0x80; + out[1] = (value >> 7) | 0x80; + out[2] = (value >> 14) | 0x80; + out[3] = (value >> 21) | 0x80; + out[4] = (value >> 28) | 0x80; + out[5] = out[6] = out[7] = out[8] = 0xff; + out[9] = 0x01; + return 10; + } else { + return uint32_pack(value, out); + } +} + +/** + * Pack a signed 32-bit integer using ZigZag encoding and return the number of + * bytes written. + * + * \param value + * Value to encode. + * \param[out] out + * Packed value. + * \return + * Number of bytes written to `out`. + */ +static inline size_t +sint32_pack(int32_t value, uint8_t *out) +{ + return uint32_pack(zigzag32(value), out); +} + +/** + * Pack a 64-bit unsigned integer using base-128 varint encoding and return the + * number of bytes written. + * + * \param value + * Value to encode. + * \param[out] out + * Packed value. + * \return + * Number of bytes written to `out`. + */ +static size_t +uint64_pack(uint64_t value, uint8_t *out) +{ + uint32_t hi = (uint32_t) (value >> 32); + uint32_t lo = (uint32_t) value; + unsigned rv; + + if (hi == 0) + return uint32_pack((uint32_t) lo, out); + out[0] = (lo) | 0x80; + out[1] = (lo >> 7) | 0x80; + out[2] = (lo >> 14) | 0x80; + out[3] = (lo >> 21) | 0x80; + if (hi < 8) { + out[4] = (hi << 4) | (lo >> 28); + return 5; + } else { + out[4] = ((hi & 7) << 4) | (lo >> 28) | 0x80; + hi >>= 3; + } + rv = 5; + while (hi >= 128) { + out[rv++] = hi | 0x80; + hi >>= 7; + } + out[rv++] = hi; + return rv; +} + +/** + * Pack a 64-bit signed integer in ZigZag encoding and return the number of + * bytes written. + * + * \param value + * Value to encode. + * \param[out] out + * Packed value. + * \return + * Number of bytes written to `out`. + */ +static inline size_t +sint64_pack(int64_t value, uint8_t *out) +{ + return uint64_pack(zigzag64(value), out); +} + +/** + * Pack a 32-bit quantity in little-endian byte order. Used for protobuf wire + * types fixed32, sfixed32, float. Similar to "htole32". + * + * \param value + * Value to encode. + * \param[out] out + * Packed value. + * \return + * Number of bytes written to `out`. + */ +static inline size_t +fixed32_pack(uint32_t value, void *out) +{ +#if !defined(WORDS_BIGENDIAN) + memcpy(out, &value, 4); +#else + uint8_t *buf = out; + + buf[0] = value; + buf[1] = value >> 8; + buf[2] = value >> 16; + buf[3] = value >> 24; +#endif + return 4; +} + +/** + * Pack a 64-bit quantity in little-endian byte order. Used for protobuf wire + * types fixed64, sfixed64, double. Similar to "htole64". + * + * \todo The big-endian impl is really only good for 32-bit machines, a 64-bit + * version would be appreciated, plus a way to decide to use 64-bit math where + * convenient. + * + * \param value + * Value to encode. + * \param[out] out + * Packed value. + * \return + * Number of bytes written to `out`. + */ +static inline size_t +fixed64_pack(uint64_t value, void *out) +{ +#if !defined(WORDS_BIGENDIAN) + memcpy(out, &value, 8); +#else + fixed32_pack(value, out); + fixed32_pack(value >> 32, ((char *) out) + 4); +#endif + return 8; +} + +/** + * Pack a boolean value as an integer and return the number of bytes written. + * + * \todo Perhaps on some platforms *out = !!value would be a better impl, b/c + * that is idiomatic C++ in some STL implementations. + * + * \param value + * Value to encode. + * \param[out] out + * Packed value. + * \return + * Number of bytes written to `out`. + */ +static inline size_t +boolean_pack(protobuf_c_boolean value, uint8_t *out) +{ + *out = value ? TRUE : FALSE; + return 1; +} + +/** + * Pack a NUL-terminated C string and return the number of bytes written. The + * output includes a length delimiter. + * + * The NULL pointer is treated as an empty string. This isn't really necessary, + * but it allows people to leave required strings blank. (See Issue #13 in the + * bug tracker for a little more explanation). + * + * \param str + * String to encode. + * \param[out] out + * Packed value. + * \return + * Number of bytes written to `out`. + */ +static inline size_t +string_pack(const char *str, uint8_t *out) +{ + if (str == NULL) { + out[0] = 0; + return 1; + } else { + size_t len = strlen(str); + size_t rv = uint32_pack(len, out); + memcpy(out + rv, str, len); + return rv + len; + } +} + +/** + * Pack a ProtobufCBinaryData and return the number of bytes written. The output + * includes a length delimiter. + * + * \param bd + * ProtobufCBinaryData to encode. + * \param[out] out + * Packed value. + * \return + * Number of bytes written to `out`. + */ +static inline size_t +binary_data_pack(const ProtobufCBinaryData *bd, uint8_t *out) +{ + size_t len = bd->len; + size_t rv = uint32_pack(len, out); + memcpy(out + rv, bd->data, len); + return rv + len; +} + +/** + * Pack a ProtobufCMessage and return the number of bytes written. The output + * includes a length delimiter. + * + * \param message + * ProtobufCMessage object to pack. + * \param[out] out + * Packed message. + * \return + * Number of bytes written to `out`. + */ +static inline size_t +prefixed_message_pack(const ProtobufCMessage *message, uint8_t *out) +{ + if (message == NULL) { + out[0] = 0; + return 1; + } else { + size_t rv = protobuf_c_message_pack(message, out + 1); + uint32_t rv_packed_size = uint32_size(rv); + if (rv_packed_size != 1) + memmove(out + rv_packed_size, out + 1, rv); + return uint32_pack(rv, out) + rv; + } +} + +/** + * Pack a field tag. + * + * Wire-type will be added in required_field_pack(). + * + * \todo Just call uint64_pack on 64-bit platforms. + * + * \param id + * Tag value to encode. + * \param[out] out + * Packed value. + * \return + * Number of bytes written to `out`. + */ +static size_t +tag_pack(uint32_t id, uint8_t *out) +{ + if (id < (1 << (32 - 3))) + return uint32_pack(id << 3, out); + else + return uint64_pack(((uint64_t) id) << 3, out); +} + +/** + * Pack a required field and return the number of bytes written. + * + * \param field + * Field descriptor. + * \param member + * The field member. + * \param[out] out + * Packed value. + * \return + * Number of bytes written to `out`. + */ +static size_t +required_field_pack(const ProtobufCFieldDescriptor *field, + const void *member, uint8_t *out) +{ + size_t rv = tag_pack(field->id, out); + + switch (field->type) { + case PROTOBUF_C_TYPE_SINT32: + out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT; + return rv + sint32_pack(*(const int32_t *) member, out + rv); + case PROTOBUF_C_TYPE_INT32: + out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT; + return rv + int32_pack(*(const uint32_t *) member, out + rv); + case PROTOBUF_C_TYPE_UINT32: + case PROTOBUF_C_TYPE_ENUM: + out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT; + return rv + uint32_pack(*(const uint32_t *) member, out + rv); + case PROTOBUF_C_TYPE_SINT64: + out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT; + return rv + sint64_pack(*(const int64_t *) member, out + rv); + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: + out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT; + return rv + uint64_pack(*(const uint64_t *) member, out + rv); + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: + out[0] |= PROTOBUF_C_WIRE_TYPE_32BIT; + return rv + fixed32_pack(*(const uint32_t *) member, out + rv); + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: + out[0] |= PROTOBUF_C_WIRE_TYPE_64BIT; + return rv + fixed64_pack(*(const uint64_t *) member, out + rv); + case PROTOBUF_C_TYPE_BOOL: + out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT; + return rv + boolean_pack(*(const protobuf_c_boolean *) member, out + rv); + case PROTOBUF_C_TYPE_STRING: + out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED; + return rv + string_pack(*(char *const *) member, out + rv); + case PROTOBUF_C_TYPE_BYTES: + out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED; + return rv + binary_data_pack((const ProtobufCBinaryData *) member, out + rv); + case PROTOBUF_C_TYPE_MESSAGE: + out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED; + return rv + prefixed_message_pack(*(ProtobufCMessage * const *) member, out + rv); + } + PROTOBUF_C__ASSERT_NOT_REACHED(); + return 0; +} + +/** + * Pack an optional field and return the number of bytes written. + * + * \param field + * Field descriptor. + * \param has + * Whether the field is set. + * \param member + * The field member. + * \param[out] out + * Packed value. + * \return + * Number of bytes written to `out`. + */ +static size_t +optional_field_pack(const ProtobufCFieldDescriptor *field, + const protobuf_c_boolean *has, + const void *member, uint8_t *out) +{ + if (field->type == PROTOBUF_C_TYPE_MESSAGE || + field->type == PROTOBUF_C_TYPE_STRING) + { + const void *ptr = *(const void * const *) member; + if (ptr == NULL || ptr == field->default_value) + return 0; + } else { + if (!*has) + return 0; + } + return required_field_pack(field, member, out); +} + +/** + * Given a field type, return the in-memory size. + * + * \todo Implement as a table lookup. + * + * \param type + * Field type. + * \return + * Size of the field. + */ +static inline size_t +sizeof_elt_in_repeated_array(ProtobufCType type) +{ + switch (type) { + case PROTOBUF_C_TYPE_SINT32: + case PROTOBUF_C_TYPE_INT32: + case PROTOBUF_C_TYPE_UINT32: + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: + case PROTOBUF_C_TYPE_ENUM: + return 4; + case PROTOBUF_C_TYPE_SINT64: + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: + return 8; + case PROTOBUF_C_TYPE_BOOL: + return sizeof(protobuf_c_boolean); + case PROTOBUF_C_TYPE_STRING: + case PROTOBUF_C_TYPE_MESSAGE: + return sizeof(void *); + case PROTOBUF_C_TYPE_BYTES: + return sizeof(ProtobufCBinaryData); + } + PROTOBUF_C__ASSERT_NOT_REACHED(); + return 0; +} + +/** + * Pack an array of 32-bit quantities. + * + * \param[out] out + * Destination. + * \param[in] in + * Source. + * \param[in] n + * Number of elements in the source array. + */ +static void +copy_to_little_endian_32(void *out, const void *in, const unsigned n) +{ +#if !defined(WORDS_BIGENDIAN) + memcpy(out, in, n * 4); +#else + unsigned i; + const uint32_t *ini = in; + for (i = 0; i < n; i++) + fixed32_pack(ini[i], (uint32_t *) out + i); +#endif +} + +/** + * Pack an array of 64-bit quantities. + * + * \param[out] out + * Destination. + * \param[in] in + * Source. + * \param[in] n + * Number of elements in the source array. + */ +static void +copy_to_little_endian_64(void *out, const void *in, const unsigned n) +{ +#if !defined(WORDS_BIGENDIAN) + memcpy(out, in, n * 8); +#else + unsigned i; + const uint64_t *ini = in; + for (i = 0; i < n; i++) + fixed64_pack(ini[i], (uint64_t *) out + i); +#endif +} + +/** + * Get the minimum number of bytes required to pack a field value of a + * particular type. + * + * \param type + * Field type. + * \return + * Number of bytes. + */ +static unsigned +get_type_min_size(ProtobufCType type) +{ + if (type == PROTOBUF_C_TYPE_SFIXED32 || + type == PROTOBUF_C_TYPE_FIXED32 || + type == PROTOBUF_C_TYPE_FLOAT) + { + return 4; + } + if (type == PROTOBUF_C_TYPE_SFIXED64 || + type == PROTOBUF_C_TYPE_FIXED64 || + type == PROTOBUF_C_TYPE_DOUBLE) + { + return 8; + } + return 1; +} + +/** + * Packs the elements of a repeated field and returns the serialised field and + * its length. + * + * \param field + * Field descriptor. + * \param count + * Number of elements in the repeated field array. + * \param member + * Pointer to the elements for this repeated field. + * \param[out] out + * Serialised representation of the repeated field. + * \return + * Number of bytes serialised to `out`. + */ +static size_t +repeated_field_pack(const ProtobufCFieldDescriptor *field, + size_t count, const void *member, uint8_t *out) +{ + void *array = *(void * const *) member; + unsigned i; + + if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED)) { + unsigned header_len; + unsigned len_start; + unsigned min_length; + unsigned payload_len; + unsigned length_size_min; + unsigned actual_length_size; + uint8_t *payload_at; + + if (count == 0) + return 0; + header_len = tag_pack(field->id, out); + out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED; + len_start = header_len; + min_length = get_type_min_size(field->type) * count; + length_size_min = uint32_size(min_length); + header_len += length_size_min; + payload_at = out + header_len; + + switch (field->type) { + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: + copy_to_little_endian_32(payload_at, array, count); + payload_at += count * 4; + break; + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: + copy_to_little_endian_64(payload_at, array, count); + payload_at += count * 8; + break; + case PROTOBUF_C_TYPE_INT32: { + const int32_t *arr = (const int32_t *) array; + for (i = 0; i < count; i++) + payload_at += int32_pack(arr[i], payload_at); + break; + } + case PROTOBUF_C_TYPE_SINT32: { + const int32_t *arr = (const int32_t *) array; + for (i = 0; i < count; i++) + payload_at += sint32_pack(arr[i], payload_at); + break; + } + case PROTOBUF_C_TYPE_SINT64: { + const int64_t *arr = (const int64_t *) array; + for (i = 0; i < count; i++) + payload_at += sint64_pack(arr[i], payload_at); + break; + } + case PROTOBUF_C_TYPE_ENUM: + case PROTOBUF_C_TYPE_UINT32: { + const uint32_t *arr = (const uint32_t *) array; + for (i = 0; i < count; i++) + payload_at += uint32_pack(arr[i], payload_at); + break; + } + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: { + const uint64_t *arr = (const uint64_t *) array; + for (i = 0; i < count; i++) + payload_at += uint64_pack(arr[i], payload_at); + break; + } + case PROTOBUF_C_TYPE_BOOL: { + const protobuf_c_boolean *arr = (const protobuf_c_boolean *) array; + for (i = 0; i < count; i++) + payload_at += boolean_pack(arr[i], payload_at); + break; + } + default: + PROTOBUF_C__ASSERT_NOT_REACHED(); + } + + payload_len = payload_at - (out + header_len); + actual_length_size = uint32_size(payload_len); + if (length_size_min != actual_length_size) { + assert(actual_length_size == length_size_min + 1); + memmove(out + header_len + 1, out + header_len, + payload_len); + header_len++; + } + uint32_pack(payload_len, out + len_start); + return header_len + payload_len; + } else { + /* not "packed" cased */ + /* CONSIDER: optimize this case a bit (by putting the loop inside the switch) */ + size_t rv = 0; + unsigned siz = sizeof_elt_in_repeated_array(field->type); + + for (i = 0; i < count; i++) { + rv += required_field_pack(field, array, out + rv); + array = (char *)array + siz; + } + return rv; + } +} + +static size_t +unknown_field_pack(const ProtobufCMessageUnknownField *field, uint8_t *out) +{ + size_t rv = tag_pack(field->tag, out); + out[0] |= field->wire_type; + memcpy(out + rv, field->data, field->len); + return rv + field->len; +} + +/**@}*/ + +size_t +protobuf_c_message_pack(const ProtobufCMessage *message, uint8_t *out) +{ + unsigned i; + size_t rv = 0; + + ASSERT_IS_MESSAGE(message); + for (i = 0; i < message->descriptor->n_fields; i++) { + const ProtobufCFieldDescriptor *field = + message->descriptor->fields + i; + const void *member = ((const char *) message) + field->offset; + + /* + * It doesn't hurt to compute qmember (a pointer to the + * quantifier field of the structure), but the pointer is only + * valid if the field is: + * - a repeated field, or + * - an optional field that isn't a pointer type + * (Meaning: not a message or a string). + */ + const void *qmember = + ((const char *) message) + field->quantifier_offset; + + if (field->label == PROTOBUF_C_LABEL_REQUIRED) { + rv += required_field_pack(field, member, out + rv); + } else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) { + /* + * Note that qmember is bogus for strings and messages, + * but it isn't used. + */ + rv += optional_field_pack(field, qmember, member, out + rv); + } else { + rv += repeated_field_pack(field, *(const size_t *) qmember, + member, out + rv); + } + } + for (i = 0; i < message->n_unknown_fields; i++) + rv += unknown_field_pack(&message->unknown_fields[i], out + rv); + return rv; +} + +/** + * \defgroup packbuf protobuf_c_message_pack_to_buffer() implementation + * + * Routines mainly used by protobuf_c_message_pack_to_buffer(). + * + * \ingroup internal + * @{ + */ + +/** + * Pack a required field to a virtual buffer. + * + * \param field + * Field descriptor. + * \param member + * The element to be packed. + * \param[out] buffer + * Virtual buffer to append data to. + * \return + * Number of bytes packed. + */ +static size_t +required_field_pack_to_buffer(const ProtobufCFieldDescriptor *field, + const void *member, ProtobufCBuffer *buffer) +{ + size_t rv; + uint8_t scratch[MAX_UINT64_ENCODED_SIZE * 2]; + + rv = tag_pack(field->id, scratch); + switch (field->type) { + case PROTOBUF_C_TYPE_SINT32: + scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT; + rv += sint32_pack(*(const int32_t *) member, scratch + rv); + buffer->append(buffer, rv, scratch); + break; + case PROTOBUF_C_TYPE_INT32: + scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT; + rv += int32_pack(*(const uint32_t *) member, scratch + rv); + buffer->append(buffer, rv, scratch); + break; + case PROTOBUF_C_TYPE_UINT32: + case PROTOBUF_C_TYPE_ENUM: + scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT; + rv += uint32_pack(*(const uint32_t *) member, scratch + rv); + buffer->append(buffer, rv, scratch); + break; + case PROTOBUF_C_TYPE_SINT64: + scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT; + rv += sint64_pack(*(const int64_t *) member, scratch + rv); + buffer->append(buffer, rv, scratch); + break; + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: + scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT; + rv += uint64_pack(*(const uint64_t *) member, scratch + rv); + buffer->append(buffer, rv, scratch); + break; + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: + scratch[0] |= PROTOBUF_C_WIRE_TYPE_32BIT; + rv += fixed32_pack(*(const uint32_t *) member, scratch + rv); + buffer->append(buffer, rv, scratch); + break; + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: + scratch[0] |= PROTOBUF_C_WIRE_TYPE_64BIT; + rv += fixed64_pack(*(const uint64_t *) member, scratch + rv); + buffer->append(buffer, rv, scratch); + break; + case PROTOBUF_C_TYPE_BOOL: + scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT; + rv += boolean_pack(*(const protobuf_c_boolean *) member, scratch + rv); + buffer->append(buffer, rv, scratch); + break; + case PROTOBUF_C_TYPE_STRING: { + const char *str = *(char *const *) member; + size_t sublen = str ? strlen(str) : 0; + + scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED; + rv += uint32_pack(sublen, scratch + rv); + buffer->append(buffer, rv, scratch); + buffer->append(buffer, sublen, (const uint8_t *) str); + rv += sublen; + break; + } + case PROTOBUF_C_TYPE_BYTES: { + const ProtobufCBinaryData *bd = ((const ProtobufCBinaryData *) member); + size_t sublen = bd->len; + + scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED; + rv += uint32_pack(sublen, scratch + rv); + buffer->append(buffer, rv, scratch); + buffer->append(buffer, sublen, bd->data); + rv += sublen; + break; + } + case PROTOBUF_C_TYPE_MESSAGE: { + uint8_t simple_buffer_scratch[256]; + size_t sublen; + const ProtobufCMessage *msg = *(ProtobufCMessage * const *) member; + ProtobufCBufferSimple simple_buffer = + PROTOBUF_C_BUFFER_SIMPLE_INIT(simple_buffer_scratch); + + scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED; + if (msg == NULL) + sublen = 0; + else + sublen = protobuf_c_message_pack_to_buffer(msg, &simple_buffer.base); + rv += uint32_pack(sublen, scratch + rv); + buffer->append(buffer, rv, scratch); + buffer->append(buffer, sublen, simple_buffer.data); + rv += sublen; + PROTOBUF_C_BUFFER_SIMPLE_CLEAR(&simple_buffer); + break; + } + default: + PROTOBUF_C__ASSERT_NOT_REACHED(); + } + return rv; +} + +/** + * Pack an optional field to a buffer. + * + * \param field + * Field descriptor. + * \param has + * Whether the field is set. + * \param member + * The element to be packed. + * \param[out] buffer + * Virtual buffer to append data to. + * \return + * Number of bytes serialised to `buffer`. + */ +static size_t +optional_field_pack_to_buffer(const ProtobufCFieldDescriptor *field, + const protobuf_c_boolean *has, + const void *member, ProtobufCBuffer *buffer) +{ + if (field->type == PROTOBUF_C_TYPE_MESSAGE || + field->type == PROTOBUF_C_TYPE_STRING) + { + const void *ptr = *(const void *const *) member; + if (ptr == NULL || ptr == field->default_value) + return 0; + } else { + if (!*has) + return 0; + } + return required_field_pack_to_buffer(field, member, buffer); +} + +/** + * Get the packed size of an array of same field type. + * + * \param field + * Field descriptor. + * \param count + * Number of elements of this type. + * \param array + * The elements to get the size of. + * \return + * Number of bytes required. + */ +static size_t +get_packed_payload_length(const ProtobufCFieldDescriptor *field, + unsigned count, const void *array) +{ + unsigned rv = 0; + unsigned i; + + switch (field->type) { + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: + return count * 4; + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: + return count * 8; + case PROTOBUF_C_TYPE_INT32: { + const int32_t *arr = (const int32_t *) array; + for (i = 0; i < count; i++) + rv += int32_size(arr[i]); + break; + } + case PROTOBUF_C_TYPE_SINT32: { + const int32_t *arr = (const int32_t *) array; + for (i = 0; i < count; i++) + rv += sint32_size(arr[i]); + break; + } + case PROTOBUF_C_TYPE_ENUM: + case PROTOBUF_C_TYPE_UINT32: { + const uint32_t *arr = (const uint32_t *) array; + for (i = 0; i < count; i++) + rv += uint32_size(arr[i]); + break; + } + case PROTOBUF_C_TYPE_SINT64: { + const int64_t *arr = (const int64_t *) array; + for (i = 0; i < count; i++) + rv += sint64_size(arr[i]); + break; + } + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: { + const uint64_t *arr = (const uint64_t *) array; + for (i = 0; i < count; i++) + rv += uint64_size(arr[i]); + break; + } + case PROTOBUF_C_TYPE_BOOL: + return count; + default: + PROTOBUF_C__ASSERT_NOT_REACHED(); + } + return rv; +} + +/** + * Pack an array of same field type to a virtual buffer. + * + * \param field + * Field descriptor. + * \param count + * Number of elements of this type. + * \param array + * The elements to get the size of. + * \param[out] buffer + * Virtual buffer to append data to. + * \return + * Number of bytes packed. + */ +static size_t +pack_buffer_packed_payload(const ProtobufCFieldDescriptor *field, + unsigned count, const void *array, + ProtobufCBuffer *buffer) +{ + uint8_t scratch[16]; + size_t rv = 0; + unsigned i; + + switch (field->type) { + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: +#if !defined(WORDS_BIGENDIAN) + rv = count * 4; + goto no_packing_needed; +#else + for (i = 0; i < count; i++) { + unsigned len = fixed32_pack(((uint32_t *) array)[i], scratch); + buffer->append(buffer, len, scratch); + rv += len; + } + break; +#endif + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: +#if !defined(WORDS_BIGENDIAN) + rv = count * 8; + goto no_packing_needed; +#else + for (i = 0; i < count; i++) { + unsigned len = fixed64_pack(((uint64_t *) array)[i], scratch); + buffer->append(buffer, len, scratch); + rv += len; + } + break; +#endif + case PROTOBUF_C_TYPE_INT32: + for (i = 0; i < count; i++) { + unsigned len = int32_pack(((int32_t *) array)[i], scratch); + buffer->append(buffer, len, scratch); + rv += len; + } + break; + case PROTOBUF_C_TYPE_SINT32: + for (i = 0; i < count; i++) { + unsigned len = sint32_pack(((int32_t *) array)[i], scratch); + buffer->append(buffer, len, scratch); + rv += len; + } + break; + case PROTOBUF_C_TYPE_ENUM: + case PROTOBUF_C_TYPE_UINT32: + for (i = 0; i < count; i++) { + unsigned len = uint32_pack(((uint32_t *) array)[i], scratch); + buffer->append(buffer, len, scratch); + rv += len; + } + break; + case PROTOBUF_C_TYPE_SINT64: + for (i = 0; i < count; i++) { + unsigned len = sint64_pack(((int64_t *) array)[i], scratch); + buffer->append(buffer, len, scratch); + rv += len; + } + break; + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: + for (i = 0; i < count; i++) { + unsigned len = uint64_pack(((uint64_t *) array)[i], scratch); + buffer->append(buffer, len, scratch); + rv += len; + } + break; + case PROTOBUF_C_TYPE_BOOL: + for (i = 0; i < count; i++) { + unsigned len = boolean_pack(((protobuf_c_boolean *) array)[i], scratch); + buffer->append(buffer, len, scratch); + rv += len; + } + return count; + default: + PROTOBUF_C__ASSERT_NOT_REACHED(); + } + return rv; + +no_packing_needed: + buffer->append(buffer, rv, array); + return rv; +} + +static size_t +repeated_field_pack_to_buffer(const ProtobufCFieldDescriptor *field, + unsigned count, const void *member, + ProtobufCBuffer *buffer) +{ + char *array = *(char * const *) member; + + if (count == 0) + return 0; + if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED)) { + uint8_t scratch[MAX_UINT64_ENCODED_SIZE * 2]; + size_t rv = tag_pack(field->id, scratch); + size_t payload_len = get_packed_payload_length(field, count, array); + size_t tmp; + + scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED; + rv += uint32_pack(payload_len, scratch + rv); + buffer->append(buffer, rv, scratch); + tmp = pack_buffer_packed_payload(field, count, array, buffer); + assert(tmp == payload_len); + return rv + payload_len; + } else { + size_t siz; + unsigned i; + /* CONSIDER: optimize this case a bit (by putting the loop inside the switch) */ + unsigned rv = 0; + + siz = sizeof_elt_in_repeated_array(field->type); + for (i = 0; i < count; i++) { + rv += required_field_pack_to_buffer(field, array, buffer); + array += siz; + } + return rv; + } +} + +static size_t +unknown_field_pack_to_buffer(const ProtobufCMessageUnknownField *field, + ProtobufCBuffer *buffer) +{ + uint8_t header[MAX_UINT64_ENCODED_SIZE]; + size_t rv = tag_pack(field->tag, header); + + header[0] |= field->wire_type; + buffer->append(buffer, rv, header); + buffer->append(buffer, field->len, field->data); + return rv + field->len; +} + +/**@}*/ + +size_t +protobuf_c_message_pack_to_buffer(const ProtobufCMessage *message, + ProtobufCBuffer *buffer) +{ + unsigned i; + size_t rv = 0; + + ASSERT_IS_MESSAGE(message); + for (i = 0; i < message->descriptor->n_fields; i++) { + const ProtobufCFieldDescriptor *field = + message->descriptor->fields + i; + const void *member = + ((const char *) message) + field->offset; + const void *qmember = + ((const char *) message) + field->quantifier_offset; + + if (field->label == PROTOBUF_C_LABEL_REQUIRED) { + rv += required_field_pack_to_buffer(field, member, buffer); + } else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) { + rv += optional_field_pack_to_buffer( + field, + qmember, + member, + buffer + ); + } else { + rv += repeated_field_pack_to_buffer( + field, + *(const size_t *) qmember, + member, + buffer + ); + } + } + for (i = 0; i < message->n_unknown_fields; i++) + rv += unknown_field_pack_to_buffer(&message->unknown_fields[i], buffer); + + return rv; +} + +/** + * \defgroup unpack unpacking implementation + * + * Routines mainly used by the unpacking functions. + * + * \ingroup internal + * @{ + */ + +static inline int +int_range_lookup(unsigned n_ranges, const ProtobufCIntRange *ranges, int value) +{ + unsigned n; + unsigned start; + + if (n_ranges == 0) + return -1; + start = 0; + n = n_ranges; + while (n > 1) { + unsigned mid = start + n / 2; + + if (value < ranges[mid].start_value) { + n = mid - start; + } else if (value >= ranges[mid].start_value + + (int) (ranges[mid + 1].orig_index - + ranges[mid].orig_index)) + { + unsigned new_start = mid + 1; + n = start + n - new_start; + start = new_start; + } else + return (value - ranges[mid].start_value) + + ranges[mid].orig_index; + } + if (n > 0) { + unsigned start_orig_index = ranges[start].orig_index; + unsigned range_size = + ranges[start + 1].orig_index - start_orig_index; + + if (ranges[start].start_value <= value && + value < (int) (ranges[start].start_value + range_size)) + { + return (value - ranges[start].start_value) + + start_orig_index; + } + } + return -1; +} + +static size_t +parse_tag_and_wiretype(size_t len, + const uint8_t *data, + uint32_t *tag_out, + ProtobufCWireType *wiretype_out) +{ + unsigned max_rv = len > 5 ? 5 : len; + uint32_t tag = (data[0] & 0x7f) >> 3; + unsigned shift = 4; + unsigned rv; + + *wiretype_out = data[0] & 7; + if ((data[0] & 0x80) == 0) { + *tag_out = tag; + return 1; + } + for (rv = 1; rv < max_rv; rv++) { + if (data[rv] & 0x80) { + tag |= (data[rv] & 0x7f) << shift; + shift += 7; + } else { + tag |= data[rv] << shift; + *tag_out = tag; + return rv + 1; + } + } + return 0; /* error: bad header */ +} + +/* sizeof(ScannedMember) must be <= (1<<BOUND_SIZEOF_SCANNED_MEMBER_LOG2) */ +#define BOUND_SIZEOF_SCANNED_MEMBER_LOG2 5 +typedef struct _ScannedMember ScannedMember; +/** Field as it's being read. */ +struct _ScannedMember { + uint32_t tag; /**< Field tag. */ + uint8_t wire_type; /**< Field type. */ + uint8_t length_prefix_len; /**< Prefix length. */ + const ProtobufCFieldDescriptor *field; /**< Field descriptor. */ + size_t len; /**< Field length. */ + const uint8_t *data; /**< Pointer to field data. */ +}; + +static inline uint32_t +scan_length_prefixed_data(size_t len, const uint8_t *data, + size_t *prefix_len_out) +{ + unsigned hdr_max = len < 5 ? len : 5; + unsigned hdr_len; + uint32_t val = 0; + unsigned i; + unsigned shift = 0; + + for (i = 0; i < hdr_max; i++) { + val |= (data[i] & 0x7f) << shift; + shift += 7; + if ((data[i] & 0x80) == 0) + break; + } + if (i == hdr_max) { + PROTOBUF_C_UNPACK_ERROR("error parsing length for length-prefixed data"); + return 0; + } + hdr_len = i + 1; + *prefix_len_out = hdr_len; + if (hdr_len + val > len) { + PROTOBUF_C_UNPACK_ERROR("data too short after length-prefix of %u", val); + return 0; + } + return hdr_len + val; +} + +static size_t +max_b128_numbers(size_t len, const uint8_t *data) +{ + size_t rv = 0; + while (len--) + if ((*data++ & 0x80) == 0) + ++rv; + return rv; +} + +/**@}*/ + +/** + * Merge earlier message into a latter message. + * + * For numeric types and strings, if the same value appears multiple + * times, the parser accepts the last value it sees. For embedded + * message fields, the parser merges multiple instances of the same + * field. That is, all singular scalar fields in the latter instance + * replace those in the former, singular embedded messages are merged, + * and repeated fields are concatenated. + * + * The earlier message should be freed after calling this function, as + * some of its fields may have been reused and changed to their default + * values during the merge. + */ +static protobuf_c_boolean +merge_messages(ProtobufCMessage *earlier_msg, + ProtobufCMessage *latter_msg, + ProtobufCAllocator *allocator) +{ + unsigned i; + const ProtobufCFieldDescriptor *fields = + earlier_msg->descriptor->fields; + for (i = 0; i < latter_msg->descriptor->n_fields; i++) { + if (fields[i].label == PROTOBUF_C_LABEL_REPEATED) { + size_t *n_earlier = + STRUCT_MEMBER_PTR(size_t, earlier_msg, + fields[i].quantifier_offset); + uint8_t **p_earlier = + STRUCT_MEMBER_PTR(uint8_t *, earlier_msg, + fields[i].offset); + size_t *n_latter = + STRUCT_MEMBER_PTR(size_t, latter_msg, + fields[i].quantifier_offset); + uint8_t **p_latter = + STRUCT_MEMBER_PTR(uint8_t *, latter_msg, + fields[i].offset); + + if (*n_earlier > 0) { + if (*n_latter > 0) { + /* Concatenate the repeated field */ + size_t el_size = + sizeof_elt_in_repeated_array(fields[i].type); + uint8_t *new_field; + + new_field = do_alloc(allocator, + (*n_earlier + *n_latter) * el_size); + if (!new_field) + return FALSE; + + memcpy(new_field, *p_earlier, + *n_earlier * el_size); + memcpy(new_field + + *n_earlier * el_size, + *p_latter, + *n_latter * el_size); + + do_free(allocator, *p_latter); + do_free(allocator, *p_earlier); + *p_latter = new_field; + *n_latter = *n_earlier + *n_latter; + } else { + /* Zero copy the repeated field from the earlier message */ + *n_latter = *n_earlier; + *p_latter = *p_earlier; + } + /* Make sure the field does not get double freed */ + *n_earlier = 0; + *p_earlier = 0; + } + } else if (fields[i].type == PROTOBUF_C_TYPE_MESSAGE) { + ProtobufCMessage **em = + STRUCT_MEMBER_PTR(ProtobufCMessage *, + earlier_msg, + fields[i].offset); + ProtobufCMessage **lm = + STRUCT_MEMBER_PTR(ProtobufCMessage *, + latter_msg, + fields[i].offset); + if (*em != NULL) { + if (*lm != NULL) { + if (!merge_messages + (*em, *lm, allocator)) + return FALSE; + } else { + /* Zero copy the optional message */ + assert(fields[i].label == + PROTOBUF_C_LABEL_OPTIONAL); + *lm = *em; + *em = NULL; + } + } + } else if (fields[i].label == PROTOBUF_C_LABEL_OPTIONAL) { + size_t el_size = 0; + protobuf_c_boolean need_to_merge = FALSE; + void *earlier_elem = + STRUCT_MEMBER_P(earlier_msg, fields[i].offset); + void *latter_elem = + STRUCT_MEMBER_P(latter_msg, fields[i].offset); + const void *def_val = fields[i].default_value; + + switch (fields[i].type) { + case PROTOBUF_C_TYPE_BYTES: { + uint8_t *e_data = + ((ProtobufCBinaryData *) earlier_elem)->data; + uint8_t *l_data = + ((ProtobufCBinaryData *) latter_elem)->data; + const ProtobufCBinaryData *d_bd = + (ProtobufCBinaryData *) def_val; + el_size = sizeof(ProtobufCBinaryData); + + need_to_merge = + (e_data != NULL && + (d_bd != NULL && + e_data != d_bd->data)) && + (l_data == NULL || + (d_bd != NULL && + l_data == d_bd->data)); + break; + } + case PROTOBUF_C_TYPE_STRING: { + char *e_str = *(char **) earlier_elem; + char *l_str = *(char **) latter_elem; + const char *d_str = def_val; + el_size = sizeof(char *); + + need_to_merge = e_str != d_str && l_str == d_str; + break; + } + default: { + el_size = sizeof_elt_in_repeated_array(fields[i].type); + + need_to_merge = + STRUCT_MEMBER(protobuf_c_boolean, + earlier_msg, + fields[i].quantifier_offset) && + !STRUCT_MEMBER(protobuf_c_boolean, + latter_msg, + fields[i].quantifier_offset); + break; + } + } + + if (need_to_merge) { + memcpy(latter_elem, earlier_elem, el_size); + /* + * Reset the element from the old message to 0 + * to make sure earlier message deallocation + * doesn't corrupt zero-copied data in the new + * message, earlier message will be freed after + * this function is called anyway + */ + memset(earlier_elem, 0, el_size); + + if (fields[i].quantifier_offset != 0) { + /* Set the has field, if applicable */ + STRUCT_MEMBER(protobuf_c_boolean, + latter_msg, + fields[i]. + quantifier_offset) = TRUE; + STRUCT_MEMBER(protobuf_c_boolean, + earlier_msg, + fields[i]. + quantifier_offset) = FALSE; + } + } + } + } + return TRUE; +} + +/** + * Count packed elements. + * + * Given a raw slab of packed-repeated values, determine the number of + * elements. This function detects certain kinds of errors but not + * others; the remaining error checking is done by + * parse_packed_repeated_member(). + */ +static protobuf_c_boolean +count_packed_elements(ProtobufCType type, + size_t len, const uint8_t *data, size_t *count_out) +{ + switch (type) { + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: + if (len % 4 != 0) { + PROTOBUF_C_UNPACK_ERROR("length must be a multiple of 4 for fixed-length 32-bit types"); + return FALSE; + } + *count_out = len / 4; + return TRUE; + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: + if (len % 8 != 0) { + PROTOBUF_C_UNPACK_ERROR("length must be a multiple of 8 for fixed-length 64-bit types"); + return FALSE; + } + *count_out = len / 8; + return TRUE; + case PROTOBUF_C_TYPE_INT32: + case PROTOBUF_C_TYPE_SINT32: + case PROTOBUF_C_TYPE_ENUM: + case PROTOBUF_C_TYPE_UINT32: + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_SINT64: + case PROTOBUF_C_TYPE_UINT64: + *count_out = max_b128_numbers(len, data); + return TRUE; + case PROTOBUF_C_TYPE_BOOL: + *count_out = len; + return TRUE; + case PROTOBUF_C_TYPE_STRING: + case PROTOBUF_C_TYPE_BYTES: + case PROTOBUF_C_TYPE_MESSAGE: + default: + PROTOBUF_C_UNPACK_ERROR("bad protobuf-c type %u for packed-repeated", type); + return FALSE; + } +} + +static inline uint32_t +parse_uint32(unsigned len, const uint8_t *data) +{ + uint32_t rv = data[0] & 0x7f; + if (len > 1) { + rv |= ((uint32_t) (data[1] & 0x7f) << 7); + if (len > 2) { + rv |= ((uint32_t) (data[2] & 0x7f) << 14); + if (len > 3) { + rv |= ((uint32_t) (data[3] & 0x7f) << 21); + if (len > 4) + rv |= ((uint32_t) (data[4]) << 28); + } + } + } + return rv; +} + +static inline uint32_t +parse_int32(unsigned len, const uint8_t *data) +{ + return parse_uint32(len, data); +} + +static inline int32_t +unzigzag32(uint32_t v) +{ + if (v & 1) + return -(v >> 1) - 1; + else + return v >> 1; +} + +static inline uint32_t +parse_fixed_uint32(const uint8_t *data) +{ +#if !defined(WORDS_BIGENDIAN) + uint32_t t; + memcpy(&t, data, 4); + return t; +#else + return data[0] | + ((uint32_t) (data[1]) << 8) | + ((uint32_t) (data[2]) << 16) | + ((uint32_t) (data[3]) << 24); +#endif +} + +static uint64_t +parse_uint64(unsigned len, const uint8_t *data) +{ + unsigned shift, i; + uint64_t rv; + + if (len < 5) + return parse_uint32(len, data); + rv = ((uint64_t) (data[0] & 0x7f)) | + ((uint64_t) (data[1] & 0x7f) << 7) | + ((uint64_t) (data[2] & 0x7f) << 14) | + ((uint64_t) (data[3] & 0x7f) << 21); + shift = 28; + for (i = 4; i < len; i++) { + rv |= (((uint64_t) (data[i] & 0x7f)) << shift); + shift += 7; + } + return rv; +} + +static inline int64_t +unzigzag64(uint64_t v) +{ + if (v & 1) + return -(v >> 1) - 1; + else + return v >> 1; +} + +static inline uint64_t +parse_fixed_uint64(const uint8_t *data) +{ +#if !defined(WORDS_BIGENDIAN) + uint64_t t; + memcpy(&t, data, 8); + return t; +#else + return (uint64_t) parse_fixed_uint32(data) | + (((uint64_t) parse_fixed_uint32(data + 4)) << 32); +#endif +} + +static protobuf_c_boolean +parse_boolean(unsigned len, const uint8_t *data) +{ + unsigned i; + for (i = 0; i < len; i++) + if (data[i] & 0x7f) + return TRUE; + return FALSE; +} + +static protobuf_c_boolean +parse_required_member(ScannedMember *scanned_member, + void *member, + ProtobufCAllocator *allocator, + protobuf_c_boolean maybe_clear) +{ + unsigned len = scanned_member->len; + const uint8_t *data = scanned_member->data; + ProtobufCWireType wire_type = scanned_member->wire_type; + + switch (scanned_member->field->type) { + case PROTOBUF_C_TYPE_INT32: + if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT) + return FALSE; + *(uint32_t *) member = parse_int32(len, data); + return TRUE; + case PROTOBUF_C_TYPE_UINT32: + if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT) + return FALSE; + *(uint32_t *) member = parse_uint32(len, data); + return TRUE; + case PROTOBUF_C_TYPE_SINT32: + if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT) + return FALSE; + *(int32_t *) member = unzigzag32(parse_uint32(len, data)); + return TRUE; + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: + if (wire_type != PROTOBUF_C_WIRE_TYPE_32BIT) + return FALSE; + *(uint32_t *) member = parse_fixed_uint32(data); + return TRUE; + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: + if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT) + return FALSE; + *(uint64_t *) member = parse_uint64(len, data); + return TRUE; + case PROTOBUF_C_TYPE_SINT64: + if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT) + return FALSE; + *(int64_t *) member = unzigzag64(parse_uint64(len, data)); + return TRUE; + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: + if (wire_type != PROTOBUF_C_WIRE_TYPE_64BIT) + return FALSE; + *(uint64_t *) member = parse_fixed_uint64(data); + return TRUE; + case PROTOBUF_C_TYPE_BOOL: + *(protobuf_c_boolean *) member = parse_boolean(len, data); + return TRUE; + case PROTOBUF_C_TYPE_ENUM: + if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT) + return FALSE; + *(uint32_t *) member = parse_uint32(len, data); + return TRUE; + case PROTOBUF_C_TYPE_STRING: { + char **pstr = member; + unsigned pref_len = scanned_member->length_prefix_len; + + if (wire_type != PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED) + return FALSE; + + if (maybe_clear && *pstr != NULL) { + const char *def = scanned_member->field->default_value; + if (*pstr != NULL && *pstr != def) + do_free(allocator, *pstr); + } + *pstr = do_alloc(allocator, len - pref_len + 1); + if (*pstr == NULL) + return FALSE; + memcpy(*pstr, data + pref_len, len - pref_len); + (*pstr)[len - pref_len] = 0; + return TRUE; + } + case PROTOBUF_C_TYPE_BYTES: { + ProtobufCBinaryData *bd = member; + const ProtobufCBinaryData *def_bd; + unsigned pref_len = scanned_member->length_prefix_len; + + if (wire_type != PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED) + return FALSE; + + def_bd = scanned_member->field->default_value; + if (maybe_clear && + bd->data != NULL && + (def_bd == NULL || bd->data != def_bd->data)) + { + do_free(allocator, bd->data); + } + if (len - pref_len > 0) { + bd->data = do_alloc(allocator, len - pref_len); + if (bd->data == NULL) + return FALSE; + memcpy(bd->data, data + pref_len, len - pref_len); + } else { + bd->data = NULL; + } + bd->len = len - pref_len; + return TRUE; + } + case PROTOBUF_C_TYPE_MESSAGE: { + ProtobufCMessage **pmessage = member; + ProtobufCMessage *subm; + const ProtobufCMessage *def_mess; + protobuf_c_boolean merge_successful = TRUE; + unsigned pref_len = scanned_member->length_prefix_len; + + if (wire_type != PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED) + return FALSE; + + def_mess = scanned_member->field->default_value; + subm = protobuf_c_message_unpack(scanned_member->field->descriptor, + allocator, + len - pref_len, + data + pref_len); + + if (maybe_clear && + *pmessage != NULL && + *pmessage != def_mess) + { + if (subm != NULL) + merge_successful = merge_messages(*pmessage, subm, allocator); + /* Delete the previous message */ + protobuf_c_message_free_unpacked(*pmessage, allocator); + } + *pmessage = subm; + if (subm == NULL || !merge_successful) + return FALSE; + return TRUE; + } + } + return FALSE; +} + +static protobuf_c_boolean +parse_optional_member(ScannedMember *scanned_member, + void *member, + ProtobufCMessage *message, + ProtobufCAllocator *allocator) +{ + if (!parse_required_member(scanned_member, member, allocator, TRUE)) + return FALSE; + if (scanned_member->field->quantifier_offset != 0) + STRUCT_MEMBER(protobuf_c_boolean, + message, + scanned_member->field->quantifier_offset) = TRUE; + return TRUE; +} + +static protobuf_c_boolean +parse_repeated_member(ScannedMember *scanned_member, + void *member, + ProtobufCMessage *message, + ProtobufCAllocator *allocator) +{ + const ProtobufCFieldDescriptor *field = scanned_member->field; + size_t *p_n = STRUCT_MEMBER_PTR(size_t, message, field->quantifier_offset); + size_t siz = sizeof_elt_in_repeated_array(field->type); + char *array = *(char **) member; + + if (!parse_required_member(scanned_member, array + siz * (*p_n), + allocator, FALSE)) + { + return FALSE; + } + *p_n += 1; + return TRUE; +} + +static unsigned +scan_varint(unsigned len, const uint8_t *data) +{ + unsigned i; + if (len > 10) + len = 10; + for (i = 0; i < len; i++) + if ((data[i] & 0x80) == 0) + break; + if (i == len) + return 0; + return i + 1; +} + +static protobuf_c_boolean +parse_packed_repeated_member(ScannedMember *scanned_member, + void *member, + ProtobufCMessage *message) +{ + const ProtobufCFieldDescriptor *field = scanned_member->field; + size_t *p_n = STRUCT_MEMBER_PTR(size_t, message, field->quantifier_offset); + size_t siz = sizeof_elt_in_repeated_array(field->type); + void *array = *(char **) member + siz * (*p_n); + const uint8_t *at = scanned_member->data + scanned_member->length_prefix_len; + size_t rem = scanned_member->len - scanned_member->length_prefix_len; + size_t count = 0; + unsigned i; + + switch (field->type) { + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: + count = (scanned_member->len - scanned_member->length_prefix_len) / 4; +#if !defined(WORDS_BIGENDIAN) + goto no_unpacking_needed; +#else + for (i = 0; i < count; i++) { + ((uint32_t *) array)[i] = parse_fixed_uint32(at); + at += 4; + } + break; +#endif + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: + count = (scanned_member->len - scanned_member->length_prefix_len) / 8; +#if !defined(WORDS_BIGENDIAN) + goto no_unpacking_needed; +#else + for (i = 0; i < count; i++) { + ((uint64_t *) array)[i] = parse_fixed_uint64(at); + at += 8; + } + break; +#endif + case PROTOBUF_C_TYPE_INT32: + while (rem > 0) { + unsigned s = scan_varint(rem, at); + if (s == 0) { + PROTOBUF_C_UNPACK_ERROR("bad packed-repeated int32 value"); + return FALSE; + } + ((int32_t *) array)[count++] = parse_int32(s, at); + at += s; + rem -= s; + } + break; + case PROTOBUF_C_TYPE_SINT32: + while (rem > 0) { + unsigned s = scan_varint(rem, at); + if (s == 0) { + PROTOBUF_C_UNPACK_ERROR("bad packed-repeated sint32 value"); + return FALSE; + } + ((int32_t *) array)[count++] = unzigzag32(parse_uint32(s, at)); + at += s; + rem -= s; + } + break; + case PROTOBUF_C_TYPE_ENUM: + case PROTOBUF_C_TYPE_UINT32: + while (rem > 0) { + unsigned s = scan_varint(rem, at); + if (s == 0) { + PROTOBUF_C_UNPACK_ERROR("bad packed-repeated enum or uint32 value"); + return FALSE; + } + ((uint32_t *) array)[count++] = parse_uint32(s, at); + at += s; + rem -= s; + } + break; + + case PROTOBUF_C_TYPE_SINT64: + while (rem > 0) { + unsigned s = scan_varint(rem, at); + if (s == 0) { + PROTOBUF_C_UNPACK_ERROR("bad packed-repeated sint64 value"); + return FALSE; + } + ((int64_t *) array)[count++] = unzigzag64(parse_uint64(s, at)); + at += s; + rem -= s; + } + break; + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: + while (rem > 0) { + unsigned s = scan_varint(rem, at); + if (s == 0) { + PROTOBUF_C_UNPACK_ERROR("bad packed-repeated int64/uint64 value"); + return FALSE; + } + ((int64_t *) array)[count++] = parse_uint64(s, at); + at += s; + rem -= s; + } + break; + case PROTOBUF_C_TYPE_BOOL: + count = rem; + for (i = 0; i < count; i++) { + if (at[i] > 1) { + PROTOBUF_C_UNPACK_ERROR("bad packed-repeated boolean value"); + return FALSE; + } + ((protobuf_c_boolean *) array)[i] = at[i]; + } + break; + default: + PROTOBUF_C__ASSERT_NOT_REACHED(); + } + *p_n += count; + return TRUE; + +#if !defined(WORDS_BIGENDIAN) +no_unpacking_needed: + memcpy(array, at, count * siz); + *p_n += count; + return TRUE; +#endif +} + +static protobuf_c_boolean +is_packable_type(ProtobufCType type) +{ + return + type != PROTOBUF_C_TYPE_STRING && + type != PROTOBUF_C_TYPE_BYTES && + type != PROTOBUF_C_TYPE_MESSAGE; +} + +static protobuf_c_boolean +parse_member(ScannedMember *scanned_member, + ProtobufCMessage *message, + ProtobufCAllocator *allocator) +{ + const ProtobufCFieldDescriptor *field = scanned_member->field; + void *member; + + if (field == NULL) { + ProtobufCMessageUnknownField *ufield = + message->unknown_fields + + (message->n_unknown_fields++); + ufield->tag = scanned_member->tag; + ufield->wire_type = scanned_member->wire_type; + ufield->len = scanned_member->len; + ufield->data = do_alloc(allocator, scanned_member->len); + if (ufield->data == NULL) + return FALSE; + memcpy(ufield->data, scanned_member->data, ufield->len); + return TRUE; + } + member = (char *) message + field->offset; + switch (field->label) { + case PROTOBUF_C_LABEL_REQUIRED: + return parse_required_member(scanned_member, member, + allocator, TRUE); + case PROTOBUF_C_LABEL_OPTIONAL: + return parse_optional_member(scanned_member, member, + message, allocator); + case PROTOBUF_C_LABEL_REPEATED: + if (scanned_member->wire_type == + PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED && + (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED) || + is_packable_type(field->type))) + { + return parse_packed_repeated_member(scanned_member, + member, message); + } else { + return parse_repeated_member(scanned_member, + member, message, + allocator); + } + } + PROTOBUF_C__ASSERT_NOT_REACHED(); + return 0; +} + +/** + * Initialise messages generated by old code. + * + * This function is used if desc->message_init == NULL (which occurs + * for old code, and which would be useful to support allocating + * descriptors dynamically). + */ +static void +message_init_generic(const ProtobufCMessageDescriptor *desc, + ProtobufCMessage *message) +{ + unsigned i; + + memset(message, 0, desc->sizeof_message); + message->descriptor = desc; + for (i = 0; i < desc->n_fields; i++) { + if (desc->fields[i].default_value != NULL && + desc->fields[i].label != PROTOBUF_C_LABEL_REPEATED) + { + void *field = + STRUCT_MEMBER_P(message, desc->fields[i].offset); + const void *dv = desc->fields[i].default_value; + + switch (desc->fields[i].type) { + case PROTOBUF_C_TYPE_INT32: + case PROTOBUF_C_TYPE_SINT32: + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_UINT32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: + case PROTOBUF_C_TYPE_ENUM: + memcpy(field, dv, 4); + break; + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_SINT64: + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_UINT64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: + memcpy(field, dv, 8); + break; + case PROTOBUF_C_TYPE_BOOL: + memcpy(field, dv, sizeof(protobuf_c_boolean)); + break; + case PROTOBUF_C_TYPE_BYTES: + memcpy(field, dv, sizeof(ProtobufCBinaryData)); + break; + + case PROTOBUF_C_TYPE_STRING: + case PROTOBUF_C_TYPE_MESSAGE: + /* + * The next line essentially implements a cast + * from const, which is totally unavoidable. + */ + *(const void **) field = dv; + break; + } + } + } +} + +/**@}*/ + +/* + * ScannedMember slabs (an unpacking implementation detail). Before doing real + * unpacking, we first scan through the elements to see how many there are (for + * repeated fields), and which field to use (for non-repeated fields given + * twice). + * + * In order to avoid allocations for small messages, we keep a stack-allocated + * slab of ScannedMembers of size FIRST_SCANNED_MEMBER_SLAB_SIZE (16). After we + * fill that up, we allocate each slab twice as large as the previous one. + */ +#define FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2 4 + +/* + * The number of slabs, including the stack-allocated ones; choose the number so + * that we would overflow if we needed a slab larger than provided. + */ +#define MAX_SCANNED_MEMBER_SLAB \ + (sizeof(unsigned int)*8 - 1 \ + - BOUND_SIZEOF_SCANNED_MEMBER_LOG2 \ + - FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2) + +#define REQUIRED_FIELD_BITMAP_SET(index) \ + (required_fields_bitmap[(index)/8] |= (1<<((index)%8))) + +#define REQUIRED_FIELD_BITMAP_IS_SET(index) \ + (required_fields_bitmap[(index)/8] & (1<<((index)%8))) + +ProtobufCMessage * +protobuf_c_message_unpack(const ProtobufCMessageDescriptor *desc, + ProtobufCAllocator *allocator, + size_t len, const uint8_t *data) +{ + ProtobufCMessage *rv; + size_t rem = len; + const uint8_t *at = data; + const ProtobufCFieldDescriptor *last_field = desc->fields + 0; + ScannedMember first_member_slab[1 << + FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2]; + + /* + * scanned_member_slabs[i] is an array of arrays of ScannedMember. + * The first slab (scanned_member_slabs[0] is just a pointer to + * first_member_slab), above. All subsequent slabs will be allocated + * using the allocator. + */ + ScannedMember *scanned_member_slabs[MAX_SCANNED_MEMBER_SLAB + 1]; + unsigned which_slab = 0; /* the slab we are currently populating */ + unsigned in_slab_index = 0; /* number of members in the slab */ + size_t n_unknown = 0; + unsigned f; + unsigned j; + unsigned i_slab; + unsigned last_field_index = 0; + unsigned required_fields_bitmap_len; + unsigned char required_fields_bitmap_stack[16]; + unsigned char *required_fields_bitmap = required_fields_bitmap_stack; + protobuf_c_boolean required_fields_bitmap_alloced = FALSE; + + ASSERT_IS_MESSAGE_DESCRIPTOR(desc); + + if (allocator == NULL) + allocator = &protobuf_c__allocator; + + rv = do_alloc(allocator, desc->sizeof_message); + if (!rv) + return (NULL); + scanned_member_slabs[0] = first_member_slab; + + required_fields_bitmap_len = (desc->n_fields + 7) / 8; + if (required_fields_bitmap_len > sizeof(required_fields_bitmap_stack)) { + required_fields_bitmap = do_alloc(allocator, required_fields_bitmap_len); + if (!required_fields_bitmap) { + do_free(allocator, rv); + return (NULL); + } + required_fields_bitmap_alloced = TRUE; + } + memset(required_fields_bitmap, 0, required_fields_bitmap_len); + + /* + * Generated code always defines "message_init". However, we provide a + * fallback for (1) users of old protobuf-c generated-code that do not + * provide the function, and (2) descriptors constructed from some other + * source (most likely, direct construction from the .proto file). + */ + if (desc->message_init != NULL) + protobuf_c_message_init(desc, rv); + else + message_init_generic(desc, rv); + + while (rem > 0) { + uint32_t tag; + ProtobufCWireType wire_type; + size_t used = parse_tag_and_wiretype(rem, at, &tag, &wire_type); + const ProtobufCFieldDescriptor *field; + ScannedMember tmp; + + if (used == 0) { + PROTOBUF_C_UNPACK_ERROR("error parsing tag/wiretype at offset %u", + (unsigned) (at - data)); + goto error_cleanup_during_scan; + } + /* + * \todo Consider optimizing for field[1].id == tag, if field[1] + * exists! + */ + if (last_field == NULL || last_field->id != tag) { + /* lookup field */ + int field_index = + int_range_lookup(desc->n_field_ranges, + desc->field_ranges, + tag); + if (field_index < 0) { + field = NULL; + n_unknown++; + } else { + field = desc->fields + field_index; + last_field = field; + last_field_index = field_index; + } + } else { + field = last_field; + } + + if (field != NULL && field->label == PROTOBUF_C_LABEL_REQUIRED) + REQUIRED_FIELD_BITMAP_SET(last_field_index); + + at += used; + rem -= used; + tmp.tag = tag; + tmp.wire_type = wire_type; + tmp.field = field; + tmp.data = at; + tmp.length_prefix_len = 0; + + switch (wire_type) { + case PROTOBUF_C_WIRE_TYPE_VARINT: { + unsigned max_len = rem < 10 ? rem : 10; + unsigned i; + + for (i = 0; i < max_len; i++) + if ((at[i] & 0x80) == 0) + break; + if (i == max_len) { + PROTOBUF_C_UNPACK_ERROR("unterminated varint at offset %u", + (unsigned) (at - data)); + goto error_cleanup_during_scan; + } + tmp.len = i + 1; + break; + } + case PROTOBUF_C_WIRE_TYPE_64BIT: + if (rem < 8) { + PROTOBUF_C_UNPACK_ERROR("too short after 64bit wiretype at offset %u", + (unsigned) (at - data)); + goto error_cleanup_during_scan; + } + tmp.len = 8; + break; + case PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED: { + size_t pref_len; + + tmp.len = scan_length_prefixed_data(rem, at, &pref_len); + if (tmp.len == 0) { + /* NOTE: scan_length_prefixed_data calls UNPACK_ERROR */ + goto error_cleanup_during_scan; + } + tmp.length_prefix_len = pref_len; + break; + } + case PROTOBUF_C_WIRE_TYPE_32BIT: + if (rem < 4) { + PROTOBUF_C_UNPACK_ERROR("too short after 32bit wiretype at offset %u", + (unsigned) (at - data)); + goto error_cleanup_during_scan; + } + tmp.len = 4; + break; + default: + PROTOBUF_C_UNPACK_ERROR("unsupported tag %u at offset %u", + wire_type, (unsigned) (at - data)); + goto error_cleanup_during_scan; + } + + if (in_slab_index == (1U << + (which_slab + FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2))) + { + size_t size; + + in_slab_index = 0; + if (which_slab == MAX_SCANNED_MEMBER_SLAB) { + PROTOBUF_C_UNPACK_ERROR("too many fields"); + goto error_cleanup_during_scan; + } + which_slab++; + size = sizeof(ScannedMember) + << (which_slab + FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2); + scanned_member_slabs[which_slab] = do_alloc(allocator, size); + if (scanned_member_slabs[which_slab] == NULL) + goto error_cleanup_during_scan; + } + scanned_member_slabs[which_slab][in_slab_index++] = tmp; + + if (field != NULL && field->label == PROTOBUF_C_LABEL_REPEATED) { + size_t *n = STRUCT_MEMBER_PTR(size_t, rv, + field->quantifier_offset); + if (wire_type == PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED && + (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED) || + is_packable_type(field->type))) + { + size_t count; + if (!count_packed_elements(field->type, + tmp.len - + tmp.length_prefix_len, + tmp.data + + tmp.length_prefix_len, + &count)) + { + PROTOBUF_C_UNPACK_ERROR("counting packed elements"); + goto error_cleanup_during_scan; + } + *n += count; + } else { + *n += 1; + } + } + + at += tmp.len; + rem -= tmp.len; + } + + /* allocate space for repeated fields, also check that all required fields have been set */ + for (f = 0; f < desc->n_fields; f++) { + const ProtobufCFieldDescriptor *field = desc->fields + f; + if (field->label == PROTOBUF_C_LABEL_REPEATED) { + size_t siz = + sizeof_elt_in_repeated_array(field->type); + size_t *n_ptr = + STRUCT_MEMBER_PTR(size_t, rv, + field->quantifier_offset); + if (*n_ptr != 0) { + void *a = 0; + unsigned n = *n_ptr; + *n_ptr = 0; + assert(rv->descriptor != NULL); +#define CLEAR_REMAINING_N_PTRS() \ + for(f++;f < desc->n_fields; f++) \ + { \ + field = desc->fields + f; \ + if (field->label == PROTOBUF_C_LABEL_REPEATED) \ + STRUCT_MEMBER (size_t, rv, field->quantifier_offset) = 0; \ + } + a = do_alloc(allocator, siz * n); + if (!a) { + CLEAR_REMAINING_N_PTRS(); + goto error_cleanup; + } + STRUCT_MEMBER(void *, rv, field->offset) = a; + } + } else if (field->label == PROTOBUF_C_LABEL_REQUIRED) { + if (field->default_value == NULL && + !REQUIRED_FIELD_BITMAP_IS_SET(f)) + { + CLEAR_REMAINING_N_PTRS(); + PROTOBUF_C_UNPACK_ERROR("message '%s': missing required field '%s'", + desc->name, field->name); + goto error_cleanup; + } + } + } +#undef CLEAR_REMAINING_N_PTRS + + /* allocate space for unknown fields */ + if (n_unknown) { + rv->unknown_fields = do_alloc(allocator, + n_unknown * sizeof(ProtobufCMessageUnknownField)); + if (rv->unknown_fields == NULL) + goto error_cleanup; + } + + /* do real parsing */ + for (i_slab = 0; i_slab <= which_slab; i_slab++) { + unsigned max = (i_slab == which_slab) ? + in_slab_index : (1U << (i_slab + 4)); + ScannedMember *slab = scanned_member_slabs[i_slab]; + unsigned j; + + for (j = 0; j < max; j++) { + if (!parse_member(slab + j, rv, allocator)) { + PROTOBUF_C_UNPACK_ERROR("error parsing member %s of %s", + slab->field ? slab->field->name : "*unknown-field*", + desc->name); + goto error_cleanup; + } + } + } + + /* cleanup */ + for (j = 1; j <= which_slab; j++) + do_free(allocator, scanned_member_slabs[j]); + if (required_fields_bitmap_alloced) + do_free(allocator, required_fields_bitmap); + return rv; + +error_cleanup: + protobuf_c_message_free_unpacked(rv, allocator); + for (j = 1; j <= which_slab; j++) + do_free(allocator, scanned_member_slabs[j]); + if (required_fields_bitmap_alloced) + do_free(allocator, required_fields_bitmap); + return NULL; + +error_cleanup_during_scan: + do_free(allocator, rv); + for (j = 1; j <= which_slab; j++) + do_free(allocator, scanned_member_slabs[j]); + if (required_fields_bitmap_alloced) + do_free(allocator, required_fields_bitmap); + return NULL; +} + +void +protobuf_c_message_free_unpacked(ProtobufCMessage *message, + ProtobufCAllocator *allocator) +{ + const ProtobufCMessageDescriptor *desc = message->descriptor; + unsigned f; + + ASSERT_IS_MESSAGE(message); + if (allocator == NULL) + allocator = &protobuf_c__allocator; + message->descriptor = NULL; + for (f = 0; f < desc->n_fields; f++) { + if (desc->fields[f].label == PROTOBUF_C_LABEL_REPEATED) { + size_t n = STRUCT_MEMBER(size_t, + message, + desc->fields[f].quantifier_offset); + void *arr = STRUCT_MEMBER(void *, + message, + desc->fields[f].offset); + + if (desc->fields[f].type == PROTOBUF_C_TYPE_STRING) { + unsigned i; + for (i = 0; i < n; i++) + do_free(allocator, ((char **) arr)[i]); + } else if (desc->fields[f].type == PROTOBUF_C_TYPE_BYTES) { + unsigned i; + for (i = 0; i < n; i++) + do_free(allocator, ((ProtobufCBinaryData *) arr)[i].data); + } else if (desc->fields[f].type == PROTOBUF_C_TYPE_MESSAGE) { + unsigned i; + for (i = 0; i < n; i++) + protobuf_c_message_free_unpacked( + ((ProtobufCMessage **) arr)[i], + allocator + ); + } + if (arr != NULL) + do_free(allocator, arr); + } else if (desc->fields[f].type == PROTOBUF_C_TYPE_STRING) { + char *str = STRUCT_MEMBER(char *, message, + desc->fields[f].offset); + + if (str && str != desc->fields[f].default_value) + do_free(allocator, str); + } else if (desc->fields[f].type == PROTOBUF_C_TYPE_BYTES) { + void *data = STRUCT_MEMBER(ProtobufCBinaryData, message, + desc->fields[f].offset).data; + const ProtobufCBinaryData *default_bd; + + default_bd = desc->fields[f].default_value; + if (data != NULL && + (default_bd == NULL || + default_bd->data != data)) + { + do_free(allocator, data); + } + } else if (desc->fields[f].type == PROTOBUF_C_TYPE_MESSAGE) { + ProtobufCMessage *sm; + + sm = STRUCT_MEMBER(ProtobufCMessage *, message, + desc->fields[f].offset); + if (sm && sm != desc->fields[f].default_value) + protobuf_c_message_free_unpacked(sm, allocator); + } + } + + for (f = 0; f < message->n_unknown_fields; f++) + do_free(allocator, message->unknown_fields[f].data); + if (message->unknown_fields != NULL) + do_free(allocator, message->unknown_fields); + + do_free(allocator, message); +} + +void +protobuf_c_message_init(const ProtobufCMessageDescriptor * descriptor, + void *message) +{ + descriptor->message_init((ProtobufCMessage *) (message)); +} + +protobuf_c_boolean +protobuf_c_message_check(const ProtobufCMessage *message) +{ + unsigned i; + + if (!message || + !message->descriptor || + message->descriptor->magic != PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC) + { + return FALSE; + } + + for (i = 0; i < message->descriptor->n_fields; i++) { + const ProtobufCFieldDescriptor *f = message->descriptor->fields + i; + ProtobufCType type = f->type; + ProtobufCLabel label = f->label; + void *field = STRUCT_MEMBER_P (message, f->offset); + + if (label == PROTOBUF_C_LABEL_REPEATED) { + size_t *quantity = STRUCT_MEMBER_P (message, f->quantifier_offset); + + if (*quantity > 0 && *(void **) field == NULL) { + return FALSE; + } + + if (type == PROTOBUF_C_TYPE_MESSAGE) { + ProtobufCMessage **submessage = *(ProtobufCMessage ***) field; + unsigned j; + for (j = 0; j < *quantity; j++) { + if (!protobuf_c_message_check(submessage[j])) + return FALSE; + } + } else if (type == PROTOBUF_C_TYPE_STRING) { + char **string = *(char ***) field; + unsigned j; + for (j = 0; j < *quantity; j++) { + if (!string[j]) + return FALSE; + } + } else if (type == PROTOBUF_C_TYPE_BYTES) { + ProtobufCBinaryData *bd = *(ProtobufCBinaryData **) field; + unsigned j; + for (j = 0; j < *quantity; j++) { + if (bd[j].len > 0 && bd[j].data == NULL) + return FALSE; + } + } + + } else { /* PROTOBUF_C_LABEL_REQUIRED or PROTOBUF_C_LABEL_OPTIONAL */ + + if (type == PROTOBUF_C_TYPE_MESSAGE) { + ProtobufCMessage *submessage = *(ProtobufCMessage **) field; + if (label == PROTOBUF_C_LABEL_REQUIRED || submessage != NULL) { + if (!protobuf_c_message_check(submessage)) + return FALSE; + } + } else if (type == PROTOBUF_C_TYPE_STRING) { + char *string = *(char **) field; + if (label == PROTOBUF_C_LABEL_REQUIRED && string == NULL) + return FALSE; + } else if (type == PROTOBUF_C_TYPE_BYTES) { + protobuf_c_boolean *has = STRUCT_MEMBER_P (message, f->quantifier_offset); + ProtobufCBinaryData *bd = field; + if (label == PROTOBUF_C_LABEL_REQUIRED || *has == TRUE) { + if (bd->len > 0 && bd->data == NULL) + return FALSE; + } + } + } + } + + return TRUE; +} + +/* === services === */ + +typedef void (*GenericHandler) (void *service, + const ProtobufCMessage *input, + ProtobufCClosure closure, + void *closure_data); +void +protobuf_c_service_invoke_internal(ProtobufCService *service, + unsigned method_index, + const ProtobufCMessage *input, + ProtobufCClosure closure, + void *closure_data) +{ + GenericHandler *handlers; + GenericHandler handler; + + /* + * Verify that method_index is within range. If this fails, you are + * likely invoking a newly added method on an old service. (Although + * other memory corruption bugs can cause this assertion too.) + */ + assert(method_index < service->descriptor->n_methods); + + /* + * Get the array of virtual methods (which are enumerated by the + * generated code). + */ + handlers = (GenericHandler *) (service + 1); + + /* + * Get our method and invoke it. + * \todo Seems like handler == NULL is a situation that needs handling. + */ + handler = handlers[method_index]; + (*handler)(service, input, closure, closure_data); +} + +void +protobuf_c_service_generated_init(ProtobufCService *service, + const ProtobufCServiceDescriptor *descriptor, + ProtobufCServiceDestroy destroy) +{ + ASSERT_IS_SERVICE_DESCRIPTOR(descriptor); + service->descriptor = descriptor; + service->destroy = destroy; + service->invoke = protobuf_c_service_invoke_internal; + memset(service + 1, 0, descriptor->n_methods * sizeof(GenericHandler)); +} + +void protobuf_c_service_destroy(ProtobufCService *service) +{ + service->destroy(service); +} + +/* --- querying the descriptors --- */ + +const ProtobufCEnumValue * +protobuf_c_enum_descriptor_get_value_by_name(const ProtobufCEnumDescriptor *desc, + const char *name) +{ + unsigned start = 0; + unsigned count = desc->n_value_names; + + while (count > 1) { + unsigned mid = start + count / 2; + int rv = strcmp(desc->values_by_name[mid].name, name); + if (rv == 0) + return desc->values + desc->values_by_name[mid].index; + else if (rv < 0) { + count = start + count - (mid + 1); + start = mid + 1; + } else + count = mid - start; + } + if (count == 0) + return NULL; + if (strcmp(desc->values_by_name[start].name, name) == 0) + return desc->values + desc->values_by_name[start].index; + return NULL; +} + +const ProtobufCEnumValue * +protobuf_c_enum_descriptor_get_value(const ProtobufCEnumDescriptor *desc, + int value) +{ + int rv = int_range_lookup(desc->n_value_ranges, desc->value_ranges, value); + if (rv < 0) + return NULL; + return desc->values + rv; +} + +const ProtobufCFieldDescriptor * +protobuf_c_message_descriptor_get_field_by_name(const ProtobufCMessageDescriptor *desc, + const char *name) +{ + unsigned start = 0; + unsigned count = desc->n_fields; + const ProtobufCFieldDescriptor *field; + + while (count > 1) { + unsigned mid = start + count / 2; + int rv; + field = desc->fields + desc->fields_sorted_by_name[mid]; + rv = strcmp(field->name, name); + if (rv == 0) + return field; + else if (rv < 0) { + count = start + count - (mid + 1); + start = mid + 1; + } else + count = mid - start; + } + if (count == 0) + return NULL; + field = desc->fields + desc->fields_sorted_by_name[start]; + if (strcmp(field->name, name) == 0) + return field; + return NULL; +} + +const ProtobufCFieldDescriptor * +protobuf_c_message_descriptor_get_field(const ProtobufCMessageDescriptor *desc, + unsigned value) +{ + int rv = int_range_lookup(desc->n_field_ranges,desc->field_ranges, value); + if (rv < 0) + return NULL; + return desc->fields + rv; +} + +const ProtobufCMethodDescriptor * +protobuf_c_service_descriptor_get_method_by_name(const ProtobufCServiceDescriptor *desc, + const char *name) +{ + unsigned start = 0; + unsigned count = desc->n_methods; + + while (count > 1) { + unsigned mid = start + count / 2; + unsigned mid_index = desc->method_indices_by_name[mid]; + const char *mid_name = desc->methods[mid_index].name; + int rv = strcmp(mid_name, name); + + if (rv == 0) + return desc->methods + desc->method_indices_by_name[mid]; + if (rv < 0) { + count = start + count - (mid + 1); + start = mid + 1; + } else { + count = mid - start; + } + } + if (count == 0) + return NULL; + if (strcmp(desc->methods[desc->method_indices_by_name[start]].name, name) == 0) + return desc->methods + desc->method_indices_by_name[start]; + return NULL; +} diff --git a/libs/libaxolotl/src/protobuf-c/protobuf-c.h b/libs/libaxolotl/src/protobuf-c/protobuf-c.h new file mode 100644 index 0000000000..55156a0510 --- /dev/null +++ b/libs/libaxolotl/src/protobuf-c/protobuf-c.h @@ -0,0 +1,1079 @@ +/* + * Copyright (c) 2008-2014, Dave Benson and the protobuf-c authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/*! \file + * \mainpage Introduction + * + * This is [protobuf-c], a C implementation of [Protocol Buffers]. + * + * This file defines the public API for the `libprotobuf-c` support library. + * This API includes interfaces that can be used directly by client code as well + * as the interfaces used by the code generated by the `protoc-c` compiler. + * + * The `libprotobuf-c` support library performs the actual serialization and + * deserialization of Protocol Buffers messages. It interacts with structures, + * definitions, and metadata generated by the `protoc-c` compiler from .proto + * files. + * + * \authors Dave Benson and the `protobuf-c` authors. + * + * \copyright 2008-2014. Licensed under the terms of the [BSD-2-Clause] license. + * + * [protobuf-c]: https://github.com/protobuf-c/protobuf-c + * [Protocol Buffers]: https://developers.google.com/protocol-buffers/ + * [BSD-2-Clause]: http://opensource.org/licenses/BSD-2-Clause + * + * \page gencode Generated Code + * + * For each enum, we generate a C enum. For each message, we generate a C + * structure which can be cast to a `ProtobufCMessage`. + * + * For each enum and message, we generate a descriptor object that allows us to + * implement a kind of reflection on the structures. + * + * First, some naming conventions: + * + * - The name of the type for enums and messages and services is camel case + * (meaning WordsAreCrammedTogether) except that double underscores are used + * to delimit scopes. For example, the following `.proto` file: + * +~~~{.proto} + package foo.bar; + message BazBah { + optional int32 val = 1; + } +~~~ + * + * would generate a C type `Foo__Bar__BazBah`. + * + * - Identifiers for functions and globals are all lowercase, with camel case + * words separated by single underscores. For example, one of the function + * prototypes generated by `protoc-c` for the above example: + * +~~~{.c} +Foo__Bar__BazBah * + foo__bar__baz_bah__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +~~~ + * + * - Identifiers for enum values contain an uppercase prefix which embeds the + * package name and the enum type name. + * + * - A double underscore is used to separate further components of identifier + * names. + * + * For example, in the name of the unpack function above, the package name + * `foo.bar` has become `foo__bar`, the message name BazBah has become + * `baz_bah`, and the method name is `unpack`. These are all joined with double + * underscores to form the C identifier `foo__bar__baz_bah__unpack`. + * + * We also generate descriptor objects for messages and enums. These are + * declared in the `.pb-c.h` files: + * +~~~{.c} +extern const ProtobufCMessageDescriptor foo__bar__baz_bah__descriptor; +~~~ + * + * The message structures all begin with `ProtobufCMessageDescriptor *` which is + * sufficient to allow them to be cast to `ProtobufCMessage`. + * + * For each message defined in a `.proto` file, we generate a number of + * functions. Each function name contains a prefix based on the package name and + * message name in order to make it a unique C identifier. + * + * - `unpack()`. Unpacks data for a particular message format. Note that the + * `allocator` parameter is usually `NULL` to indicate that the system's + * `malloc()` and `free()` functions should be used for dynamically allocating + * memory. + * +~~~{.c} +Foo__Bar__BazBah * + foo__bar__baz_bah__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +~~~ + * + * - `free_unpacked()`. Frees a message object obtained with the `unpack()` + * method. + * +~~~{.c} +void foo__bar__baz_bah__free_unpacked + (Foo__Bar__BazBah *message, + ProtobufCAllocator *allocator); +~~~ + * + * - `get_packed_size()`. Calculates the length in bytes of the serialized + * representation of the message object. + * +~~~{.c} +size_t foo__bar__baz_bah__get_packed_size + (const Foo__Bar__BazBah *message); +~~~ + * + * - `pack()`. Pack a message object into a preallocated buffer. Assumes that + * the buffer is large enough. (Use `get_packed_size()` first.) + * +~~~{.c} +size_t foo__bar__baz_bah__pack + (const Foo__Bar__BazBah *message, + uint8_t *out); +~~~ + * + * - `pack_to_buffer()`. Packs a message into a "virtual buffer". This is an + * object which defines an "append bytes" callback to consume data as it is + * serialized. + * +~~~{.c} +size_t foo__bar__baz_bah__pack_to_buffer + (const Foo__Bar__BazBah *message, + ProtobufCBuffer *buffer); +~~~ + * + * \page pack Packing and unpacking messages + * + * To pack a message, first compute the packed size of the message with + * protobuf_c_message_get_packed_size(), then allocate a buffer of at least + * that size, then call protobuf_c_message_pack(). + * + * Alternatively, a message can be serialized without calculating the final size + * first. Use the protobuf_c_message_pack_to_buffer() function and provide a + * ProtobufCBuffer object which implements an "append" method that consumes + * data. + * + * To unpack a message, call the protobuf_c_message_unpack() function. The + * result can be cast to an object of the type that matches the descriptor for + * the message. + * + * The result of unpacking a message should be freed with + * protobuf_c_message_free_unpacked(). + */ + +#ifndef PROTOBUF_C_H +#define PROTOBUF_C_H + +#include <assert.h> +#include <limits.h> +#include <stddef.h> +#include <stdint.h> + +#ifdef __cplusplus +# define PROTOBUF_C__BEGIN_DECLS extern "C" { +# define PROTOBUF_C__END_DECLS } +#else +# define PROTOBUF_C__BEGIN_DECLS +# define PROTOBUF_C__END_DECLS +#endif + +PROTOBUF_C__BEGIN_DECLS + +#if defined(_WIN32) && defined(PROTOBUF_C_USE_SHARED_LIB) +# ifdef PROTOBUF_C_EXPORT +# define PROTOBUF_C__API __declspec(dllexport) +# else +# define PROTOBUF_C__API __declspec(dllimport) +# endif +#else +# define PROTOBUF_C__API +#endif + +#if !defined(PROTOBUF_C__NO_DEPRECATED) +# if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +# define PROTOBUF_C__DEPRECATED __attribute__((__deprecated__)) +# endif +#else +# define PROTOBUF_C__DEPRECATED +#endif + +#ifndef PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE + #define PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(enum_name) \ + , _##enum_name##_IS_INT_SIZE = INT_MAX +#endif + +#define PROTOBUF_C__SERVICE_DESCRIPTOR_MAGIC 0x14159bc3 +#define PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC 0x28aaeef9 +#define PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC 0x114315af + +/** + * \defgroup api Public API + * + * This is the public API for `libprotobuf-c`. These interfaces are stable and + * subject to Semantic Versioning guarantees. + * + * @{ + */ + +/** + * Values for the `flags` word in `ProtobufCFieldDescriptor`. + */ +typedef enum { + /** Set if the field is repeated and marked with the `packed` option. */ + PROTOBUF_C_FIELD_FLAG_PACKED = (1 << 0), + + /** Set if the field is marked with the `deprecated` option. */ + PROTOBUF_C_FIELD_FLAG_DEPRECATED = (1 << 1), +} ProtobufCFieldFlag; + +/** + * Message field rules. + * + * \see [Defining A Message Type] in the Protocol Buffers documentation. + * + * [Defining A Message Type]: + * https://developers.google.com/protocol-buffers/docs/proto#simple + */ +typedef enum { + /** A well-formed message must have exactly one of this field. */ + PROTOBUF_C_LABEL_REQUIRED, + + /** + * A well-formed message can have zero or one of this field (but not + * more than one). + */ + PROTOBUF_C_LABEL_OPTIONAL, + + /** + * This field can be repeated any number of times (including zero) in a + * well-formed message. The order of the repeated values will be + * preserved. + */ + PROTOBUF_C_LABEL_REPEATED, +} ProtobufCLabel; + +/** + * Field value types. + * + * \see [Scalar Value Types] in the Protocol Buffers documentation. + * + * [Scalar Value Types]: + * https://developers.google.com/protocol-buffers/docs/proto#scalar + */ +typedef enum { + PROTOBUF_C_TYPE_INT32, /**< int32 */ + PROTOBUF_C_TYPE_SINT32, /**< signed int32 */ + PROTOBUF_C_TYPE_SFIXED32, /**< signed int32 (4 bytes) */ + PROTOBUF_C_TYPE_INT64, /**< int64 */ + PROTOBUF_C_TYPE_SINT64, /**< signed int64 */ + PROTOBUF_C_TYPE_SFIXED64, /**< signed int64 (8 bytes) */ + PROTOBUF_C_TYPE_UINT32, /**< unsigned int32 */ + PROTOBUF_C_TYPE_FIXED32, /**< unsigned int32 (4 bytes) */ + PROTOBUF_C_TYPE_UINT64, /**< unsigned int64 */ + PROTOBUF_C_TYPE_FIXED64, /**< unsigned int64 (8 bytes) */ + PROTOBUF_C_TYPE_FLOAT, /**< float */ + PROTOBUF_C_TYPE_DOUBLE, /**< double */ + PROTOBUF_C_TYPE_BOOL, /**< boolean */ + PROTOBUF_C_TYPE_ENUM, /**< enumerated type */ + PROTOBUF_C_TYPE_STRING, /**< UTF-8 or ASCII string */ + PROTOBUF_C_TYPE_BYTES, /**< arbitrary byte sequence */ + PROTOBUF_C_TYPE_MESSAGE, /**< nested message */ +} ProtobufCType; + +/** + * Field wire types. + * + * \see [Message Structure] in the Protocol Buffers documentation. + * + * [Message Structure]: + * https://developers.google.com/protocol-buffers/docs/encoding#structure + */ +typedef enum { + PROTOBUF_C_WIRE_TYPE_VARINT = 0, + PROTOBUF_C_WIRE_TYPE_64BIT = 1, + PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED = 2, + /* "Start group" and "end group" wire types are unsupported. */ + PROTOBUF_C_WIRE_TYPE_32BIT = 5, +} ProtobufCWireType; + +struct ProtobufCAllocator; +struct ProtobufCBinaryData; +struct ProtobufCBuffer; +struct ProtobufCBufferSimple; +struct ProtobufCEnumDescriptor; +struct ProtobufCEnumValue; +struct ProtobufCEnumValueIndex; +struct ProtobufCFieldDescriptor; +struct ProtobufCIntRange; +struct ProtobufCMessage; +struct ProtobufCMessageDescriptor; +struct ProtobufCMessageUnknownField; +struct ProtobufCMethodDescriptor; +struct ProtobufCService; +struct ProtobufCServiceDescriptor; + +typedef struct ProtobufCAllocator ProtobufCAllocator; +typedef struct ProtobufCBinaryData ProtobufCBinaryData; +typedef struct ProtobufCBuffer ProtobufCBuffer; +typedef struct ProtobufCBufferSimple ProtobufCBufferSimple; +typedef struct ProtobufCEnumDescriptor ProtobufCEnumDescriptor; +typedef struct ProtobufCEnumValue ProtobufCEnumValue; +typedef struct ProtobufCEnumValueIndex ProtobufCEnumValueIndex; +typedef struct ProtobufCFieldDescriptor ProtobufCFieldDescriptor; +typedef struct ProtobufCIntRange ProtobufCIntRange; +typedef struct ProtobufCMessage ProtobufCMessage; +typedef struct ProtobufCMessageDescriptor ProtobufCMessageDescriptor; +typedef struct ProtobufCMessageUnknownField ProtobufCMessageUnknownField; +typedef struct ProtobufCMethodDescriptor ProtobufCMethodDescriptor; +typedef struct ProtobufCService ProtobufCService; +typedef struct ProtobufCServiceDescriptor ProtobufCServiceDescriptor; + +/** Boolean type. */ +typedef int protobuf_c_boolean; + +typedef void (*ProtobufCClosure)(const ProtobufCMessage *, void *closure_data); +typedef void (*ProtobufCMessageInit)(ProtobufCMessage *); +typedef void (*ProtobufCServiceDestroy)(ProtobufCService *); + +/** + * Structure for defining a custom memory allocator. + */ +struct ProtobufCAllocator { + /** Function to allocate memory. */ + void *(*alloc)(void *allocator_data, size_t size); + + /** Function to free memory. */ + void (*free)(void *allocator_data, void *pointer); + + /** Opaque pointer passed to `alloc` and `free` functions. */ + void *allocator_data; +}; + +/** + * Structure for the protobuf `bytes` scalar type. + * + * The data contained in a `ProtobufCBinaryData` is an arbitrary sequence of + * bytes. It may contain embedded `NUL` characters and is not required to be + * `NUL`-terminated. + */ +struct ProtobufCBinaryData { + size_t len; /**< Number of bytes in the `data` field. */ + uint8_t *data; /**< Data bytes. */ +}; + +/** + * Structure for defining a virtual append-only buffer. Used by + * protobuf_c_message_pack_to_buffer() to abstract the consumption of serialized + * bytes. + * + * `ProtobufCBuffer` "subclasses" may be defined on the stack. For example, to + * write to a `FILE` object: + * +~~~{.c} +typedef struct { + ProtobufCBuffer base; + FILE *fp; +} BufferAppendToFile; + +static void +my_buffer_file_append(ProtobufCBuffer *buffer, + size_t len, + const uint8_t *data) +{ + BufferAppendToFile *file_buf = (BufferAppendToFile *) buffer; + fwrite(data, len, 1, file_buf->fp); // XXX: No error handling! +} +~~~ + * + * To use this new type of ProtobufCBuffer, it could be called as follows: + * +~~~{.c} +... +BufferAppendToFile tmp = {0}; +tmp.base.append = my_buffer_file_append; +tmp.fp = fp; +protobuf_c_message_pack_to_buffer(&message, &tmp); +... +~~~ + */ +struct ProtobufCBuffer { + /** Append function. Consumes the `len` bytes stored at `data`. */ + void (*append)(ProtobufCBuffer *buffer, + size_t len, + const uint8_t *data); +}; + +/** + * Simple buffer "subclass" of `ProtobufCBuffer`. + * + * A `ProtobufCBufferSimple` object is declared on the stack and uses a + * scratch buffer provided by the user for the initial allocation. It performs + * exponential resizing, using dynamically allocated memory. A + * `ProtobufCBufferSimple` object can be created and used as follows: + * +~~~{.c} +uint8_t pad[128]; +ProtobufCBufferSimple simple = PROTOBUF_C_BUFFER_SIMPLE_INIT(pad); +ProtobufCBuffer *buffer = (ProtobufCBuffer *) &simple; +~~~ + * + * `buffer` can now be used with `protobuf_c_message_pack_to_buffer()`. Once a + * message has been serialized to a `ProtobufCBufferSimple` object, the + * serialized data bytes can be accessed from the `.data` field. + * + * To free the memory allocated by a `ProtobufCBufferSimple` object, if any, + * call PROTOBUF_C_BUFFER_SIMPLE_CLEAR() on the object, for example: + * +~~~{.c} +PROTOBUF_C_BUFFER_SIMPLE_CLEAR(&simple); +~~~ + * + * \see PROTOBUF_C_BUFFER_SIMPLE_INIT + * \see PROTOBUF_C_BUFFER_SIMPLE_CLEAR + */ +struct ProtobufCBufferSimple { + /** "Base class". */ + ProtobufCBuffer base; + /** Number of bytes allocated in `data`. */ + size_t alloced; + /** Number of bytes currently stored in `data`. */ + size_t len; + /** Data bytes. */ + uint8_t *data; + /** Whether `data` must be freed. */ + protobuf_c_boolean must_free_data; + /** Allocator to use. May be NULL to indicate the system allocator. */ + ProtobufCAllocator *allocator; +}; + +/** + * Describes an enumeration as a whole, with all of its values. + */ +struct ProtobufCEnumDescriptor { + /** Magic value checked to ensure that the API is used correctly. */ + uint32_t magic; + + /** The qualified name (e.g., "namespace.Type"). */ + const char *name; + /** The unqualified name as given in the .proto file (e.g., "Type"). */ + const char *short_name; + /** Identifier used in generated C code. */ + const char *c_name; + /** The dot-separated namespace. */ + const char *package_name; + + /** Number elements in `values`. */ + unsigned n_values; + /** Array of distinct values, sorted by numeric value. */ + const ProtobufCEnumValue *values; + + /** Number of elements in `values_by_name`. */ + unsigned n_value_names; + /** Array of named values, including aliases, sorted by name. */ + const ProtobufCEnumValueIndex *values_by_name; + + /** Number of elements in `value_ranges`. */ + unsigned n_value_ranges; + /** Value ranges, for faster lookups by numeric value. */ + const ProtobufCIntRange *value_ranges; + + /** Reserved for future use. */ + void *reserved1; + /** Reserved for future use. */ + void *reserved2; + /** Reserved for future use. */ + void *reserved3; + /** Reserved for future use. */ + void *reserved4; +}; + +/** + * Represents a single value of an enumeration. + */ +struct ProtobufCEnumValue { + /** The string identifying this value in the .proto file. */ + const char *name; + + /** The string identifying this value in generated C code. */ + const char *c_name; + + /** The numeric value assigned in the .proto file. */ + int value; +}; + +/** + * Used by `ProtobufCEnumDescriptor` to look up enum values. + */ +struct ProtobufCEnumValueIndex { + /** Name of the enum value. */ + const char *name; + /** Index into values[] array. */ + unsigned index; +}; + +/** + * Describes a single field in a message. + */ +struct ProtobufCFieldDescriptor { + /** Name of the field as given in the .proto file. */ + const char *name; + + /** Tag value of the field as given in the .proto file. */ + uint32_t id; + + /** Whether the field is `REQUIRED`, `OPTIONAL`, or `REPEATED`. */ + ProtobufCLabel label; + + /** The type of the field. */ + ProtobufCType type; + + /** + * The offset in bytes of the message's C structure's quantifier field + * (the `has_MEMBER` field for optional members or the `n_MEMBER` field + * for repeated members. + */ + unsigned quantifier_offset; + + /** + * The offset in bytes into the message's C structure for the member + * itself. + */ + unsigned offset; + + /** + * A type-specific descriptor. + * + * If `type` is `PROTOBUF_C_TYPE_ENUM`, then `descriptor` points to the + * corresponding `ProtobufCEnumDescriptor`. + * + * If `type` is `PROTOBUF_C_TYPE_MESSAGE`, then `descriptor` points to + * the corresponding `ProtobufCMessageDescriptor`. + * + * Otherwise this field is NULL. + */ + const void *descriptor; /* for MESSAGE and ENUM types */ + + /** The default value for this field, if defined. May be NULL. */ + const void *default_value; + + /** + * A flag word. Zero or more of the bits defined in the + * `ProtobufCFieldFlag` enum may be set. + */ + uint32_t flags; + + /** Reserved for future use. */ + unsigned reserved_flags; + /** Reserved for future use. */ + void *reserved2; + /** Reserved for future use. */ + void *reserved3; +}; + +/** + * Helper structure for optimizing int => index lookups in the case + * where the keys are mostly consecutive values, as they presumably are for + * enums and fields. + * + * The data structures requires that the values in the original array are + * sorted. + */ +struct ProtobufCIntRange { + int start_value; + unsigned orig_index; + /* + * NOTE: the number of values in the range can be inferred by looking + * at the next element's orig_index. A dummy element is added to make + * this simple. + */ +}; + +/** + * An instance of a message. + * + * `ProtobufCMessage` is a light-weight "base class" for all messages. + * + * In particular, `ProtobufCMessage` doesn't have any allocation policy + * associated with it. That's because it's common to create `ProtobufCMessage` + * objects on the stack. In fact, that's what we recommend for sending messages. + * If the object is allocated from the stack, you can't really have a memory + * leak. + * + * This means that calls to functions like protobuf_c_message_unpack() which + * return a `ProtobufCMessage` must be paired with a call to a free function, + * like protobuf_c_message_free_unpacked(). + */ +struct ProtobufCMessage { + /** The descriptor for this message type. */ + const ProtobufCMessageDescriptor *descriptor; + /** The number of elements in `unknown_fields`. */ + unsigned n_unknown_fields; + /** The fields that weren't recognized by the parser. */ + ProtobufCMessageUnknownField *unknown_fields; +}; + +/** + * Describes a message. + */ +struct ProtobufCMessageDescriptor { + /** Magic value checked to ensure that the API is used correctly. */ + uint32_t magic; + + /** The qualified name (e.g., "namespace.Type"). */ + const char *name; + /** The unqualified name as given in the .proto file (e.g., "Type"). */ + const char *short_name; + /** Identifier used in generated C code. */ + const char *c_name; + /** The dot-separated namespace. */ + const char *package_name; + + /** + * Size in bytes of the C structure representing an instance of this + * type of message. + */ + size_t sizeof_message; + + /** Number of elements in `fields`. */ + unsigned n_fields; + /** Field descriptors, sorted by tag number. */ + const ProtobufCFieldDescriptor *fields; + /** Used for looking up fields by name. */ + const unsigned *fields_sorted_by_name; + + /** Number of elements in `field_ranges`. */ + unsigned n_field_ranges; + /** Used for looking up fields by id. */ + const ProtobufCIntRange *field_ranges; + + /** Message initialisation function. */ + ProtobufCMessageInit message_init; + + /** Reserved for future use. */ + void *reserved1; + /** Reserved for future use. */ + void *reserved2; + /** Reserved for future use. */ + void *reserved3; +}; + +/** + * An unknown message field. + */ +struct ProtobufCMessageUnknownField { + /** The tag number. */ + uint32_t tag; + /** The wire type of the field. */ + ProtobufCWireType wire_type; + /** Number of bytes in `data`. */ + size_t len; + /** Field data. */ + uint8_t *data; +}; + +/** + * Method descriptor. + */ +struct ProtobufCMethodDescriptor { + /** Method name. */ + const char *name; + /** Input message descriptor. */ + const ProtobufCMessageDescriptor *input; + /** Output message descriptor. */ + const ProtobufCMessageDescriptor *output; +}; + +/** + * Service. + */ +struct ProtobufCService { + /** Service descriptor. */ + const ProtobufCServiceDescriptor *descriptor; + /** Function to invoke the service. */ + void (*invoke)(ProtobufCService *service, + unsigned method_index, + const ProtobufCMessage *input, + ProtobufCClosure closure, + void *closure_data); + /** Function to destroy the service. */ + void (*destroy)(ProtobufCService *service); +}; + +/** + * Service descriptor. + */ +struct ProtobufCServiceDescriptor { + /** Magic value checked to ensure that the API is used correctly. */ + uint32_t magic; + + /** Service name. */ + const char *name; + /** Short version of service name. */ + const char *short_name; + /** C identifier for the service name. */ + const char *c_name; + /** Package name. */ + const char *package; + /** Number of elements in `methods`. */ + unsigned n_methods; + /** Method descriptors, in the order defined in the .proto file. */ + const ProtobufCMethodDescriptor *methods; + /** Sort index of methods. */ + const unsigned *method_indices_by_name; +}; + +/** + * Get the version of the protobuf-c library. Note that this is the version of + * the library linked against, not the version of the headers compiled against. + * + * \return A string containing the version number of protobuf-c. + */ +PROTOBUF_C__API +const char * +protobuf_c_version(void); + +/** + * Get the version of the protobuf-c library. Note that this is the version of + * the library linked against, not the version of the headers compiled against. + * + * \return A 32 bit unsigned integer containing the version number of + * protobuf-c, represented in base-10 as (MAJOR*1E6) + (MINOR*1E3) + PATCH. + */ +PROTOBUF_C__API +uint32_t +protobuf_c_version_number(void); + +/** + * The version of the protobuf-c headers, represented as a string using the same + * format as protobuf_c_version(). + */ +#define PROTOBUF_C_VERSION "1.0.1" + +/** + * The version of the protobuf-c headers, represented as an integer using the + * same format as protobuf_c_version_number(). + */ +#define PROTOBUF_C_VERSION_NUMBER 1000001 + +/** + * The minimum protoc-c version which works with the current version of the + * protobuf-c headers. + */ +#define PROTOBUF_C_MIN_COMPILER_VERSION 1000000 + +/** + * Look up a `ProtobufCEnumValue` from a `ProtobufCEnumDescriptor` by name. + * + * \param desc + * The `ProtobufCEnumDescriptor` object. + * \param name + * The `name` field from the corresponding `ProtobufCEnumValue` object to + * match. + * \return + * A `ProtobufCEnumValue` object. + * \retval NULL + * If not found. + */ +PROTOBUF_C__API +const ProtobufCEnumValue * +protobuf_c_enum_descriptor_get_value_by_name( + const ProtobufCEnumDescriptor *desc, + const char *name); + +/** + * Look up a `ProtobufCEnumValue` from a `ProtobufCEnumDescriptor` by numeric + * value. + * + * \param desc + * The `ProtobufCEnumDescriptor` object. + * \param value + * The `value` field from the corresponding `ProtobufCEnumValue` object to + * match. + * + * \return + * A `ProtobufCEnumValue` object. + * \retval NULL + * If not found. + */ +PROTOBUF_C__API +const ProtobufCEnumValue * +protobuf_c_enum_descriptor_get_value( + const ProtobufCEnumDescriptor *desc, + int value); + +/** + * Look up a `ProtobufCFieldDescriptor` from a `ProtobufCMessageDescriptor` by + * the name of the field. + * + * \param desc + * The `ProtobufCMessageDescriptor` object. + * \param name + * The name of the field. + * \return + * A `ProtobufCFieldDescriptor` object. + * \retval NULL + * If not found. + */ +PROTOBUF_C__API +const ProtobufCFieldDescriptor * +protobuf_c_message_descriptor_get_field_by_name( + const ProtobufCMessageDescriptor *desc, + const char *name); + +/** + * Look up a `ProtobufCFieldDescriptor` from a `ProtobufCMessageDescriptor` by + * the tag value of the field. + * + * \param desc + * The `ProtobufCMessageDescriptor` object. + * \param value + * The tag value of the field. + * \return + * A `ProtobufCFieldDescriptor` object. + * \retval NULL + * If not found. + */ +PROTOBUF_C__API +const ProtobufCFieldDescriptor * +protobuf_c_message_descriptor_get_field( + const ProtobufCMessageDescriptor *desc, + unsigned value); + +/** + * Determine the number of bytes required to store the serialised message. + * + * \param message + * The message object to serialise. + * \return + * Number of bytes. + */ +PROTOBUF_C__API +size_t +protobuf_c_message_get_packed_size(const ProtobufCMessage *message); + +/** + * Serialise a message from its in-memory representation. + * + * This function stores the serialised bytes of the message in a pre-allocated + * buffer. + * + * \param message + * The message object to serialise. + * \param[out] out + * Buffer to store the bytes of the serialised message. This buffer must + * have enough space to store the packed message. Use + * protobuf_c_message_get_packed_size() to determine the number of bytes + * required. + * \return + * Number of bytes stored in `out`. + */ +PROTOBUF_C__API +size_t +protobuf_c_message_pack(const ProtobufCMessage *message, uint8_t *out); + +/** + * Serialise a message from its in-memory representation to a virtual buffer. + * + * This function calls the `append` method of a `ProtobufCBuffer` object to + * consume the bytes generated by the serialiser. + * + * \param message + * The message object to serialise. + * \param buffer + * The virtual buffer object. + * \return + * Number of bytes passed to the virtual buffer. + */ +PROTOBUF_C__API +size_t +protobuf_c_message_pack_to_buffer( + const ProtobufCMessage *message, + ProtobufCBuffer *buffer); + +/** + * Unpack a serialised message into an in-memory representation. + * + * \param descriptor + * The message descriptor. + * \param allocator + * `ProtobufCAllocator` to use for memory allocation. May be NULL to + * specify the default allocator. + * \param len + * Length in bytes of the serialised message. + * \param data + * Pointer to the serialised message. + * \return + * An unpacked message object. + * \retval NULL + * If an error occurred during unpacking. + */ +PROTOBUF_C__API +ProtobufCMessage * +protobuf_c_message_unpack( + const ProtobufCMessageDescriptor *descriptor, + ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); + +/** + * Free an unpacked message object. + * + * This function should be used to deallocate the memory used by a call to + * protobuf_c_message_unpack(). + * + * \param message + * The message object to free. + * \param allocator + * `ProtobufCAllocator` to use for memory deallocation. May be NULL to + * specify the default allocator. + */ +PROTOBUF_C__API +void +protobuf_c_message_free_unpacked( + ProtobufCMessage *message, + ProtobufCAllocator *allocator); + +/** + * Check the validity of a message object. + * + * Makes sure all required fields (`PROTOBUF_C_LABEL_REQUIRED`) are present. + * Recursively checks nested messages. + * + * \retval TRUE + * Message is valid. + * \retval FALSE + * Message is invalid. + */ +PROTOBUF_C__API +protobuf_c_boolean +protobuf_c_message_check(const ProtobufCMessage *); + +/** Message initialiser. */ +#define PROTOBUF_C_MESSAGE_INIT(descriptor) { descriptor, 0, NULL } + +/** + * Initialise a message object from a message descriptor. + * + * \param descriptor + * Message descriptor. + * \param message + * Allocated block of memory of size `descriptor->sizeof_message`. + */ +PROTOBUF_C__API +void +protobuf_c_message_init( + const ProtobufCMessageDescriptor *descriptor, + void *message); + +/** + * Free a service. + * + * \param service + * The service object to free. + */ +PROTOBUF_C__API +void +protobuf_c_service_destroy(ProtobufCService *service); + +/** + * Look up a `ProtobufCMethodDescriptor` by name. + * + * \param desc + * Service descriptor. + * \param name + * Name of the method. + * + * \return + * A `ProtobufCMethodDescriptor` object. + * \retval NULL + * If not found. + */ +PROTOBUF_C__API +const ProtobufCMethodDescriptor * +protobuf_c_service_descriptor_get_method_by_name( + const ProtobufCServiceDescriptor *desc, + const char *name); + +/** + * Initialise a `ProtobufCBufferSimple` object. + */ +#define PROTOBUF_C_BUFFER_SIMPLE_INIT(array_of_bytes) \ +{ \ + { protobuf_c_buffer_simple_append }, \ + sizeof(array_of_bytes), \ + 0, \ + (array_of_bytes), \ + 0, \ + NULL \ +} + +/** + * Clear a `ProtobufCBufferSimple` object, freeing any allocated memory. + */ +#define PROTOBUF_C_BUFFER_SIMPLE_CLEAR(simp_buf) \ +do { \ + if ((simp_buf)->must_free_data) { \ + if ((simp_buf)->allocator != NULL) \ + (simp_buf)->allocator->free( \ + (simp_buf)->allocator, \ + (simp_buf)->data); \ + else \ + free((simp_buf)->data); \ + } \ +} while (0) + +/** + * The `append` method for `ProtobufCBufferSimple`. + * + * \param buffer + * The buffer object to append to. Must actually be a + * `ProtobufCBufferSimple` object. + * \param len + * Number of bytes in `data`. + * \param data + * Data to append. + */ +PROTOBUF_C__API +void +protobuf_c_buffer_simple_append( + ProtobufCBuffer *buffer, + size_t len, + const unsigned char *data); + +PROTOBUF_C__API +void +protobuf_c_service_generated_init( + ProtobufCService *service, + const ProtobufCServiceDescriptor *descriptor, + ProtobufCServiceDestroy destroy); + +PROTOBUF_C__API +void +protobuf_c_service_invoke_internal( + ProtobufCService *service, + unsigned method_index, + const ProtobufCMessage *input, + ProtobufCClosure closure, + void *closure_data); + +/**@}*/ + +PROTOBUF_C__END_DECLS + +#endif /* PROTOBUF_C_H */ diff --git a/libs/libaxolotl/src/protocol.c b/libs/libaxolotl/src/protocol.c new file mode 100644 index 0000000000..5d29a5b138 --- /dev/null +++ b/libs/libaxolotl/src/protocol.c @@ -0,0 +1,1797 @@ +#include "protocol.h" + +#include <string.h> +#include <assert.h> + +#include "axolotl_internal.h" +#include "curve.h" +#include "WhisperTextProtocol.pb-c.h" + +#define WHISPER_MESSAGE_MAC_LENGTH 8 +#define SIGNATURE_LENGTH 64 + +struct key_exchange_message +{ + axolotl_type_base base; + + uint8_t version; + uint8_t supported_version; + uint32_t sequence; + uint32_t flags; + + ec_public_key *base_key; + uint8_t base_key_signature[CURVE_SIGNATURE_LEN]; + + ec_public_key *ratchet_key; + ec_public_key *identity_key; + + axolotl_buffer *serialized; +}; + +struct ciphertext_message +{ + axolotl_type_base base; + int message_type; + axolotl_context *global_context; + axolotl_buffer *serialized; +}; + +struct whisper_message +{ + ciphertext_message base_message; + uint8_t message_version; + ec_public_key *sender_ratchet_key; + uint32_t counter; + uint32_t previous_counter; + axolotl_buffer *ciphertext; +}; + +struct pre_key_whisper_message +{ + ciphertext_message base_message; + uint8_t version; + uint32_t registration_id; + int has_pre_key_id; + uint32_t pre_key_id; + uint32_t signed_pre_key_id; + ec_public_key *base_key; + ec_public_key *identity_key; + whisper_message *message; +}; + +struct sender_key_message +{ + ciphertext_message base_message; + uint8_t message_version; + uint32_t key_id; + uint32_t iteration; + axolotl_buffer *ciphertext; +}; + +struct sender_key_distribution_message +{ + ciphertext_message base_message; + uint32_t id; + uint32_t iteration; + axolotl_buffer *chain_key; + ec_public_key *signature_key; +}; + +static int key_exchange_message_serialize(axolotl_buffer **buffer, const key_exchange_message *message); + +static int whisper_message_serialize(axolotl_buffer **buffer, const whisper_message *message); +static int whisper_message_get_mac(axolotl_buffer **buffer, + uint8_t message_version, + ec_public_key *sender_identity_key, + ec_public_key *receiver_identity_key, + const uint8_t *mac_key, size_t mac_key_len, + const uint8_t *serialized, size_t serialized_len, + axolotl_context *global_context); + +static int pre_key_whisper_message_serialize(axolotl_buffer **buffer, const pre_key_whisper_message *message); + +static int sender_key_message_serialize(axolotl_buffer **buffer, const sender_key_message *message, ec_private_key *signature_key, axolotl_context *global_context); +static int sender_key_distribution_message_serialize(axolotl_buffer **buffer, const sender_key_distribution_message *message); + +int key_exchange_message_create(key_exchange_message **message, + uint8_t message_version, uint32_t sequence, uint32_t flags, + ec_public_key *base_key, uint8_t *base_key_signature, + ec_public_key *ratchet_key, ec_public_key *identity_key) +{ + int result = 0; + key_exchange_message *result_message = 0; + + result_message = malloc(sizeof(key_exchange_message)); + if(!result_message) { + result = AX_ERR_NOMEM; + goto complete; + } + memset(result_message, 0, sizeof(key_exchange_message)); + AXOLOTL_INIT(result_message, key_exchange_message_destroy); + + result_message->supported_version = CIPHERTEXT_CURRENT_VERSION; + result_message->version = message_version; + result_message->sequence = sequence; + result_message->flags = flags; + + AXOLOTL_REF(base_key); + result_message->base_key = base_key; + + memcpy(result_message->base_key_signature, base_key_signature, sizeof(result_message->base_key_signature)); + + AXOLOTL_REF(ratchet_key); + result_message->ratchet_key = ratchet_key; + + AXOLOTL_REF(identity_key); + result_message->identity_key = identity_key; + + result = key_exchange_message_serialize(&result_message->serialized, result_message); + if(result < 0) { + goto complete; + } + result = 0; + +complete: + if(result >= 0) { + *message = result_message; + } + else { + if(result_message) { + AXOLOTL_UNREF(result_message); + } + } + return result; +} + +static int key_exchange_message_serialize(axolotl_buffer **buffer, const key_exchange_message *message) +{ + int result = 0; + size_t result_size = 0; + axolotl_buffer *result_buf = 0; + Textsecure__KeyExchangeMessage message_structure = TEXTSECURE__KEY_EXCHANGE_MESSAGE__INIT; + size_t len = 0; + uint8_t *data = 0; + + uint8_t version = (message->version << 4) | message->supported_version; + + message_structure.has_id = 1; + message_structure.id = (message->sequence << 5) | message->flags; + + if(message->base_key) { + message_structure.has_basekey = 1; + result = ec_public_key_serialize_protobuf(&message_structure.basekey, message->base_key); + if(result < 0) { + goto complete; + } + } + + if(message->ratchet_key) { + message_structure.has_ratchetkey = 1; + result = ec_public_key_serialize_protobuf(&message_structure.ratchetkey, message->ratchet_key); + if(result < 0) { + goto complete; + } + } + + if(message->identity_key) { + message_structure.has_identitykey = 1; + result = ec_public_key_serialize_protobuf(&message_structure.identitykey, message->identity_key); + if(result < 0) { + goto complete; + } + } + + if(message->version >= 3) { + message_structure.has_basekeysignature = 1; + message_structure.basekeysignature.len = sizeof(message->base_key_signature); + message_structure.basekeysignature.data = malloc(message_structure.basekeysignature.len); + if(!message_structure.basekeysignature.data) { + result = AX_ERR_NOMEM; + goto complete; + } + memcpy(message_structure.basekeysignature.data, message->base_key_signature, message_structure.basekeysignature.len); + } + + len = textsecure__key_exchange_message__get_packed_size(&message_structure); + + result_buf = axolotl_buffer_alloc(len + 1); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(result_buf); + data[0] = version; + + result_size = textsecure__key_exchange_message__pack(&message_structure, data + 1); + if(result_size != len) { + axolotl_buffer_free(result_buf); + result = AX_ERR_INVALID_PROTO_BUF; + result_buf = 0; + goto complete; + } + +complete: + if(message_structure.basekey.data) { + free(message_structure.basekey.data); + } + if(message_structure.ratchetkey.data) { + free(message_structure.ratchetkey.data); + } + if(message_structure.identitykey.data) { + free(message_structure.identitykey.data); + } + if(message_structure.basekeysignature.data) { + free(message_structure.basekeysignature.data); + } + if(result >= 0) { + *buffer = result_buf; + } + return result; +} + +int key_exchange_message_deserialize(key_exchange_message **message, const uint8_t *data, size_t len, axolotl_context *global_context) +{ + int result = 0; + key_exchange_message *result_message = 0; + Textsecure__KeyExchangeMessage *message_structure = 0; + uint8_t version = 0; + uint8_t supported_version = 0; + + if(!data || len <= 1) { + result = AX_ERR_INVAL; + goto complete; + } + + version = (data[0] & 0xF0) >> 4; + supported_version = data[0] & 0x0F; + + if(version <= CIPHERTEXT_UNSUPPORTED_VERSION) { + axolotl_log(global_context, AX_LOG_WARNING, "Unsupported legacy version: %d", version); + result = AX_ERR_LEGACY_MESSAGE; + goto complete; + } + + if(version > CIPHERTEXT_CURRENT_VERSION) { + axolotl_log(global_context, AX_LOG_WARNING, "Unknown version: %d", version); + result = AX_ERR_INVALID_VERSION; + goto complete; + } + + message_structure = textsecure__key_exchange_message__unpack(0, len - 1, data + 1); + if(!message_structure) { + result = AX_ERR_INVALID_PROTO_BUF; + goto complete; + } + + if(!message_structure->has_id || !message_structure->has_basekey + || !message_structure->has_ratchetkey || !message_structure->has_identitykey + || (version >= 3 && !message_structure->has_basekeysignature)) { + axolotl_log(global_context, AX_LOG_WARNING, "Some required fields missing"); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + if(message_structure->has_basekeysignature && message_structure->basekeysignature.len != CURVE_SIGNATURE_LEN) { + axolotl_log(global_context, AX_LOG_WARNING, "Invalid base key signature length: %d", message_structure->basekeysignature.len); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + result_message = malloc(sizeof(key_exchange_message)); + if(!result_message) { + result = AX_ERR_NOMEM; + goto complete; + } + memset(result_message, 0, sizeof(key_exchange_message)); + AXOLOTL_INIT(result_message, key_exchange_message_destroy); + + result_message->version = version; + result_message->supported_version = supported_version; + result_message->sequence = message_structure->id >> 5; + result_message->flags = message_structure->id & 0x1F; + + result = curve_decode_point(&result_message->base_key, + message_structure->basekey.data, message_structure->basekey.len, global_context); + if(result < 0) { + goto complete; + } + + if(message_structure->has_basekeysignature) { + memcpy(result_message->base_key_signature, + message_structure->basekeysignature.data, + message_structure->basekeysignature.len); + } + + result = curve_decode_point(&result_message->ratchet_key, + message_structure->ratchetkey.data, message_structure->ratchetkey.len, global_context); + if(result < 0) { + goto complete; + } + + result = curve_decode_point(&result_message->identity_key, + message_structure->identitykey.data, message_structure->identitykey.len, global_context); + if(result < 0) { + goto complete; + } + + result_message->serialized = axolotl_buffer_alloc(len); + if(!result_message->serialized) { + result = AX_ERR_NOMEM; + goto complete; + } + memcpy(result_message->serialized, data, len); + +complete: + if(message_structure) { + textsecure__key_exchange_message__free_unpacked(message_structure, 0); + } + if(result >= 0) { + *message = result_message; + } + else { + if(result_message) { + AXOLOTL_UNREF(result_message); + } + } + return result; +} + +axolotl_buffer *key_exchange_message_get_serialized(const key_exchange_message *message) +{ + assert(message); + assert(message->serialized); + return message->serialized; +} + +uint8_t key_exchange_message_get_version(const key_exchange_message *message) +{ + assert(message); + return message->version; +} + +ec_public_key *key_exchange_message_get_base_key(const key_exchange_message *message) +{ + assert(message); + return message->base_key; +} + +uint8_t *key_exchange_message_get_base_key_signature(key_exchange_message *message) +{ + assert(message); + return message->base_key_signature; +} + +ec_public_key *key_exchange_message_get_ratchet_key(const key_exchange_message *message) +{ + assert(message); + return message->ratchet_key; +} + +ec_public_key *key_exchange_message_get_identity_key(const key_exchange_message *message) +{ + assert(message); + return message->identity_key; +} + +int key_exchange_message_has_identity_key(const key_exchange_message *message) +{ + assert(message); + return 1; +} + +uint8_t key_exchange_message_get_max_version(const key_exchange_message *message) +{ + assert(message); + return message->supported_version; +} + +int key_exchange_message_is_response(const key_exchange_message *message) +{ + assert(message); + return (message->flags & KEY_EXCHANGE_RESPONSE_FLAG) != 0; +} + +int key_exchange_message_is_initiate(const key_exchange_message *message) +{ + assert(message); + return (message->flags & KEY_EXCHANGE_INITIATE_FLAG) != 0; +} + +int key_exchange_message_is_response_for_simultaneous_initiate(const key_exchange_message *message) +{ + assert(message); + return (message->flags & KEY_EXCHANGE_SIMULTAENOUS_INITIATE_FLAG) != 0; +} + +uint32_t key_exchange_message_get_flags(const key_exchange_message *message) +{ + assert(message); + return message->flags; +} + +uint32_t key_exchange_message_get_sequence(const key_exchange_message *message) +{ + assert(message); + return message->sequence; +} + +void key_exchange_message_destroy(axolotl_type_base *type) +{ + key_exchange_message *message = (key_exchange_message *)type; + AXOLOTL_UNREF(message->base_key); + AXOLOTL_UNREF(message->ratchet_key); + AXOLOTL_UNREF(message->identity_key); + if(message->serialized) { + axolotl_buffer_free(message->serialized); + } + free(message); +} + +/*------------------------------------------------------------------------*/ + +int ciphertext_message_get_type(const ciphertext_message *message) +{ + assert(message); + return message->message_type; +} + +axolotl_buffer *ciphertext_message_get_serialized(const ciphertext_message *message) +{ + assert(message); + return message->serialized; +} + +/*------------------------------------------------------------------------*/ + +int whisper_message_create(whisper_message **message, uint8_t message_version, + const uint8_t *mac_key, size_t mac_key_len, + ec_public_key *sender_ratchet_key, uint32_t counter, uint32_t previous_counter, + const uint8_t *ciphertext, size_t ciphertext_len, + ec_public_key *sender_identity_key, ec_public_key *receiver_identity_key, + axolotl_context *global_context) +{ + int result = 0; + axolotl_buffer *message_buf = 0; + axolotl_buffer *mac_buf = 0; + whisper_message *result_message = 0; + + assert(global_context); + + result_message = malloc(sizeof(whisper_message)); + if(!result_message) { + return AX_ERR_NOMEM; + } + memset(result_message, 0, sizeof(whisper_message)); + AXOLOTL_INIT(result_message, whisper_message_destroy); + + result_message->base_message.message_type = CIPHERTEXT_WHISPER_TYPE; + result_message->base_message.global_context = global_context; + + AXOLOTL_REF(sender_ratchet_key); + result_message->sender_ratchet_key = sender_ratchet_key; + + result_message->counter = counter; + result_message->previous_counter = previous_counter; + + result_message->ciphertext = axolotl_buffer_create(ciphertext, ciphertext_len); + if(!result_message->ciphertext) { + result = AX_ERR_NOMEM; + goto complete; + } + + result_message->message_version = message_version; + + result = whisper_message_serialize(&message_buf, result_message); + if(result < 0) { + goto complete; + } + + result = whisper_message_get_mac(&mac_buf, + message_version, sender_identity_key, receiver_identity_key, + mac_key, mac_key_len, + axolotl_buffer_data(message_buf), + axolotl_buffer_len(message_buf), + global_context); + if(result < 0) { + goto complete; + } + + result_message->base_message.serialized = axolotl_buffer_append( + message_buf, + axolotl_buffer_data(mac_buf), + axolotl_buffer_len(mac_buf)); + if(result_message->base_message.serialized) { + message_buf = 0; + } + else { + result = AX_ERR_NOMEM; + } + +complete: + if(message_buf) { + axolotl_buffer_free(message_buf); + } + if(mac_buf) { + axolotl_buffer_free(mac_buf); + } + if(result >= 0) { + result = 0; + *message = result_message; + } + else { + if(result_message) { + AXOLOTL_UNREF(result_message); + } + } + return result; +} + +static int whisper_message_serialize(axolotl_buffer **buffer, const whisper_message *message) +{ + int result = 0; + size_t result_size = 0; + axolotl_buffer *result_buf = 0; + Textsecure__WhisperMessage message_structure = TEXTSECURE__WHISPER_MESSAGE__INIT; + size_t len = 0; + uint8_t *data = 0; + + uint8_t version = (message->message_version << 4) | CIPHERTEXT_CURRENT_VERSION; + + result = ec_public_key_serialize_protobuf(&message_structure.ratchetkey, message->sender_ratchet_key); + if(result < 0) { + goto complete; + } + message_structure.has_ratchetkey = 1; + + message_structure.counter = message->counter; + message_structure.has_counter = 1; + + message_structure.previouscounter = message->previous_counter; + message_structure.has_previouscounter = 1; + + message_structure.ciphertext.data = axolotl_buffer_data(message->ciphertext); + message_structure.ciphertext.len = axolotl_buffer_len(message->ciphertext); + message_structure.has_ciphertext = 1; + + len = textsecure__whisper_message__get_packed_size(&message_structure); + + result_buf = axolotl_buffer_alloc(len + 1); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(result_buf); + data[0] = version; + + result_size = textsecure__whisper_message__pack(&message_structure, data + 1); + if(result_size != len) { + axolotl_buffer_free(result_buf); + result = AX_ERR_INVALID_PROTO_BUF; + result_buf = 0; + goto complete; + } + +complete: + if(message_structure.ratchetkey.data) { + free(message_structure.ratchetkey.data); + } + if(result >= 0) { + *buffer = result_buf; + } + return result; +} + +int whisper_message_deserialize(whisper_message **message, const uint8_t *data, size_t len, axolotl_context *global_context) +{ + int result = 0; + whisper_message *result_message = 0; + Textsecure__WhisperMessage *message_structure = 0; + uint8_t version = 0; + uint8_t *ciphertext_data = 0; + uint8_t *serialized_data = 0; + const uint8_t *message_data = 0; + size_t message_len = 0; + + assert(global_context); + + if(!data || len <= 1 + WHISPER_MESSAGE_MAC_LENGTH) { + result = AX_ERR_INVAL; + goto complete; + } + + version = (data[0] & 0xF0) >> 4; + + /* Set some pointers and lengths for the sections of the raw data */ + message_data = data + 1; + message_len = len - 1 - WHISPER_MESSAGE_MAC_LENGTH; + + if(version <= CIPHERTEXT_UNSUPPORTED_VERSION) { + axolotl_log(global_context, AX_LOG_WARNING, "Unsupported legacy version: %d", version); + result = AX_ERR_LEGACY_MESSAGE; + goto complete; + } + + if(version > CIPHERTEXT_CURRENT_VERSION) { + axolotl_log(global_context, AX_LOG_WARNING, "Unknown version: %d", version); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + message_structure = textsecure__whisper_message__unpack(0, message_len, message_data); + if(!message_structure) { + result = AX_ERR_INVALID_PROTO_BUF; + goto complete; + } + + if(!message_structure->has_ciphertext + || !message_structure->has_counter + || !message_structure->has_ratchetkey) { + axolotl_log(global_context, AX_LOG_WARNING, "Incomplete message"); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + result_message = malloc(sizeof(whisper_message)); + if(!result_message) { + result = AX_ERR_NOMEM; + goto complete; + } + memset(result_message, 0, sizeof(whisper_message)); + AXOLOTL_INIT(result_message, whisper_message_destroy); + + result_message->base_message.message_type = CIPHERTEXT_WHISPER_TYPE; + result_message->base_message.global_context = global_context; + + result = curve_decode_point(&result_message->sender_ratchet_key, + message_structure->ratchetkey.data, message_structure->ratchetkey.len, global_context); + if(result < 0) { + goto complete; + } + + result_message->message_version = version; + result_message->counter = message_structure->counter; + result_message->previous_counter = message_structure->previouscounter; + + result_message->ciphertext = axolotl_buffer_alloc(message_structure->ciphertext.len); + if(!result_message->ciphertext) { + result = AX_ERR_NOMEM; + goto complete; + } + ciphertext_data = axolotl_buffer_data(result_message->ciphertext); + memcpy(ciphertext_data, message_structure->ciphertext.data, message_structure->ciphertext.len); + + result_message->base_message.serialized = axolotl_buffer_alloc(len); + if(!result_message->base_message.serialized) { + result = AX_ERR_NOMEM; + goto complete; + } + serialized_data = axolotl_buffer_data(result_message->base_message.serialized); + memcpy(serialized_data, data, len); + +complete: + if(message_structure) { + textsecure__whisper_message__free_unpacked(message_structure, 0); + } + if(result >= 0) { + *message = result_message; + } + else { + if(result_message) { + AXOLOTL_UNREF(result_message); + } + } + return result; +} + +int whisper_message_copy(whisper_message **message, whisper_message *other_message, axolotl_context *global_context) +{ + int result = 0; + whisper_message *result_message = 0; + + assert(other_message); + assert(global_context); + + result = whisper_message_deserialize( + &result_message, + axolotl_buffer_data(other_message->base_message.serialized), + axolotl_buffer_len(other_message->base_message.serialized), + global_context); + if(result >= 0) { + *message = result_message; + } + + return result; +} + +ec_public_key *whisper_message_get_sender_ratchet_key(const whisper_message *message) +{ + assert(message); + return message->sender_ratchet_key; +} + +uint8_t whisper_message_get_message_version(const whisper_message *message) +{ + assert(message); + return message->message_version; +} + +uint32_t whisper_message_get_counter(const whisper_message *message) +{ + assert(message); + return message->counter; +} + +axolotl_buffer *whisper_message_get_body(const whisper_message *message) +{ + assert(message); + return message->ciphertext; +} + +int whisper_message_verify_mac(whisper_message *message, + uint8_t message_version, + ec_public_key *sender_identity_key, + ec_public_key *receiver_identity_key, + const uint8_t *mac_key, size_t mac_key_len, + axolotl_context *global_context) +{ + int result = 0; + axolotl_buffer *our_mac_buffer = 0; + uint8_t *serialized_data = 0; + size_t serialized_len = 0; + uint8_t *serialized_message_data = 0; + size_t serialized_message_len = 0; + uint8_t *their_mac_data = 0; + const size_t their_mac_len = WHISPER_MESSAGE_MAC_LENGTH; + uint8_t *our_mac_data = 0; + size_t our_mac_len = 0; + + assert(message); + assert(message->base_message.serialized); + + /* Set some pointers and lengths for the sections of the raw data */ + serialized_data = axolotl_buffer_data(message->base_message.serialized); + serialized_len = axolotl_buffer_len(message->base_message.serialized); + serialized_message_data = serialized_data; + serialized_message_len = serialized_len - WHISPER_MESSAGE_MAC_LENGTH; + their_mac_data = serialized_data + serialized_message_len; + + result = whisper_message_get_mac(&our_mac_buffer, + message->message_version, + sender_identity_key, receiver_identity_key, + mac_key, mac_key_len, + serialized_message_data, serialized_message_len, + message->base_message.global_context); + if(result < 0) { + goto complete; + } + + our_mac_data = axolotl_buffer_data(our_mac_buffer); + our_mac_len = axolotl_buffer_len(our_mac_buffer); + if(our_mac_len != their_mac_len) { + axolotl_log(global_context, AX_LOG_WARNING, "MAC length mismatch: %d != %d", our_mac_len, their_mac_len); + result = AX_ERR_UNKNOWN; + goto complete; + } + + if(axolotl_constant_memcmp(our_mac_data, their_mac_data, our_mac_len) == 0) { + result = 1; + } + else { + axolotl_log(global_context, AX_LOG_NOTICE, "Bad MAC"); + result = 0; + } + +complete: + if(our_mac_buffer) { + axolotl_buffer_free(our_mac_buffer); + } + return result; +} + +static int whisper_message_get_mac(axolotl_buffer **buffer, + uint8_t message_version, + ec_public_key *sender_identity_key, + ec_public_key *receiver_identity_key, + const uint8_t *mac_key, size_t mac_key_len, + const uint8_t *serialized, size_t serialized_len, + axolotl_context *global_context) +{ + int result = 0; + void *hmac_context; + axolotl_buffer *sender_key_buffer = 0; + axolotl_buffer *receiver_key_buffer = 0; + axolotl_buffer *full_mac_buffer = 0; + axolotl_buffer *result_buf = 0; + uint8_t *result_data = 0; + + assert(global_context); + + result = axolotl_hmac_sha256_init(global_context, + &hmac_context, mac_key, mac_key_len); + if(result < 0) { + goto complete; + } + + if(message_version >= 3) { + result = ec_public_key_serialize(&sender_key_buffer, sender_identity_key); + if(result < 0) { + goto complete; + } + + result = axolotl_hmac_sha256_update(global_context, hmac_context, + axolotl_buffer_data(sender_key_buffer), + axolotl_buffer_len(sender_key_buffer)); + if(result < 0) { + goto complete; + } + + result = ec_public_key_serialize(&receiver_key_buffer, receiver_identity_key); + if(result < 0) { + goto complete; + } + + result = axolotl_hmac_sha256_update(global_context, hmac_context, + axolotl_buffer_data(receiver_key_buffer), + axolotl_buffer_len(receiver_key_buffer)); + if(result < 0) { + goto complete; + } + } + + result = axolotl_hmac_sha256_update(global_context, hmac_context, + serialized, serialized_len); + if(result < 0) { + goto complete; + } + + result = axolotl_hmac_sha256_final(global_context, + hmac_context, &full_mac_buffer); + if(result < 0 || axolotl_buffer_len(full_mac_buffer) < WHISPER_MESSAGE_MAC_LENGTH) { + if(result >= 0) { result = AX_ERR_UNKNOWN; } + goto complete; + } + + result_buf = axolotl_buffer_alloc(WHISPER_MESSAGE_MAC_LENGTH); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + result_data = axolotl_buffer_data(result_buf); + memcpy(result_data, axolotl_buffer_data(full_mac_buffer), WHISPER_MESSAGE_MAC_LENGTH); + +complete: + axolotl_hmac_sha256_cleanup(global_context, hmac_context); + axolotl_buffer_free(sender_key_buffer); + axolotl_buffer_free(receiver_key_buffer); + axolotl_buffer_free(full_mac_buffer); + if(result >= 0) { + *buffer = result_buf; + } + return result; +} + +int whisper_message_is_legacy(const uint8_t *data, size_t len) +{ + return data && len >= 1 && ((data[0] & 0xF0) >> 4) <= CIPHERTEXT_UNSUPPORTED_VERSION; +} + +void whisper_message_destroy(axolotl_type_base *type) +{ + whisper_message *message = (whisper_message *)type; + + if(message->base_message.serialized) { + axolotl_buffer_free(message->base_message.serialized); + } + AXOLOTL_UNREF(message->sender_ratchet_key); + if(message->ciphertext) { + axolotl_buffer_free(message->ciphertext); + } + free(message); +} + +/*------------------------------------------------------------------------*/ + +int pre_key_whisper_message_create(pre_key_whisper_message **pre_key_message, + uint8_t message_version, uint32_t registration_id, const uint32_t *pre_key_id, + uint32_t signed_pre_key_id, ec_public_key *base_key, ec_public_key *identity_key, + whisper_message *message, + axolotl_context *global_context) +{ + int result = 0; + pre_key_whisper_message *result_message = 0; + + assert(global_context); + + result_message = malloc(sizeof(pre_key_whisper_message)); + + if(!result_message) { + return AX_ERR_NOMEM; + } + memset(result_message, 0, sizeof(pre_key_whisper_message)); + AXOLOTL_INIT(result_message, pre_key_whisper_message_destroy); + + result_message->base_message.message_type = CIPHERTEXT_PREKEY_TYPE; + result_message->base_message.global_context = global_context; + + result_message->version = message_version; + result_message->registration_id = registration_id; + if(pre_key_id) { + result_message->has_pre_key_id = 1; + result_message->pre_key_id = *pre_key_id; + } + result_message->signed_pre_key_id = signed_pre_key_id; + + AXOLOTL_REF(base_key); + result_message->base_key = base_key; + + AXOLOTL_REF(identity_key); + result_message->identity_key = identity_key; + + AXOLOTL_REF(message); + result_message->message = message; + + result = pre_key_whisper_message_serialize(&result_message->base_message.serialized, result_message); + + if(result >= 0) { + result = 0; + *pre_key_message = result_message; + } + else { + if(result_message) { + AXOLOTL_UNREF(result_message); + } + } + return result; +} + +static int pre_key_whisper_message_serialize(axolotl_buffer **buffer, const pre_key_whisper_message *message) +{ + int result = 0; + size_t result_size = 0; + axolotl_buffer *result_buf = 0; + Textsecure__PreKeyWhisperMessage message_structure = TEXTSECURE__PRE_KEY_WHISPER_MESSAGE__INIT; + axolotl_buffer *inner_message_buffer = 0; + size_t len = 0; + uint8_t *data = 0; + + uint8_t version = (message->version << 4) | CIPHERTEXT_CURRENT_VERSION; + + message_structure.registrationid = message->registration_id; + message_structure.has_registrationid = 1; + + if(message->has_pre_key_id) { + message_structure.prekeyid = message->pre_key_id; + message_structure.has_prekeyid = 1; + } + + message_structure.signedprekeyid = message->signed_pre_key_id; + message_structure.has_signedprekeyid = 1; + + result = ec_public_key_serialize_protobuf(&message_structure.basekey, message->base_key); + if(result < 0) { + goto complete; + } + message_structure.has_basekey = 1; + + result = ec_public_key_serialize_protobuf(&message_structure.identitykey, message->identity_key); + if(result < 0) { + goto complete; + } + message_structure.has_identitykey = 1; + + inner_message_buffer = message->message->base_message.serialized; + message_structure.message.data = axolotl_buffer_data(inner_message_buffer); + message_structure.message.len = axolotl_buffer_len(inner_message_buffer); + message_structure.has_message = 1; + + len = textsecure__pre_key_whisper_message__get_packed_size(&message_structure); + + result_buf = axolotl_buffer_alloc(len + 1); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(result_buf); + data[0] = version; + + result_size = textsecure__pre_key_whisper_message__pack(&message_structure, data + 1); + if(result_size != len) { + axolotl_buffer_free(result_buf); + result = AX_ERR_INVALID_PROTO_BUF; + result_buf = 0; + goto complete; + } + +complete: + if(message_structure.basekey.data) { + free(message_structure.basekey.data); + } + if(message_structure.identitykey.data) { + free(message_structure.identitykey.data); + } + if(result >= 0) { + *buffer = result_buf; + } + return result; +} + +int pre_key_whisper_message_deserialize(pre_key_whisper_message **message, + const uint8_t *data, size_t len, + axolotl_context *global_context) +{ + int result = 0; + pre_key_whisper_message *result_message = 0; + Textsecure__PreKeyWhisperMessage *message_structure = 0; + uint8_t version = 0; + const uint8_t *message_data = 0; + size_t message_len = 0; + uint8_t *serialized_data = 0; + + assert(global_context); + + if(!data || len <= 1) { + result = AX_ERR_INVAL; + goto complete; + } + + version = (data[0] & 0xF0) >> 4; + + /* Set some pointers and lengths for the sections of the raw data */ + message_data = data + 1; + message_len = len - 1; + + if(version <= CIPHERTEXT_UNSUPPORTED_VERSION) { + axolotl_log(global_context, AX_LOG_WARNING, "Unsupported legacy version: %d", version); + result = AX_ERR_LEGACY_MESSAGE; + goto complete; + } + + if(version > CIPHERTEXT_CURRENT_VERSION) { + axolotl_log(global_context, AX_LOG_WARNING, "Unknown version: %d", version); + result = AX_ERR_INVALID_VERSION; + goto complete; + } + + message_structure = textsecure__pre_key_whisper_message__unpack(0, message_len, message_data); + if(!message_structure) { + result = AX_ERR_INVALID_PROTO_BUF; + goto complete; + } + + if((version == 2 && !message_structure->has_prekeyid) || + (version == 3 && !message_structure->has_signedprekeyid) || + !message_structure->has_basekey || + !message_structure->has_identitykey || + !message_structure->has_message) { + axolotl_log(global_context, AX_LOG_WARNING, "Incomplete message"); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + result_message = malloc(sizeof(pre_key_whisper_message)); + if(!result_message) { + result = AX_ERR_NOMEM; + goto complete; + } + memset(result_message, 0, sizeof(pre_key_whisper_message)); + AXOLOTL_INIT(result_message, pre_key_whisper_message_destroy); + + result_message->base_message.message_type = CIPHERTEXT_PREKEY_TYPE; + result_message->base_message.global_context = global_context; + + result_message->version = version; + + if(message_structure->has_registrationid) { + result_message->registration_id = message_structure->registrationid; + } + + if(message_structure->has_prekeyid) { + result_message->pre_key_id = message_structure->prekeyid; + result_message->has_pre_key_id = 1; + } + + if(message_structure->has_signedprekeyid) { + result_message->signed_pre_key_id = message_structure->signedprekeyid; + } + + if(message_structure->has_basekey) { + result = curve_decode_point(&result_message->base_key, + message_structure->basekey.data, message_structure->basekey.len, global_context); + if(result < 0) { + goto complete; + } + } + + if(message_structure->has_identitykey) { + result = curve_decode_point(&result_message->identity_key, + message_structure->identitykey.data, message_structure->identitykey.len, global_context); + if(result < 0) { + goto complete; + } + } + + if(message_structure->has_message) { + result = whisper_message_deserialize(&result_message->message, + message_structure->message.data, + message_structure->message.len, + global_context); + if(result < 0) { + goto complete; + } + if(result_message->message->message_version != version) { + axolotl_log(global_context, AX_LOG_WARNING, "Inner message version mismatch: %d != %d", + result_message->message->message_version, version); + result = AX_ERR_INVALID_VERSION; + goto complete; + } + } + + result_message->base_message.serialized = axolotl_buffer_alloc(len); + if(!result_message->base_message.serialized) { + result = AX_ERR_NOMEM; + goto complete; + } + serialized_data = axolotl_buffer_data(result_message->base_message.serialized); + memcpy(serialized_data, data, len); + +complete: + if(message_structure) { + textsecure__pre_key_whisper_message__free_unpacked(message_structure, 0); + } + if(result >= 0) { + *message = result_message; + } + else { + if(result_message) { + AXOLOTL_UNREF(result_message); + } + } + return result; +} + +int pre_key_whisper_message_copy(pre_key_whisper_message **message, pre_key_whisper_message *other_message, axolotl_context *global_context) +{ + int result = 0; + pre_key_whisper_message *result_message = 0; + + assert(other_message); + assert(global_context); + + result = pre_key_whisper_message_deserialize( + &result_message, + axolotl_buffer_data(other_message->base_message.serialized), + axolotl_buffer_len(other_message->base_message.serialized), + global_context); + if(result >= 0) { + *message = result_message; + } + + return result; +} + +uint8_t pre_key_whisper_message_get_message_version(const pre_key_whisper_message *message) +{ + assert(message); + return message->version; +} + +ec_public_key *pre_key_whisper_message_get_identity_key(const pre_key_whisper_message *message) +{ + assert(message); + return message->identity_key; +} + +uint32_t pre_key_whisper_message_get_registration_id(const pre_key_whisper_message *message) +{ + assert(message); + return message->registration_id; +} + +int pre_key_whisper_message_has_pre_key_id(const pre_key_whisper_message *message) +{ + assert(message); + return message->has_pre_key_id; +} + +uint32_t pre_key_whisper_message_get_pre_key_id(const pre_key_whisper_message *message) +{ + assert(message); + assert(message->has_pre_key_id); + return message->pre_key_id; +} + +uint32_t pre_key_whisper_message_get_signed_pre_key_id(const pre_key_whisper_message *message) +{ + assert(message); + return message->signed_pre_key_id; +} + +ec_public_key *pre_key_whisper_message_get_base_key(const pre_key_whisper_message *message) +{ + assert(message); + return message->base_key; +} + +whisper_message *pre_key_whisper_message_get_whisper_message(const pre_key_whisper_message *message) +{ + assert(message); + return message->message; +} + +void pre_key_whisper_message_destroy(axolotl_type_base *type) +{ + pre_key_whisper_message *message = (pre_key_whisper_message *)type; + + if(message->base_message.serialized) { + axolotl_buffer_free(message->base_message.serialized); + } + AXOLOTL_UNREF(message->base_key); + AXOLOTL_UNREF(message->identity_key); + AXOLOTL_UNREF(message->message); + free(message); +} + +int sender_key_message_create(sender_key_message **message, + uint32_t key_id, uint32_t iteration, + const uint8_t *ciphertext, size_t ciphertext_len, + ec_private_key *signature_key, + axolotl_context *global_context) +{ + int result = 0; + sender_key_message *result_message = 0; + axolotl_buffer *message_buf = 0; + + assert(global_context); + + result_message = malloc(sizeof(sender_key_message)); + + if(!result_message) { + return AX_ERR_NOMEM; + } + memset(result_message, 0, sizeof(sender_key_message)); + AXOLOTL_INIT(result_message, sender_key_message_destroy); + + result_message->base_message.message_type = CIPHERTEXT_SENDERKEY_TYPE; + result_message->base_message.global_context = global_context; + + result_message->message_version = CIPHERTEXT_CURRENT_VERSION; + result_message->key_id = key_id; + result_message->iteration = iteration; + + result_message->ciphertext = axolotl_buffer_create(ciphertext, ciphertext_len); + if(!result_message->ciphertext) { + result = AX_ERR_NOMEM; + goto complete; + } + + result = sender_key_message_serialize(&message_buf, result_message, signature_key, global_context); + if(result < 0) { + goto complete; + } + + result_message->base_message.serialized = message_buf; + +complete: + if(result >= 0) { + result = 0; + *message = result_message; + } + else { + if(result_message) { + AXOLOTL_UNREF(result_message); + } + } + return result; +} + +int sender_key_message_serialize(axolotl_buffer **buffer, const sender_key_message *message, ec_private_key *signature_key, axolotl_context *global_context) +{ + int result = 0; + uint8_t version = (CIPHERTEXT_CURRENT_VERSION << 4) | CIPHERTEXT_CURRENT_VERSION; + size_t result_size = 0; + axolotl_buffer *result_buf = 0; + axolotl_buffer *signature_buf = 0; + Textsecure__SenderKeyMessage message_structure = TEXTSECURE__SENDER_KEY_MESSAGE__INIT; + size_t len = 0; + uint8_t *data = 0; + + message_structure.id = message->key_id; + message_structure.has_id = 1; + + message_structure.iteration = message->iteration; + message_structure.has_iteration = 1; + + message_structure.ciphertext.data = axolotl_buffer_data(message->ciphertext); + message_structure.ciphertext.len = axolotl_buffer_len(message->ciphertext); + message_structure.has_ciphertext = 1; + + len = textsecure__sender_key_message__get_packed_size(&message_structure); + + result_buf = axolotl_buffer_alloc(sizeof(version) + len + SIGNATURE_LENGTH); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(result_buf); + data[0] = version; + + result_size = textsecure__sender_key_message__pack(&message_structure, data + sizeof(version)); + if(result_size != len) { + axolotl_buffer_free(result_buf); + result = AX_ERR_INVALID_PROTO_BUF; + result_buf = 0; + goto complete; + } + + result = curve_calculate_signature(global_context, &signature_buf, signature_key, + data, len + sizeof(version)); + if(result < 0) { + if(result == AX_ERR_INVALID_KEY) { + result = AX_ERR_UNKNOWN; + } + goto complete; + } + else if(axolotl_buffer_len(signature_buf) != SIGNATURE_LENGTH) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + memcpy(data + sizeof(version) + len, axolotl_buffer_data(signature_buf), SIGNATURE_LENGTH); + +complete: + axolotl_buffer_free(signature_buf); + if(result >= 0) { + *buffer = result_buf; + } + else { + axolotl_buffer_free(result_buf); + } + return result; +} + +int sender_key_message_deserialize(sender_key_message **message, + const uint8_t *data, size_t len, + axolotl_context *global_context) +{ + int result = 0; + sender_key_message *result_message = 0; + uint8_t version = 0; + const uint8_t *message_data = 0; + size_t message_len = 0; + Textsecure__SenderKeyMessage *message_structure = 0; + + assert(global_context); + + if(!data || len <= sizeof(uint8_t) + SIGNATURE_LENGTH) { + result = AX_ERR_INVAL; + goto complete; + } + + version = (data[0] & 0xF0) >> 4; + message_data = data + sizeof(uint8_t); + message_len = len - sizeof(uint8_t) - SIGNATURE_LENGTH; + + if(version < CIPHERTEXT_CURRENT_VERSION) { + axolotl_log(global_context, AX_LOG_WARNING, "Legacy message: %d", version); + result = AX_ERR_LEGACY_MESSAGE; + goto complete; + } + + if(version > CIPHERTEXT_CURRENT_VERSION) { + axolotl_log(global_context, AX_LOG_WARNING, "Unknown version: %d", version); + result = AX_ERR_INVALID_VERSION; + goto complete; + } + + message_structure = textsecure__sender_key_message__unpack(0, message_len, message_data); + if(!message_structure) { + result = AX_ERR_INVALID_PROTO_BUF; + goto complete; + } + + if(!message_structure->has_id + || !message_structure->has_iteration + || !message_structure->has_ciphertext) { + axolotl_log(global_context, AX_LOG_WARNING, "Incomplete message"); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + result_message = malloc(sizeof(sender_key_message)); + if(!result_message) { + result = AX_ERR_NOMEM; + goto complete; + } + memset(result_message, 0, sizeof(sender_key_message)); + AXOLOTL_INIT(result_message, sender_key_message_destroy); + + result_message->base_message.message_type = CIPHERTEXT_SENDERKEY_TYPE; + result_message->base_message.global_context = global_context; + + result_message->key_id = message_structure->id; + result_message->iteration = message_structure->iteration; + result_message->message_version = version; + + result_message->ciphertext = axolotl_buffer_create( + message_structure->ciphertext.data, + message_structure->ciphertext.len); + if(!result_message->ciphertext) { + result = AX_ERR_NOMEM; + goto complete; + } + + result_message->base_message.serialized = axolotl_buffer_create(data, len); + if(!result_message->base_message.serialized) { + result = AX_ERR_NOMEM; + goto complete; + } + +complete: + if(message_structure) { + textsecure__sender_key_message__free_unpacked(message_structure, 0); + } + if(result >= 0) { + *message = result_message; + } + else { + if(result_message) { + AXOLOTL_UNREF(result_message); + } + } + return result; +} + +int sender_key_message_copy(sender_key_message **message, sender_key_message *other_message, axolotl_context *global_context) +{ + int result = 0; + sender_key_message *result_message = 0; + + assert(other_message); + assert(global_context); + + result = sender_key_message_deserialize( + &result_message, + axolotl_buffer_data(other_message->base_message.serialized), + axolotl_buffer_len(other_message->base_message.serialized), + global_context); + if(result >= 0) { + *message = result_message; + } + + return result; +} + +uint32_t sender_key_message_get_key_id(sender_key_message *message) +{ + assert(message); + return message->key_id; +} + +uint32_t sender_key_message_get_iteration(sender_key_message *message) +{ + assert(message); + return message->iteration; +} + +axolotl_buffer *sender_key_message_get_ciphertext(sender_key_message *message) +{ + assert(message); + return message->ciphertext; +} + +int sender_key_message_verify_signature(sender_key_message *message, ec_public_key *signature_key) +{ + int result = 0; + uint8_t *data; + size_t data_len; + + assert(message); + + data = axolotl_buffer_data(message->base_message.serialized); + data_len = axolotl_buffer_len(message->base_message.serialized) - SIGNATURE_LENGTH; + + result = curve_verify_signature(signature_key, data, data_len, data + data_len, SIGNATURE_LENGTH); + + if(result == 0) { + axolotl_log(message->base_message.global_context, AX_LOG_ERROR, "Invalid signature!"); + result = AX_ERR_INVALID_MESSAGE; + } + else if(result < 0) { + result = AX_ERR_INVALID_MESSAGE; + } + else { + result = 0; + } + + return result; +} + +void sender_key_message_destroy(axolotl_type_base *type) +{ + sender_key_message *message = (sender_key_message *)type; + + if(message->base_message.serialized) { + axolotl_buffer_free(message->base_message.serialized); + } + if(message->ciphertext) { + axolotl_buffer_free(message->ciphertext); + } + free(message); +} + +int sender_key_distribution_message_create(sender_key_distribution_message **message, + uint32_t id, uint32_t iteration, + const uint8_t *chain_key, size_t chain_key_len, + ec_public_key *signature_key, + axolotl_context *global_context) +{ + int result = 0; + + sender_key_distribution_message *result_message = 0; + axolotl_buffer *message_buf = 0; + + assert(global_context); + + result_message = malloc(sizeof(sender_key_distribution_message)); + + if(!result_message) { + return AX_ERR_NOMEM; + } + memset(result_message, 0, sizeof(sender_key_distribution_message)); + AXOLOTL_INIT(result_message, sender_key_distribution_message_destroy); + + result_message->base_message.message_type = CIPHERTEXT_SENDERKEY_DISTRIBUTION_TYPE; + result_message->base_message.global_context = global_context; + + result_message->id = id; + result_message->iteration = iteration; + + result_message->chain_key = axolotl_buffer_create(chain_key, chain_key_len); + if(!result_message->chain_key) { + goto complete; + } + + AXOLOTL_REF(signature_key); + result_message->signature_key = signature_key; + + result = sender_key_distribution_message_serialize(&message_buf, result_message); + if(result < 0) { + goto complete; + } + + result_message->base_message.serialized = message_buf; + +complete: + if(result >= 0) { + result = 0; + *message = result_message; + } + else { + if(result_message) { + AXOLOTL_UNREF(result_message); + } + } + return result; +} + +int sender_key_distribution_message_serialize(axolotl_buffer **buffer, const sender_key_distribution_message *message) +{ + int result = 0; + uint8_t version = (CIPHERTEXT_CURRENT_VERSION << 4) | CIPHERTEXT_CURRENT_VERSION; + size_t result_size = 0; + axolotl_buffer *result_buf = 0; + Textsecure__SenderKeyDistributionMessage message_structure = TEXTSECURE__SENDER_KEY_DISTRIBUTION_MESSAGE__INIT; + size_t len = 0; + uint8_t *data = 0; + + message_structure.id = message->id; + message_structure.has_id = 1; + + message_structure.iteration = message->iteration; + message_structure.has_iteration = 1; + + message_structure.chainkey.data = axolotl_buffer_data(message->chain_key); + message_structure.chainkey.len = axolotl_buffer_len(message->chain_key); + message_structure.has_chainkey = 1; + + result = ec_public_key_serialize_protobuf(&message_structure.signingkey, message->signature_key); + if(result < 0) { + goto complete; + } + message_structure.has_signingkey = 1; + + len = textsecure__sender_key_distribution_message__get_packed_size(&message_structure); + + result_buf = axolotl_buffer_alloc(sizeof(version) + len); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(result_buf); + data[0] = version; + + result_size = textsecure__sender_key_distribution_message__pack(&message_structure, data + sizeof(version)); + if(result_size != len) { + axolotl_buffer_free(result_buf); + result = AX_ERR_INVALID_PROTO_BUF; + result_buf = 0; + goto complete; + } + +complete: + if(message_structure.has_signingkey) { + free(message_structure.signingkey.data); + } + if(result >= 0) { + *buffer = result_buf; + } + else { + axolotl_buffer_free(result_buf); + } + return result; +} + +int sender_key_distribution_message_deserialize(sender_key_distribution_message **message, + const uint8_t *data, size_t len, + axolotl_context *global_context) +{ + int result = 0; + sender_key_distribution_message *result_message = 0; + uint8_t version = 0; + const uint8_t *message_data = 0; + size_t message_len = 0; + Textsecure__SenderKeyDistributionMessage *message_structure = 0; + + assert(global_context); + + if(!data || len <= sizeof(uint8_t)) { + result = AX_ERR_INVAL; + goto complete; + } + + version = (data[0] & 0xF0) >> 4; + message_data = data + sizeof(uint8_t); + message_len = len - sizeof(uint8_t); + + if(version < CIPHERTEXT_CURRENT_VERSION) { + axolotl_log(global_context, AX_LOG_WARNING, "Legacy message: %d", version); + result = AX_ERR_LEGACY_MESSAGE; + goto complete; + } + + if(version > CIPHERTEXT_CURRENT_VERSION) { + axolotl_log(global_context, AX_LOG_WARNING, "Unknown version: %d", version); + result = AX_ERR_INVALID_VERSION; + goto complete; + } + + message_structure = textsecure__sender_key_distribution_message__unpack(0, message_len, message_data); + if(!message_structure) { + result = AX_ERR_INVALID_PROTO_BUF; + goto complete; + } + + if(!message_structure->has_id + || !message_structure->has_iteration + || !message_structure->has_chainkey + || !message_structure->has_signingkey) { + axolotl_log(global_context, AX_LOG_WARNING, "Incomplete message"); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + result_message = malloc(sizeof(sender_key_distribution_message)); + if(!result_message) { + result = AX_ERR_NOMEM; + goto complete; + } + memset(result_message, 0, sizeof(sender_key_distribution_message)); + AXOLOTL_INIT(result_message, sender_key_distribution_message_destroy); + + result_message->base_message.message_type = CIPHERTEXT_SENDERKEY_DISTRIBUTION_TYPE; + result_message->base_message.global_context = global_context; + + result_message->id = message_structure->id; + result_message->iteration = message_structure->iteration; + + result_message->chain_key = axolotl_buffer_create( + message_structure->chainkey.data, + message_structure->chainkey.len); + if(!result_message->chain_key) { + result = AX_ERR_NOMEM; + goto complete; + } + + result = curve_decode_point(&result_message->signature_key, + message_structure->signingkey.data, + message_structure->signingkey.len, + global_context); + if(result < 0) { + goto complete; + } + + result_message->base_message.serialized = axolotl_buffer_create(data, len); + if(!result_message->base_message.serialized) { + result = AX_ERR_NOMEM; + goto complete; + } + +complete: + if(message_structure) { + textsecure__sender_key_distribution_message__free_unpacked(message_structure, 0); + } + if(result >= 0) { + *message = result_message; + } + else { + if(result_message) { + AXOLOTL_UNREF(result_message); + } + } + return result; +} + +int sender_key_distribution_message_copy(sender_key_distribution_message **message, sender_key_distribution_message *other_message, axolotl_context *global_context) +{ + int result = 0; + sender_key_distribution_message *result_message = 0; + + assert(other_message); + assert(global_context); + + result = sender_key_distribution_message_deserialize( + &result_message, + axolotl_buffer_data(other_message->base_message.serialized), + axolotl_buffer_len(other_message->base_message.serialized), + global_context); + if(result >= 0) { + *message = result_message; + } + + return result; +} + +uint32_t sender_key_distribution_message_get_id(sender_key_distribution_message *message) +{ + assert(message); + return message->id; +} + +uint32_t sender_key_distribution_message_get_iteration(sender_key_distribution_message *message) +{ + assert(message); + return message->iteration; +} + +axolotl_buffer *sender_key_distribution_message_get_chain_key(sender_key_distribution_message *message) +{ + assert(message); + return message->chain_key; +} + +ec_public_key *sender_key_distribution_message_get_signature_key(sender_key_distribution_message *message) +{ + assert(message); + return message->signature_key; +} + +void sender_key_distribution_message_destroy(axolotl_type_base *type) +{ + sender_key_distribution_message *message = (sender_key_distribution_message *)type; + + if(message->base_message.serialized) { + axolotl_buffer_free(message->base_message.serialized); + } + + if(message->chain_key) { + axolotl_buffer_free(message->chain_key); + } + AXOLOTL_UNREF(message->signature_key); + free(message); +} diff --git a/libs/libaxolotl/src/protocol.h b/libs/libaxolotl/src/protocol.h new file mode 100644 index 0000000000..e149ae84a1 --- /dev/null +++ b/libs/libaxolotl/src/protocol.h @@ -0,0 +1,151 @@ +#ifndef PROTOCOL_H +#define PROTOCOL_H + +#include <stdint.h> +#include <stddef.h> +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define KEY_EXCHANGE_INITIATE_FLAG 0x01 +#define KEY_EXCHANGE_RESPONSE_FLAG 0X02 +#define KEY_EXCHANGE_SIMULTAENOUS_INITIATE_FLAG 0x04 + +#define CIPHERTEXT_UNSUPPORTED_VERSION 1 +#define CIPHERTEXT_CURRENT_VERSION 3 + +#define CIPHERTEXT_WHISPER_TYPE 2 +#define CIPHERTEXT_PREKEY_TYPE 3 +#define CIPHERTEXT_SENDERKEY_TYPE 4 +#define CIPHERTEXT_SENDERKEY_DISTRIBUTION_TYPE 5 + +/* Worst case overhead. Not always accurate, but good enough for padding. */ +#define CIPHERTEXT_ENCRYPTED_MESSAGE_OVERHEAD 53 + +int key_exchange_message_create(key_exchange_message **message, + uint8_t message_version, uint32_t sequence, uint32_t flags, + ec_public_key *base_key, uint8_t *base_key_signature, + ec_public_key *ratchet_key, ec_public_key *identity_key); + +int key_exchange_message_deserialize(key_exchange_message **message, const uint8_t *data, size_t len, axolotl_context *global_context); + +axolotl_buffer *key_exchange_message_get_serialized(const key_exchange_message *message); + +uint8_t key_exchange_message_get_version(const key_exchange_message *message); +ec_public_key *key_exchange_message_get_base_key(const key_exchange_message *message); +uint8_t *key_exchange_message_get_base_key_signature(key_exchange_message *message); +ec_public_key *key_exchange_message_get_ratchet_key(const key_exchange_message *message); +ec_public_key *key_exchange_message_get_identity_key(const key_exchange_message *message); +int key_exchange_message_has_identity_key(const key_exchange_message *message); +uint8_t key_exchange_message_get_max_version(const key_exchange_message *message); +int key_exchange_message_is_response(const key_exchange_message *message); +int key_exchange_message_is_initiate(const key_exchange_message *message); +int key_exchange_message_is_response_for_simultaneous_initiate(const key_exchange_message *message); +uint32_t key_exchange_message_get_flags(const key_exchange_message *message); +uint32_t key_exchange_message_get_sequence(const key_exchange_message *message); + +void key_exchange_message_destroy(axolotl_type_base *type); + +int ciphertext_message_get_type(const ciphertext_message *message); +axolotl_buffer *ciphertext_message_get_serialized(const ciphertext_message *message); + +int whisper_message_create(whisper_message **message, uint8_t message_version, + const uint8_t *mac_key, size_t mac_key_len, + ec_public_key *sender_ratchet_key, uint32_t counter, uint32_t previous_counter, + const uint8_t *ciphertext, size_t ciphertext_len, + ec_public_key *sender_identity_key, ec_public_key *receiver_identity_key, + axolotl_context *global_context); + +int whisper_message_deserialize(whisper_message **message, const uint8_t *data, size_t len, + axolotl_context *global_context); + +int whisper_message_copy(whisper_message **message, whisper_message *other_message, axolotl_context *global_context); + +ec_public_key *whisper_message_get_sender_ratchet_key(const whisper_message *message); + +uint8_t whisper_message_get_message_version(const whisper_message *message); + +uint32_t whisper_message_get_counter(const whisper_message *message); + +axolotl_buffer *whisper_message_get_body(const whisper_message *message); + +/** + * Verify the MAC on the Whisper message. + * + * @return 1 if verified, 0 if invalid, negative on error + */ +int whisper_message_verify_mac(whisper_message *message, + uint8_t message_version, + ec_public_key *sender_identity_key, + ec_public_key *receiver_identity_key, + const uint8_t *mac_key, size_t mac_key_len, + axolotl_context *global_context); + +int whisper_message_is_legacy(const uint8_t *data, size_t len); + +void whisper_message_destroy(axolotl_type_base *type); + +int pre_key_whisper_message_create(pre_key_whisper_message **pre_key_message, + uint8_t message_version, uint32_t registration_id, const uint32_t *pre_key_id, + uint32_t signed_pre_key_id, ec_public_key *base_key, ec_public_key *identity_key, + whisper_message *message, + axolotl_context *global_context); + +int pre_key_whisper_message_deserialize(pre_key_whisper_message **message, + const uint8_t *data, size_t len, + axolotl_context *global_context); + +int pre_key_whisper_message_copy(pre_key_whisper_message **message, pre_key_whisper_message *other_message, axolotl_context *global_context); + +uint8_t pre_key_whisper_message_get_message_version(const pre_key_whisper_message *message); +ec_public_key *pre_key_whisper_message_get_identity_key(const pre_key_whisper_message *message); +uint32_t pre_key_whisper_message_get_registration_id(const pre_key_whisper_message *message); +int pre_key_whisper_message_has_pre_key_id(const pre_key_whisper_message *message); +uint32_t pre_key_whisper_message_get_pre_key_id(const pre_key_whisper_message *message); +uint32_t pre_key_whisper_message_get_signed_pre_key_id(const pre_key_whisper_message *message); +ec_public_key *pre_key_whisper_message_get_base_key(const pre_key_whisper_message *message); +whisper_message *pre_key_whisper_message_get_whisper_message(const pre_key_whisper_message *message); + +void pre_key_whisper_message_destroy(axolotl_type_base *type); + +int sender_key_message_create(sender_key_message **message, + uint32_t key_id, uint32_t iteration, + const uint8_t *ciphertext, size_t ciphertext_len, + ec_private_key *signature_key, + axolotl_context *global_context); +int sender_key_message_deserialize(sender_key_message **message, + const uint8_t *data, size_t len, + axolotl_context *global_context); +int sender_key_message_copy(sender_key_message **message, sender_key_message *other_message, axolotl_context *global_context); + +uint32_t sender_key_message_get_key_id(sender_key_message *message); +uint32_t sender_key_message_get_iteration(sender_key_message *message); +axolotl_buffer *sender_key_message_get_ciphertext(sender_key_message *message); +int sender_key_message_verify_signature(sender_key_message *message, ec_public_key *signature_key); + +void sender_key_message_destroy(axolotl_type_base *type); + +int sender_key_distribution_message_create(sender_key_distribution_message **message, + uint32_t id, uint32_t iteration, + const uint8_t *chain_key, size_t chain_key_len, + ec_public_key *signature_key, + axolotl_context *global_context); +int sender_key_distribution_message_deserialize(sender_key_distribution_message **message, + const uint8_t *data, size_t len, + axolotl_context *global_context); +int sender_key_distribution_message_copy(sender_key_distribution_message **message, sender_key_distribution_message *other_message, axolotl_context *global_context); + +uint32_t sender_key_distribution_message_get_id(sender_key_distribution_message *message); +uint32_t sender_key_distribution_message_get_iteration(sender_key_distribution_message *message); +axolotl_buffer *sender_key_distribution_message_get_chain_key(sender_key_distribution_message *message); +ec_public_key *sender_key_distribution_message_get_signature_key(sender_key_distribution_message *message); + +void sender_key_distribution_message_destroy(axolotl_type_base *type); + +#ifdef __cplusplus +} +#endif + +#endif /* PROTOCOL_H */ diff --git a/libs/libaxolotl/src/ratchet.c b/libs/libaxolotl/src/ratchet.c new file mode 100644 index 0000000000..8b0dc87bec --- /dev/null +++ b/libs/libaxolotl/src/ratchet.c @@ -0,0 +1,1237 @@ +#include "ratchet.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include "protobuf-c/protobuf-c.h" + +#include "axolotl_internal.h" +#include "hkdf.h" +#include "curve.h" +#include "session_state.h" +#include "vpool.h" +#include "LocalStorageProtocol.pb-c.h" + +#define HASH_OUTPUT_SIZE 32 +#define DERIVED_MESSAGE_SECRETS_SIZE 80 +#define DERIVED_ROOT_SECRETS_SIZE 64 + +struct ratchet_chain_key { + axolotl_type_base base; + axolotl_context *global_context; + hkdf_context *kdf; + uint8_t *key; + size_t key_len; + uint32_t index; +}; + +struct ratchet_root_key { + axolotl_type_base base; + axolotl_context *global_context; + hkdf_context *kdf; + uint8_t *key; + size_t key_len; +}; + +struct ratchet_identity_key_pair { + axolotl_type_base base; + ec_public_key *public_key; + ec_private_key *private_key; +}; + +int ratchet_chain_key_create(ratchet_chain_key **chain_key, hkdf_context *kdf, uint8_t *key, size_t key_len, uint32_t index, axolotl_context *global_context) +{ + ratchet_chain_key *result = 0; + + if(!kdf || !key) { + return AX_ERR_INVAL; + } + + result = malloc(sizeof(ratchet_chain_key)); + if(!result) { + return AX_ERR_NOMEM; + } + + AXOLOTL_INIT(result, ratchet_chain_key_destroy); + result->global_context = global_context; + result->kdf = kdf; + + result->key = malloc(key_len); + if(!result->key) { + free(result); + return AX_ERR_NOMEM; + } + memcpy(result->key, key, key_len); + result->key_len = key_len; + + result->index = index; + AXOLOTL_REF(result->kdf); + *chain_key = result; + + return 0; +} + +int ratchet_chain_key_get_key(const ratchet_chain_key *chain_key, axolotl_buffer **buffer) +{ + axolotl_buffer *buf = 0; + uint8_t *data = 0; + + buf = axolotl_buffer_alloc(chain_key->key_len); + if(!buf) { + return AX_ERR_NOMEM; + } + + data = axolotl_buffer_data(buf); + memcpy(data, chain_key->key, chain_key->key_len); + + *buffer = buf; + + return 0; +} + +int ratchet_chain_key_get_key_protobuf(const ratchet_chain_key *chain_key, ProtobufCBinaryData *buffer) +{ + uint8_t *data = 0; + + assert(chain_key); + assert(buffer); + + data = malloc(chain_key->key_len); + if(!data) { + return AX_ERR_NOMEM; + } + + memcpy(data, chain_key->key, chain_key->key_len); + + buffer->data = data; + buffer->len = chain_key->key_len; + return 0; +} + +uint32_t ratchet_chain_key_get_index(const ratchet_chain_key *chain_key) +{ + return chain_key->index; +} + +ssize_t ratchet_chain_key_get_base_material(const ratchet_chain_key *chain_key, uint8_t **material, const uint8_t *seed, size_t seed_len) +{ + int result = 0; + axolotl_buffer *output_buffer = 0; + uint8_t *output = 0; + size_t output_len = 0; + + void *hmac_context = 0; + result = axolotl_hmac_sha256_init(chain_key->global_context, &hmac_context, chain_key->key, chain_key->key_len); + if(result < 0) { + goto complete; + } + + result = axolotl_hmac_sha256_update(chain_key->global_context, hmac_context, seed, seed_len); + if(result < 0) { + goto complete; + } + + result = axolotl_hmac_sha256_final(chain_key->global_context, hmac_context, &output_buffer); + if(result < 0) { + goto complete; + } + + output_len = axolotl_buffer_len(output_buffer); + output = malloc(output_len); + if(!output) { + result = AX_ERR_NOMEM; + goto complete; + } + + memcpy(output, axolotl_buffer_data(output_buffer), output_len); + +complete: + axolotl_hmac_sha256_cleanup(chain_key->global_context, hmac_context); + axolotl_buffer_free(output_buffer); + + if(result >= 0) { + *material = output; + return (ssize_t)output_len; + } + else { + return result; + } +} + +int ratchet_chain_key_get_message_keys(ratchet_chain_key *chain_key, ratchet_message_keys *message_keys) +{ + static const uint8_t message_key_seed = 0x01; + static const char key_material_seed[] = "WhisperMessageKeys"; + uint8_t salt[HASH_OUTPUT_SIZE]; + int result = 0; + ssize_t result_size = 0; + uint8_t *input_key_material = 0; + size_t input_key_material_len = 0; + uint8_t *key_material_data = 0; + size_t key_material_data_len = 0; + + memset(message_keys, 0, sizeof(ratchet_message_keys)); + + result_size = ratchet_chain_key_get_base_material(chain_key, &input_key_material, &message_key_seed, sizeof(message_key_seed)); + if(result_size < 0) { + result = (int)result_size; + axolotl_log(chain_key->global_context, AX_LOG_WARNING, "ratchet_chain_key_get_base_material failed"); + goto complete; + } + input_key_material_len = (size_t)result_size; + + memset(salt, 0, sizeof(salt)); + + result_size = hkdf_derive_secrets(chain_key->kdf, + &key_material_data, + input_key_material, input_key_material_len, + salt, sizeof(salt), + (uint8_t *)key_material_seed, sizeof(key_material_seed) - 1, + DERIVED_MESSAGE_SECRETS_SIZE); + if(result_size < 0) { + result = (int)result_size; + axolotl_log(chain_key->global_context, AX_LOG_WARNING, "hkdf_derive_secrets failed"); + goto complete; + } + key_material_data_len = (size_t)result_size; + + if(key_material_data_len != RATCHET_CIPHER_KEY_LENGTH + RATCHET_MAC_KEY_LENGTH + RATCHET_IV_LENGTH) { + axolotl_log(chain_key->global_context, AX_LOG_WARNING, + "key_material_data length mismatch: %d != %d", + key_material_data_len, (RATCHET_CIPHER_KEY_LENGTH + RATCHET_MAC_KEY_LENGTH + RATCHET_IV_LENGTH)); + result = AX_ERR_UNKNOWN; + goto complete; + } + + memcpy(message_keys->cipher_key, key_material_data, RATCHET_CIPHER_KEY_LENGTH); + memcpy(message_keys->mac_key, key_material_data + RATCHET_CIPHER_KEY_LENGTH, RATCHET_MAC_KEY_LENGTH); + memcpy(message_keys->iv, key_material_data + RATCHET_CIPHER_KEY_LENGTH + RATCHET_MAC_KEY_LENGTH, RATCHET_IV_LENGTH); + message_keys->counter = chain_key->index; + +complete: + if(input_key_material) { + free(input_key_material); + } + if(key_material_data) { + free(key_material_data); + } + if(result < 0) { + return result; + } + else { + return 0; + } +} + +int ratchet_chain_key_create_next(const ratchet_chain_key *chain_key, ratchet_chain_key **next_chain_key) +{ + static const uint8_t chain_key_seed = 0x02; + int result = 0; + ssize_t result_size = 0; + uint8_t *next_key = 0; + size_t next_key_len = 0; + + result_size = ratchet_chain_key_get_base_material(chain_key, &next_key, &chain_key_seed, sizeof(chain_key_seed)); + if(result_size < 0) { + result = (int)result_size; + axolotl_log(chain_key->global_context, AX_LOG_WARNING, "ratchet_chain_key_get_base_material failed"); + goto complete; + } + next_key_len = (size_t)result_size; + + result = ratchet_chain_key_create( + next_chain_key, + chain_key->kdf, + next_key, next_key_len, + chain_key->index + 1, + chain_key->global_context); + +complete: + if(next_key) { + free(next_key); + } + + return result; +} + +void ratchet_chain_key_destroy(axolotl_type_base *type) +{ + ratchet_chain_key *chain_key = (ratchet_chain_key *)type; + AXOLOTL_UNREF(chain_key->kdf); + if(chain_key->key) { + axolotl_explicit_bzero(chain_key->key, chain_key->key_len); + free(chain_key->key); + } + free(chain_key); +} + +int ratchet_root_key_create(ratchet_root_key **root_key, hkdf_context *kdf, const uint8_t *key, size_t key_len, axolotl_context *global_context) +{ + ratchet_root_key *result = 0; + + if(!kdf || !key) { + return AX_ERR_INVAL; + } + + result = malloc(sizeof(ratchet_root_key)); + if(!result) { + return AX_ERR_NOMEM; + } + + AXOLOTL_INIT(result, ratchet_root_key_destroy); + result->global_context = global_context; + result->kdf = kdf; + + result->key = malloc(key_len); + if(!result->key) { + free(result); + return AX_ERR_NOMEM; + } + memcpy(result->key, key, key_len); + result->key_len = key_len; + AXOLOTL_REF(result->kdf); + *root_key = result; + + return 0; +} + +int ratchet_root_key_create_chain(ratchet_root_key *root_key, + ratchet_root_key **new_root_key, ratchet_chain_key **new_chain_key, + ec_public_key *their_ratchet_key, + ec_private_key *our_ratchet_key_private) +{ + static const char key_info[] = "WhisperRatchet"; + int result = 0; + ssize_t result_size = 0; + uint8_t *shared_secret = 0; + size_t shared_secret_len = 0; + uint8_t *derived_secret = 0; + ratchet_root_key *new_root_key_result = 0; + ratchet_chain_key *new_chain_key_result = 0; + + if(!their_ratchet_key || !our_ratchet_key_private) { + return AX_ERR_INVAL; + } + + result = curve_calculate_agreement(&shared_secret, their_ratchet_key, our_ratchet_key_private); + if(result < 0) { + axolotl_log(root_key->global_context, AX_LOG_WARNING, "curve_calculate_agreement failed"); + goto complete; + } + shared_secret_len = (size_t)result; + + result_size = hkdf_derive_secrets(root_key->kdf, &derived_secret, + shared_secret, shared_secret_len, + root_key->key, root_key->key_len, + (uint8_t *)key_info, sizeof(key_info) - 1, + DERIVED_ROOT_SECRETS_SIZE); + if(result_size < 0) { + result = (int)result_size; + axolotl_log(root_key->global_context, AX_LOG_WARNING, "hkdf_derive_secrets failed"); + goto complete; + } + else if(result_size != DERIVED_ROOT_SECRETS_SIZE) { + result = AX_ERR_UNKNOWN; + axolotl_log(root_key->global_context, AX_LOG_WARNING, "hkdf_derive_secrets size mismatch"); + goto complete; + } + + result = ratchet_root_key_create(&new_root_key_result, root_key->kdf, + derived_secret, 32, + root_key->global_context); + if(result < 0) { + axolotl_log(root_key->global_context, AX_LOG_WARNING, "ratchet_root_key_create failed"); + goto complete; + } + + result = ratchet_chain_key_create(&new_chain_key_result, root_key->kdf, + derived_secret + 32, 32, 0, + root_key->global_context); + if(result < 0) { + axolotl_log(root_key->global_context, AX_LOG_WARNING, "ratchet_chain_key_create failed"); + goto complete; + } + +complete: + if(shared_secret) { + free(shared_secret); + } + if(derived_secret) { + free(derived_secret); + } + if(result < 0) { + if(new_root_key_result) { + AXOLOTL_UNREF(new_root_key_result); + } + if(new_chain_key_result) { + AXOLOTL_UNREF(new_chain_key_result); + } + return result; + } + else { + *new_root_key = new_root_key_result; + *new_chain_key = new_chain_key_result; + return 0; + } +} + +int ratchet_root_key_get_key(ratchet_root_key *root_key, axolotl_buffer **buffer) +{ + axolotl_buffer *buf = 0; + uint8_t *data = 0; + + assert(root_key); + + buf = axolotl_buffer_alloc(root_key->key_len); + if(!buf) { + return AX_ERR_NOMEM; + } + + data = axolotl_buffer_data(buf); + memcpy(data, root_key->key, root_key->key_len); + + *buffer = buf; + + return 0; +} + +int ratchet_root_key_get_key_protobuf(const ratchet_root_key *root_key, ProtobufCBinaryData *buffer) +{ + uint8_t *data = 0; + + assert(root_key); + assert(buffer); + + data = malloc(root_key->key_len); + if(!data) { + return AX_ERR_NOMEM; + } + + memcpy(data, root_key->key, root_key->key_len); + + buffer->data = data; + buffer->len = root_key->key_len; + return 0; +} + +int ratchet_root_key_compare(const ratchet_root_key *key1, const ratchet_root_key *key2) +{ + if(key1 == key2) { + return 0; + } + else if(key1 == 0 && key2 != 0) { + return -1; + } + else if(key1 != 0 && key2 == 0) { + return 1; + } + else { + int kdf_compare = hkdf_compare(key1->kdf, key2->kdf); + if(kdf_compare != 0) { + return kdf_compare; + } + else if(key1->key_len < key2->key_len) { + return -1; + } + else if(key1->key_len > key2->key_len) { + return 1; + } + else { + return axolotl_constant_memcmp(key1->key, key2->key, key1->key_len); + } + } +} + +void ratchet_root_key_destroy(axolotl_type_base *type) +{ + ratchet_root_key *root_key = (ratchet_root_key *)type; + AXOLOTL_UNREF(root_key->kdf); + if(root_key->key) { + axolotl_explicit_bzero(root_key->key, root_key->key_len); + free(root_key->key); + } + free(root_key); +} + +int ratchet_identity_key_pair_create( + ratchet_identity_key_pair **key_pair, + ec_public_key *public_key, + ec_private_key *private_key) +{ + ratchet_identity_key_pair *result = malloc(sizeof(ratchet_identity_key_pair)); + if(!result) { + return AX_ERR_NOMEM; + } + + AXOLOTL_INIT(result, ratchet_identity_key_pair_destroy); + AXOLOTL_REF(public_key); + AXOLOTL_REF(private_key); + result->public_key = public_key; + result->private_key = private_key; + + *key_pair = result; + + return 0; +} + +int ratchet_identity_key_pair_serialize(axolotl_buffer **buffer, const ratchet_identity_key_pair *key_pair) +{ + int result = 0; + size_t result_size = 0; + axolotl_buffer *result_buf = 0; + Textsecure__IdentityKeyPairStructure key_structure = TEXTSECURE__IDENTITY_KEY_PAIR_STRUCTURE__INIT; + size_t len = 0; + uint8_t *data = 0; + + result = ec_public_key_serialize_protobuf(&key_structure.publickey, key_pair->public_key); + if(result < 0) { + goto complete; + } + key_structure.has_publickey = 1; + + result = ec_private_key_serialize_protobuf(&key_structure.privatekey, key_pair->private_key); + if(result < 0) { + goto complete; + } + key_structure.has_privatekey = 1; + + len = textsecure__identity_key_pair_structure__get_packed_size(&key_structure); + result_buf = axolotl_buffer_alloc(len); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(result_buf); + result_size = textsecure__identity_key_pair_structure__pack(&key_structure, data); + if(result_size != len) { + axolotl_buffer_free(result_buf); + result = AX_ERR_INVALID_PROTO_BUF; + result_buf = 0; + goto complete; + } + +complete: + if(key_structure.has_publickey) { + free(key_structure.publickey.data); + } + if(key_structure.has_privatekey) { + free(key_structure.privatekey.data); + } + if(result >= 0) { + result = 0; + *buffer = result_buf; + } + return result; +} + +int ratchet_identity_key_pair_deserialize(ratchet_identity_key_pair **key_pair, const uint8_t *data, size_t len, axolotl_context *global_context) +{ + int result = 0; + ec_public_key *public_key = 0; + ec_private_key *private_key = 0; + ratchet_identity_key_pair *result_pair = 0; + Textsecure__IdentityKeyPairStructure *key_structure = 0; + + key_structure = textsecure__identity_key_pair_structure__unpack(0, len, data); + if(!key_structure) { + result = AX_ERR_INVALID_PROTO_BUF; + goto complete; + } + + if(!key_structure->has_publickey || !key_structure->has_privatekey) { + result = AX_ERR_INVALID_KEY; + goto complete; + } + + result = curve_decode_point( + &public_key, + key_structure->publickey.data, + key_structure->publickey.len, + global_context); + if(result < 0) { + goto complete; + } + + result = curve_decode_private_point( + &private_key, + key_structure->privatekey.data, + key_structure->privatekey.len, + global_context); + if(result < 0) { + goto complete; + } + + result = ratchet_identity_key_pair_create(&result_pair, + public_key, private_key); + +complete: + AXOLOTL_UNREF(public_key); + AXOLOTL_UNREF(private_key); + if(key_structure) { + textsecure__identity_key_pair_structure__free_unpacked(key_structure, 0); + } + if(result >= 0) { + *key_pair = result_pair; + } + return result; +} + +ec_public_key *ratchet_identity_key_pair_get_public(const ratchet_identity_key_pair *key_pair) +{ + assert(key_pair); + assert(key_pair->public_key); + return key_pair->public_key; +} + +ec_private_key *ratchet_identity_key_pair_get_private(const ratchet_identity_key_pair *key_pair) +{ + assert(key_pair); + assert(key_pair->private_key); + return key_pair->private_key; +} + +void ratchet_identity_key_pair_destroy(axolotl_type_base *type) +{ + ratchet_identity_key_pair *key_pair = (ratchet_identity_key_pair *)type; + AXOLOTL_UNREF(key_pair->public_key); + AXOLOTL_UNREF(key_pair->private_key); + free(key_pair); +} + +struct symmetric_axolotl_parameters +{ + axolotl_type_base base; + ratchet_identity_key_pair *our_identity_key; + ec_key_pair *our_base_key; + ec_key_pair *our_ratchet_key; + ec_public_key *their_base_key; + ec_public_key *their_ratchet_key; + ec_public_key *their_identity_key; +}; + +struct alice_axolotl_parameters +{ + axolotl_type_base base; + ratchet_identity_key_pair *our_identity_key; + ec_key_pair *our_base_key; + ec_public_key *their_identity_key; + ec_public_key *their_signed_pre_key; + ec_public_key *their_one_time_pre_key; /* optional */ + ec_public_key *their_ratchet_key; +}; + +struct bob_axolotl_parameters +{ + axolotl_type_base base; + ratchet_identity_key_pair *our_identity_key; + ec_key_pair *our_signed_pre_key; + ec_key_pair *our_one_time_pre_key; /* optional */ + ec_key_pair *our_ratchet_key; + ec_public_key *their_identity_key; + ec_public_key *their_base_key; +}; + +int symmetric_axolotl_parameters_create( + symmetric_axolotl_parameters **parameters, + ratchet_identity_key_pair *our_identity_key, + ec_key_pair *our_base_key, + ec_key_pair *our_ratchet_key, + ec_public_key *their_base_key, + ec_public_key *their_ratchet_key, + ec_public_key *their_identity_key) +{ + symmetric_axolotl_parameters *result = 0; + + if(!our_identity_key || !our_base_key || !our_ratchet_key + || !their_base_key || !their_ratchet_key || !their_identity_key) { + return AX_ERR_INVAL; + } + + result = malloc(sizeof(symmetric_axolotl_parameters)); + if(!result) { + return AX_ERR_NOMEM; + } + + memset(result, 0, sizeof(symmetric_axolotl_parameters)); + + AXOLOTL_INIT(result, symmetric_axolotl_parameters_destroy); + AXOLOTL_REF(our_identity_key); + AXOLOTL_REF(our_base_key); + AXOLOTL_REF(our_ratchet_key); + AXOLOTL_REF(their_base_key); + AXOLOTL_REF(their_ratchet_key); + AXOLOTL_REF(their_identity_key); + result->our_identity_key = our_identity_key; + result->our_base_key = our_base_key; + result->our_ratchet_key = our_ratchet_key; + result->their_base_key = their_base_key; + result->their_ratchet_key = their_ratchet_key; + result->their_identity_key = their_identity_key; + + *parameters = result; + return 0; +} + +ratchet_identity_key_pair *symmetric_axolotl_parameters_get_our_identity_key(const symmetric_axolotl_parameters *parameters) +{ + assert(parameters); + return parameters->our_identity_key; +} + +ec_key_pair *symmetric_axolotl_parameters_get_our_base_key(const symmetric_axolotl_parameters *parameters) +{ + assert(parameters); + return parameters->our_base_key; +} + +ec_key_pair *symmetric_axolotl_parameters_get_our_ratchet_key(const symmetric_axolotl_parameters *parameters) +{ + assert(parameters); + return parameters->our_ratchet_key; +} + +ec_public_key *symmetric_axolotl_parameters_get_their_base_key(const symmetric_axolotl_parameters *parameters) +{ + assert(parameters); + return parameters->their_base_key; +} + +ec_public_key *symmetric_axolotl_parameters_get_their_ratchet_key(const symmetric_axolotl_parameters *parameters) +{ + assert(parameters); + return parameters->their_ratchet_key; +} + +ec_public_key *symmetric_axolotl_parameters_get_their_identity_key(const symmetric_axolotl_parameters *parameters) +{ + assert(parameters); + return parameters->their_identity_key; +} + +void symmetric_axolotl_parameters_destroy(axolotl_type_base *type) +{ + symmetric_axolotl_parameters *parameters = (symmetric_axolotl_parameters *)type; + + AXOLOTL_UNREF(parameters->our_identity_key); + AXOLOTL_UNREF(parameters->our_base_key); + AXOLOTL_UNREF(parameters->our_ratchet_key); + AXOLOTL_UNREF(parameters->their_base_key); + AXOLOTL_UNREF(parameters->their_ratchet_key); + AXOLOTL_UNREF(parameters->their_identity_key); + + free(parameters); +} + +int alice_axolotl_parameters_create( + alice_axolotl_parameters **parameters, + ratchet_identity_key_pair *our_identity_key, + ec_key_pair *our_base_key, + ec_public_key *their_identity_key, + ec_public_key *their_signed_pre_key, + ec_public_key *their_one_time_pre_key, + ec_public_key *their_ratchet_key) +{ + alice_axolotl_parameters *result = 0; + + /* Only "their_one_time_pre_key" is allowed to be null */ + if(!our_identity_key || !our_base_key || !their_identity_key + || !their_signed_pre_key || !their_ratchet_key) { + return AX_ERR_INVAL; + } + + result = malloc(sizeof(alice_axolotl_parameters)); + if(!result) { + return AX_ERR_NOMEM; + } + + memset(result, 0, sizeof(alice_axolotl_parameters)); + + AXOLOTL_INIT(result, alice_axolotl_parameters_destroy); + AXOLOTL_REF(our_identity_key); + AXOLOTL_REF(our_base_key); + AXOLOTL_REF(their_identity_key); + AXOLOTL_REF(their_signed_pre_key); + AXOLOTL_REF(their_ratchet_key); + result->our_identity_key = our_identity_key; + result->our_base_key = our_base_key; + result->their_identity_key = their_identity_key; + result->their_signed_pre_key = their_signed_pre_key; + result->their_ratchet_key = their_ratchet_key; + + if(their_one_time_pre_key) { + AXOLOTL_REF(their_one_time_pre_key); + result->their_one_time_pre_key = their_one_time_pre_key; + } + + *parameters = result; + return 0; +} + +void alice_axolotl_parameters_destroy(axolotl_type_base *type) +{ + alice_axolotl_parameters *parameters = (alice_axolotl_parameters *)type; + + AXOLOTL_UNREF(parameters->our_identity_key); + AXOLOTL_UNREF(parameters->our_base_key); + AXOLOTL_UNREF(parameters->their_identity_key); + AXOLOTL_UNREF(parameters->their_signed_pre_key); + AXOLOTL_UNREF(parameters->their_ratchet_key); + + if(parameters->their_one_time_pre_key) { + AXOLOTL_UNREF(parameters->their_one_time_pre_key); + } + + free(parameters); +} + +int bob_axolotl_parameters_create( + bob_axolotl_parameters **parameters, + ratchet_identity_key_pair *our_identity_key, + ec_key_pair *our_signed_pre_key, + ec_key_pair *our_one_time_pre_key, + ec_key_pair *our_ratchet_key, + ec_public_key *their_identity_key, + ec_public_key *their_base_key) +{ + bob_axolotl_parameters *result = 0; + + /* Only "our_one_time_pre_key" is allowed to be null */ + if(!our_identity_key || !our_signed_pre_key || !our_ratchet_key + || !their_identity_key || !their_base_key) { + return AX_ERR_INVAL; + } + + result = malloc(sizeof(bob_axolotl_parameters)); + if(!result) { + return AX_ERR_NOMEM; + } + + memset(result, 0, sizeof(bob_axolotl_parameters)); + + AXOLOTL_INIT(result, bob_axolotl_parameters_destroy); + AXOLOTL_REF(our_identity_key); + AXOLOTL_REF(our_signed_pre_key); + AXOLOTL_REF(our_ratchet_key); + AXOLOTL_REF(their_identity_key); + AXOLOTL_REF(their_base_key); + result->our_identity_key = our_identity_key; + result->our_signed_pre_key = our_signed_pre_key; + result->our_ratchet_key = our_ratchet_key; + result->their_identity_key = their_identity_key; + result->their_base_key = their_base_key; + + if(our_one_time_pre_key) { + AXOLOTL_REF(our_one_time_pre_key); + result->our_one_time_pre_key = our_one_time_pre_key; + } + + *parameters = result; + return 0; +} + +void bob_axolotl_parameters_destroy(axolotl_type_base *type) +{ + bob_axolotl_parameters *parameters = (bob_axolotl_parameters *)type; + + AXOLOTL_UNREF(parameters->our_identity_key); + AXOLOTL_UNREF(parameters->our_signed_pre_key); + AXOLOTL_UNREF(parameters->our_ratchet_key); + AXOLOTL_UNREF(parameters->their_identity_key); + AXOLOTL_UNREF(parameters->their_base_key); + + if(parameters->our_one_time_pre_key) { + AXOLOTL_UNREF(parameters->our_one_time_pre_key); + } + + free(parameters); +} + +int ratcheting_session_calculate_derived_keys(ratchet_root_key **root_key, ratchet_chain_key **chain_key, + uint32_t version, uint8_t *secret, size_t secret_len, axolotl_context *global_context) +{ + int result = 0; + ssize_t result_size = 0; + hkdf_context *kdf = 0; + ratchet_root_key *root_key_result = 0; + ratchet_chain_key *chain_key_result = 0; + uint8_t *output = 0; + uint8_t salt[HASH_OUTPUT_SIZE]; + static const char key_info[] = "WhisperText"; + + result = hkdf_create(&kdf, (int)version, global_context); + if(result < 0) { + goto complete; + } + + memset(salt, 0, sizeof(salt)); + + result_size = hkdf_derive_secrets(kdf, &output, + secret, secret_len, + salt, sizeof(salt), + (uint8_t *)key_info, sizeof(key_info) - 1, 64); + if(result_size != 64) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + result = ratchet_root_key_create(&root_key_result, kdf, output, 32, global_context); + if(result < 0) { + goto complete; + } + + result = ratchet_chain_key_create(&chain_key_result, kdf, output + 32, 32, 0, global_context); + if(result < 0) { + goto complete; + } + +complete: + if(kdf) { + AXOLOTL_UNREF(kdf); + } + if(output) { + free(output); + } + + if(result < 0) { + if(root_key_result) { + AXOLOTL_UNREF(root_key_result); + } + if(chain_key_result) { + AXOLOTL_UNREF(chain_key_result); + } + } + else { + *root_key = root_key_result; + *chain_key = chain_key_result; + } + + return result; +} + +int ratcheting_session_symmetric_is_alice(symmetric_axolotl_parameters *parameters) +{ + //FIXME Java code checks if our_base_key < their_base_key + // This comparison may not return the same result. However, we should find + // out whether the Java code was doing the right thing and why. + return ec_public_key_memcmp( + ec_key_pair_get_public(parameters->our_base_key), + parameters->their_base_key) < 0; +} + +int ratcheting_session_symmetric_initialize( + session_state *state, uint32_t version, + symmetric_axolotl_parameters *parameters, + axolotl_context *global_context) +{ + int result = 0; + + assert(state); + assert(parameters); + assert(global_context); + + if(ratcheting_session_symmetric_is_alice(parameters)) { + alice_axolotl_parameters *alice_parameters = 0; + result = alice_axolotl_parameters_create(&alice_parameters, + parameters->our_identity_key, + parameters->our_base_key, + parameters->their_identity_key, + parameters->their_base_key, + 0, + parameters->their_ratchet_key); + if(result >= 0) { + result = ratcheting_session_alice_initialize(state, version, alice_parameters, global_context); + } + if(alice_parameters) { + AXOLOTL_UNREF(alice_parameters); + } + } + else { + bob_axolotl_parameters *bob_parameters = 0; + result = bob_axolotl_parameters_create(&bob_parameters, + parameters->our_identity_key, + parameters->our_base_key, + 0, + parameters->our_ratchet_key, + parameters->their_identity_key, + parameters->their_base_key); + if(result >= 0) { + result = ratcheting_session_bob_initialize(state, version, bob_parameters, global_context); + } + if(bob_parameters) { + AXOLOTL_UNREF(bob_parameters); + } + } + return result; +} + +int ratcheting_session_alice_initialize( + session_state *state, uint32_t version, + alice_axolotl_parameters *parameters, + axolotl_context *global_context) +{ + int result = 0; + uint8_t *agreement = 0; + int agreement_len = 0; + ec_key_pair *sending_ratchet_key = 0; + ratchet_root_key *derived_root = 0; + ratchet_chain_key *derived_chain = 0; + ratchet_root_key *sending_chain_root = 0; + ratchet_chain_key *sending_chain_key = 0; + struct vpool vp; + uint8_t *secret = 0; + size_t secret_len = 0; + + assert(state); + assert(parameters); + assert(global_context); + + vpool_init(&vp, 1024, 0); + + result = curve_generate_key_pair(global_context, &sending_ratchet_key); + if(result < 0) { + goto complete; + } + + if(version >= 3) { + uint8_t discontinuity_data[32]; + memset(discontinuity_data, 0xFF, sizeof(discontinuity_data)); + if(!vpool_insert(&vp, vpool_get_length(&vp), discontinuity_data, sizeof(discontinuity_data))) { + result = AX_ERR_NOMEM; + goto complete; + } + } + + agreement_len = curve_calculate_agreement(&agreement, + parameters->their_signed_pre_key, parameters->our_identity_key->private_key); + if(agreement_len < 0) { + result = agreement_len; + goto complete; + } + if(vpool_insert(&vp, vpool_get_length(&vp), agreement, (size_t)agreement_len)) { + free(agreement); agreement = 0; agreement_len = 0; + } + else { + result = AX_ERR_NOMEM; + goto complete; + } + + agreement_len = curve_calculate_agreement(&agreement, + parameters->their_identity_key, ec_key_pair_get_private(parameters->our_base_key)); + if(agreement_len < 0) { + result = agreement_len; + goto complete; + } + if(vpool_insert(&vp, vpool_get_length(&vp), agreement, (size_t)agreement_len)) { + free(agreement); agreement = 0; agreement_len = 0; + } + else { + result = AX_ERR_NOMEM; + goto complete; + } + + agreement_len = curve_calculate_agreement(&agreement, + parameters->their_signed_pre_key, ec_key_pair_get_private(parameters->our_base_key)); + if(agreement_len < 0) { + result = agreement_len; + goto complete; + } + if(vpool_insert(&vp, vpool_get_length(&vp), agreement, (size_t)agreement_len)) { + free(agreement); agreement = 0; agreement_len = 0; + } + else { + result = AX_ERR_NOMEM; + goto complete; + } + + if(version >= 3 && parameters->their_one_time_pre_key) { + agreement_len = curve_calculate_agreement(&agreement, + parameters->their_one_time_pre_key, ec_key_pair_get_private(parameters->our_base_key)); + if(agreement_len < 0) { + result = agreement_len; + goto complete; + } + if(vpool_insert(&vp, vpool_get_length(&vp), agreement, (size_t)agreement_len)) { + free(agreement); agreement = 0; agreement_len = 0; + } + else { + result = AX_ERR_NOMEM; + goto complete; + } + } + + if(vpool_is_empty(&vp)) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + secret = vpool_get_buf(&vp); + secret_len = vpool_get_length(&vp); + + result = ratcheting_session_calculate_derived_keys(&derived_root, &derived_chain, version, secret, secret_len, global_context); + if(result < 0) { + goto complete; + } + + result = ratchet_root_key_create_chain(derived_root, + &sending_chain_root, &sending_chain_key, + parameters->their_ratchet_key, + ec_key_pair_get_private(sending_ratchet_key)); + if(result < 0) { + goto complete; + } + +complete: + if(result >= 0) { + session_state_set_session_version(state, version); + session_state_set_remote_identity_key(state, parameters->their_identity_key); + session_state_set_local_identity_key(state, parameters->our_identity_key->public_key); + session_state_add_receiver_chain(state, parameters->their_ratchet_key, derived_chain); + session_state_set_sender_chain(state, sending_ratchet_key, sending_chain_key); + session_state_set_root_key(state, sending_chain_root); + } + + vpool_final(&vp); + if(agreement) { + free(agreement); + } + if(sending_ratchet_key) { + AXOLOTL_UNREF(sending_ratchet_key); + } + if(derived_root) { + AXOLOTL_UNREF(derived_root); + } + if(derived_chain) { + AXOLOTL_UNREF(derived_chain); + } + if(sending_chain_root) { + AXOLOTL_UNREF(sending_chain_root); + } + if(sending_chain_key) { + AXOLOTL_UNREF(sending_chain_key); + } + + return result; +} + +int ratcheting_session_bob_initialize( + session_state *state, uint32_t version, + bob_axolotl_parameters *parameters, + axolotl_context *global_context) +{ + int result = 0; + uint8_t *agreement = 0; + int agreement_len = 0; + ratchet_root_key *derived_root = 0; + ratchet_chain_key *derived_chain = 0; + struct vpool vp; + uint8_t *secret = 0; + size_t secret_len = 0; + + + assert(state); + assert(parameters); + assert(global_context); + + vpool_init(&vp, 1024, 0); + + if(version >= 3) { + uint8_t discontinuity_data[32]; + memset(discontinuity_data, 0xFF, sizeof(discontinuity_data)); + if(!vpool_insert(&vp, vpool_get_length(&vp), discontinuity_data, sizeof(discontinuity_data))) { + result = AX_ERR_NOMEM; + goto complete; + } + } + + agreement_len = curve_calculate_agreement(&agreement, + parameters->their_identity_key, ec_key_pair_get_private(parameters->our_signed_pre_key)); + if(agreement_len < 0) { + result = agreement_len; + goto complete; + } + if(vpool_insert(&vp, vpool_get_length(&vp), agreement, (size_t)agreement_len)) { + free(agreement); agreement = 0; agreement_len = 0; + } + else { + result = AX_ERR_NOMEM; + goto complete; + } + + agreement_len = curve_calculate_agreement(&agreement, + parameters->their_base_key, parameters->our_identity_key->private_key); + if(agreement_len < 0) { + result = agreement_len; + goto complete; + } + if(vpool_insert(&vp, vpool_get_length(&vp), agreement, (size_t)agreement_len)) { + free(agreement); agreement = 0; agreement_len = 0; + } + else { + result = AX_ERR_NOMEM; + goto complete; + } + + agreement_len = curve_calculate_agreement(&agreement, + parameters->their_base_key, ec_key_pair_get_private(parameters->our_signed_pre_key)); + if(agreement_len < 0) { + result = agreement_len; + goto complete; + } + if(vpool_insert(&vp, vpool_get_length(&vp), agreement, (size_t)agreement_len)) { + free(agreement); agreement = 0; agreement_len = 0; + } + else { + result = AX_ERR_NOMEM; + goto complete; + } + + if(version >= 3 && parameters->our_one_time_pre_key) { + agreement_len = curve_calculate_agreement(&agreement, + parameters->their_base_key, ec_key_pair_get_private(parameters->our_one_time_pre_key)); + if(agreement_len < 0) { + result = agreement_len; + goto complete; + } + if(vpool_insert(&vp, vpool_get_length(&vp), agreement, (size_t)agreement_len)) { + free(agreement); agreement = 0; agreement_len = 0; + } + else { + result = AX_ERR_NOMEM; + goto complete; + } + } + + if(vpool_is_empty(&vp)) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + secret = vpool_get_buf(&vp); + secret_len = vpool_get_length(&vp); + + result = ratcheting_session_calculate_derived_keys(&derived_root, &derived_chain, version, secret, secret_len, global_context); + +complete: + if(result >= 0) { + session_state_set_session_version(state, version); + session_state_set_remote_identity_key(state, parameters->their_identity_key); + session_state_set_local_identity_key(state, parameters->our_identity_key->public_key); + session_state_set_sender_chain(state, parameters->our_ratchet_key, derived_chain); + session_state_set_root_key(state, derived_root); + } + + vpool_final(&vp); + if(agreement) { + free(agreement); + } + if(derived_root) { + AXOLOTL_UNREF(derived_root); + } + if(derived_chain) { + AXOLOTL_UNREF(derived_chain); + } + + return result; +} diff --git a/libs/libaxolotl/src/ratchet.h b/libs/libaxolotl/src/ratchet.h new file mode 100644 index 0000000000..1534b3c24c --- /dev/null +++ b/libs/libaxolotl/src/ratchet.h @@ -0,0 +1,90 @@ +#ifndef RATCHET_H +#define RATCHET_H + +#include <stdint.h> +#include <stddef.h> +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int ratchet_chain_key_create(ratchet_chain_key **chain_key, hkdf_context *kdf, + uint8_t *key, size_t key_len, uint32_t index, + axolotl_context *global_context); +int ratchet_chain_key_get_key(const ratchet_chain_key *chain_key, axolotl_buffer **buffer); +uint32_t ratchet_chain_key_get_index(const ratchet_chain_key *chain_key); +int ratchet_chain_key_get_message_keys(ratchet_chain_key *chain_key, ratchet_message_keys *message_keys); +int ratchet_chain_key_create_next(const ratchet_chain_key *chain_key, ratchet_chain_key **next_chain_key); +void ratchet_chain_key_destroy(axolotl_type_base *type); + +int ratchet_root_key_create(ratchet_root_key **root_key, hkdf_context *kdf, + const uint8_t *key, size_t key_len, + axolotl_context *global_context); +int ratchet_root_key_create_chain(ratchet_root_key *root_key, + ratchet_root_key **new_root_key, ratchet_chain_key **new_chain_key, + ec_public_key *their_ratchet_key, + ec_private_key *our_ratchet_key_private); +int ratchet_root_key_get_key(ratchet_root_key *root_key, axolotl_buffer **buffer); +int ratchet_root_key_compare(const ratchet_root_key *key1, const ratchet_root_key *key2); +void ratchet_root_key_destroy(axolotl_type_base *type); + +int ratchet_identity_key_pair_create( + ratchet_identity_key_pair **key_pair, + ec_public_key *public_key, + ec_private_key *private_key); +int ratchet_identity_key_pair_serialize(axolotl_buffer **buffer, const ratchet_identity_key_pair *key_pair); +int ratchet_identity_key_pair_deserialize(ratchet_identity_key_pair **key_pair, const uint8_t *data, size_t len, axolotl_context *global_context); +ec_public_key *ratchet_identity_key_pair_get_public(const ratchet_identity_key_pair *key_pair); +ec_private_key *ratchet_identity_key_pair_get_private(const ratchet_identity_key_pair *key_pair); +void ratchet_identity_key_pair_destroy(axolotl_type_base *type); + +typedef struct symmetric_axolotl_parameters symmetric_axolotl_parameters; +typedef struct alice_axolotl_parameters alice_axolotl_parameters; +typedef struct bob_axolotl_parameters bob_axolotl_parameters; + +int symmetric_axolotl_parameters_create( + symmetric_axolotl_parameters **parameters, + ratchet_identity_key_pair *our_identity_key, + ec_key_pair *our_base_key, + ec_key_pair *our_ratchet_key, + ec_public_key *their_base_key, + ec_public_key *their_ratchet_key, + ec_public_key *their_identity_key); +ratchet_identity_key_pair *symmetric_axolotl_parameters_get_our_identity_key(const symmetric_axolotl_parameters *parameters); +ec_key_pair *symmetric_axolotl_parameters_get_our_base_key(const symmetric_axolotl_parameters *parameters); +ec_key_pair *symmetric_axolotl_parameters_get_our_ratchet_key(const symmetric_axolotl_parameters *parameters); +ec_public_key *symmetric_axolotl_parameters_get_their_base_key(const symmetric_axolotl_parameters *parameters); +ec_public_key *symmetric_axolotl_parameters_get_their_ratchet_key(const symmetric_axolotl_parameters *parameters); +ec_public_key *symmetric_axolotl_parameters_get_their_identity_key(const symmetric_axolotl_parameters *parameters); +void symmetric_axolotl_parameters_destroy(axolotl_type_base *type); + +int alice_axolotl_parameters_create( + alice_axolotl_parameters **parameters, + ratchet_identity_key_pair *our_identity_key, + ec_key_pair *our_base_key, + ec_public_key *their_identity_key, + ec_public_key *their_signed_pre_key, + ec_public_key *their_one_time_pre_key, + ec_public_key *their_ratchet_key); +void alice_axolotl_parameters_destroy(axolotl_type_base *type); + +int bob_axolotl_parameters_create( + bob_axolotl_parameters **parameters, + ratchet_identity_key_pair *our_identity_key, + ec_key_pair *our_signed_pre_key, + ec_key_pair *our_one_time_pre_key, + ec_key_pair *our_ratchet_key, + ec_public_key *their_identity_key, + ec_public_key *their_base_key); +void bob_axolotl_parameters_destroy(axolotl_type_base *type); + +int ratcheting_session_symmetric_initialize(session_state *state, uint32_t version, symmetric_axolotl_parameters *parameters, axolotl_context *global_context); +int ratcheting_session_alice_initialize(session_state *state, uint32_t version, alice_axolotl_parameters *parameters, axolotl_context *global_context); +int ratcheting_session_bob_initialize(session_state *state, uint32_t version, bob_axolotl_parameters *parameters, axolotl_context *global_context); + +#ifdef __cplusplus +} +#endif + +#endif /* RATCHET_H */ diff --git a/libs/libaxolotl/src/sender_key.c b/libs/libaxolotl/src/sender_key.c new file mode 100644 index 0000000000..6647598fc9 --- /dev/null +++ b/libs/libaxolotl/src/sender_key.c @@ -0,0 +1,285 @@ +#include "sender_key.h" + +#include <assert.h> +#include <string.h> + +#include "hkdf.h" +#include "axolotl_internal.h" + +#define HASH_OUTPUT_SIZE 32 + +struct sender_message_key { + axolotl_type_base base; + uint32_t iteration; + axolotl_buffer *iv; + axolotl_buffer *cipher_key; + axolotl_buffer *seed; + axolotl_context *global_context; +}; + +struct sender_chain_key { + axolotl_type_base base; + uint32_t iteration; + axolotl_buffer *chain_key; + axolotl_context *global_context; +}; + +static int sender_chain_key_get_derivative(axolotl_buffer **derivative, uint8_t seed, axolotl_buffer *key, + axolotl_context *global_context); + +int sender_message_key_create(sender_message_key **key, + uint32_t iteration, axolotl_buffer *seed, + axolotl_context *global_context) +{ + sender_message_key *result = 0; + int ret = 0; + ssize_t ret_size = 0; + hkdf_context *kdf = 0; + static const char info_material[] = "WhisperGroup"; + uint8_t salt[HASH_OUTPUT_SIZE]; + uint8_t *derivative = 0; + + assert(global_context); + + if(!seed) { + return AX_ERR_INVAL; + } + + memset(salt, 0, sizeof(salt)); + + result = malloc(sizeof(sender_message_key)); + if(!result) { + return AX_ERR_NOMEM; + } + + AXOLOTL_INIT(result, sender_message_key_destroy); + + ret = hkdf_create(&kdf, 3, global_context); + if(ret < 0) { + goto complete; + } + + ret_size = hkdf_derive_secrets(kdf, &derivative, + axolotl_buffer_data(seed), axolotl_buffer_len(seed), + salt, sizeof(salt), + (uint8_t *)info_material, sizeof(info_material) - 1, 48); + if(ret_size != 48) { + ret = (ret_size < 0) ? (int)ret_size : AX_ERR_UNKNOWN; + axolotl_log(global_context, AX_LOG_WARNING, "hkdf_derive_secrets failed"); + goto complete; + } + + result->iteration = iteration; + + result->seed = axolotl_buffer_copy(seed); + if(!result->seed) { + ret = AX_ERR_NOMEM; + goto complete; + } + + result->iv = axolotl_buffer_create(derivative, 16); + if(!result->iv) { + ret = AX_ERR_NOMEM; + goto complete; + } + + result->cipher_key = axolotl_buffer_create(derivative + 16, 32); + if(!result->cipher_key) { + ret = AX_ERR_NOMEM; + goto complete; + } + + result->global_context = global_context; + +complete: + AXOLOTL_UNREF(kdf); + if(derivative) { + free(derivative); + } + if(ret < 0) { + AXOLOTL_UNREF(result); + } + else { + ret = 0; + *key = result; + } + return ret; +} + +uint32_t sender_message_key_get_iteration(sender_message_key *key) +{ + assert(key); + return key->iteration; +} + +axolotl_buffer *sender_message_key_get_iv(sender_message_key *key) +{ + assert(key); + return key->iv; +} + +axolotl_buffer *sender_message_key_get_cipher_key(sender_message_key *key) +{ + assert(key); + return key->cipher_key; +} + +axolotl_buffer *sender_message_key_get_seed(sender_message_key *key) +{ + assert(key); + return key->seed; +} + +void sender_message_key_destroy(axolotl_type_base *type) +{ + sender_message_key *key = (sender_message_key *)type; + axolotl_buffer_bzero_free(key->iv); + axolotl_buffer_bzero_free(key->cipher_key); + axolotl_buffer_bzero_free(key->seed); + free(key); +} + +int sender_chain_key_create(sender_chain_key **key, + uint32_t iteration, axolotl_buffer *chain_key, + axolotl_context *global_context) +{ + sender_chain_key *result = 0; + int ret = 0; + + assert(global_context); + + if(!chain_key) { + return AX_ERR_INVAL; + } + + result = malloc(sizeof(sender_chain_key)); + if(!result) { + return AX_ERR_NOMEM; + } + + AXOLOTL_INIT(result, sender_chain_key_destroy); + + result->iteration = iteration; + + result->chain_key = axolotl_buffer_copy(chain_key); + if(!result->chain_key) { + ret = AX_ERR_NOMEM; + goto complete; + } + + result->global_context = global_context; + +complete: + if(ret < 0) { + AXOLOTL_UNREF(result); + } + else { + ret = 0; + *key = result; + } + return ret; +} + +uint32_t sender_chain_key_get_iteration(sender_chain_key *key) +{ + assert(key); + return key->iteration; +} + +int sender_chain_key_create_message_key(sender_chain_key *key, sender_message_key **message_key) +{ + static const uint8_t MESSAGE_KEY_SEED = 0x01; + int ret = 0; + axolotl_buffer *derivative = 0; + sender_message_key *result = 0; + + assert(key); + + ret = sender_chain_key_get_derivative(&derivative, MESSAGE_KEY_SEED, key->chain_key, key->global_context); + if(ret < 0) { + goto complete; + } + + ret = sender_message_key_create(&result, key->iteration, derivative, key->global_context); + +complete: + axolotl_buffer_free(derivative); + if(ret >= 0) { + ret = 0; + *message_key = result; + } + return ret; +} + +int sender_chain_key_create_next(sender_chain_key *key, sender_chain_key **next_key) +{ + static const uint8_t CHAIN_KEY_SEED = 0x02; + int ret = 0; + axolotl_buffer *derivative = 0; + sender_chain_key *result = 0; + + assert(key); + + ret = sender_chain_key_get_derivative(&derivative, CHAIN_KEY_SEED, key->chain_key, key->global_context); + if(ret < 0) { + goto complete; + } + + ret = sender_chain_key_create(&result, key->iteration + 1, derivative, key->global_context); + +complete: + axolotl_buffer_free(derivative); + if(ret >= 0) { + ret = 0; + *next_key = result; + } + return ret; +} + +axolotl_buffer *sender_chain_key_get_seed(sender_chain_key *key) +{ + assert(key); + return key->chain_key; +} + +void sender_chain_key_destroy(axolotl_type_base *type) +{ + sender_chain_key *key = (sender_chain_key *)type; + axolotl_buffer_bzero_free(key->chain_key); + free(key); +} + +int sender_chain_key_get_derivative(axolotl_buffer **derivative, uint8_t seed, axolotl_buffer *key, + axolotl_context *global_context) +{ + int result = 0; + axolotl_buffer *output_buffer = 0; + void *hmac_context = 0; + + result = axolotl_hmac_sha256_init(global_context, &hmac_context, + axolotl_buffer_data(key), axolotl_buffer_len(key)); + if(result < 0) { + goto complete; + } + + result = axolotl_hmac_sha256_update(global_context, hmac_context, &seed, sizeof(seed)); + if(result < 0) { + goto complete; + } + + result = axolotl_hmac_sha256_final(global_context, hmac_context, &output_buffer); + if(result < 0) { + goto complete; + } + +complete: + axolotl_hmac_sha256_cleanup(global_context, hmac_context); + + if(result < 0) { + axolotl_buffer_free(output_buffer); + } + else { + *derivative = output_buffer; + } + return result; +} diff --git a/libs/libaxolotl/src/sender_key.h b/libs/libaxolotl/src/sender_key.h new file mode 100644 index 0000000000..61bd7ef553 --- /dev/null +++ b/libs/libaxolotl/src/sender_key.h @@ -0,0 +1,34 @@ +#ifndef SENDER_KEY +#define SENDER_KEY + +#include <stdint.h> +#include <stddef.h> +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int sender_message_key_create(sender_message_key **key, + uint32_t iteration, axolotl_buffer *seed, + axolotl_context *global_context); +uint32_t sender_message_key_get_iteration(sender_message_key *key); +axolotl_buffer *sender_message_key_get_iv(sender_message_key *key); +axolotl_buffer *sender_message_key_get_cipher_key(sender_message_key *key); +axolotl_buffer *sender_message_key_get_seed(sender_message_key *key); +void sender_message_key_destroy(axolotl_type_base *type); + +int sender_chain_key_create(sender_chain_key **key, + uint32_t iteration, axolotl_buffer *chain_key, + axolotl_context *global_context); +uint32_t sender_chain_key_get_iteration(sender_chain_key *key); +int sender_chain_key_create_message_key(sender_chain_key *key, sender_message_key **message_key); +int sender_chain_key_create_next(sender_chain_key *key, sender_chain_key **next_key); +axolotl_buffer *sender_chain_key_get_seed(sender_chain_key *key); +void sender_chain_key_destroy(axolotl_type_base *type); + +#ifdef __cplusplus +} +#endif + +#endif /* SENDER_KEY */ diff --git a/libs/libaxolotl/src/sender_key_record.c b/libs/libaxolotl/src/sender_key_record.c new file mode 100644 index 0000000000..0d081f4278 --- /dev/null +++ b/libs/libaxolotl/src/sender_key_record.c @@ -0,0 +1,343 @@ +#include "sender_key_record.h" + +#include <string.h> + +#include "axolotl_internal.h" +#include "sender_key_state.h" +#include "sender_key.h" +#include "utlist.h" +#include "LocalStorageProtocol.pb-c.h" + +#define MAX_STATES 5 + +typedef struct sender_key_state_node { + sender_key_state *state; + struct sender_key_state_node *prev, *next; +} sender_key_state_node; + +struct sender_key_record +{ + axolotl_type_base base; + sender_key_state_node *sender_key_states_head; + axolotl_context *global_context; +}; + +int sender_key_record_create(sender_key_record **record, + axolotl_context *global_context) +{ + sender_key_record *result = malloc(sizeof(sender_key_record)); + if(!result) { + return AX_ERR_NOMEM; + } + memset(result, 0, sizeof(sender_key_record)); + AXOLOTL_INIT(result, sender_key_record_destroy); + + result->global_context = global_context; + + *record = result; + return 0; +} + +int sender_key_record_serialize(axolotl_buffer **buffer, sender_key_record *record) +{ + int result = 0; + size_t result_size = 0; + unsigned int i = 0; + Textsecure__SenderKeyRecordStructure record_structure = TEXTSECURE__SENDER_KEY_RECORD_STRUCTURE__INIT; + sender_key_state_node *cur_node = 0; + axolotl_buffer *result_buf = 0; + uint8_t *data; + size_t len; + + if(record->sender_key_states_head) { + unsigned int count; + DL_COUNT(record->sender_key_states_head, cur_node, count); + + if(count > SIZE_MAX / sizeof(Textsecure__SenderKeyStateStructure *)) { + result = AX_ERR_NOMEM; + goto complete; + } + + record_structure.senderkeystates = malloc(sizeof(Textsecure__SenderKeyStateStructure *) * count); + if(!record_structure.senderkeystates) { + result = AX_ERR_NOMEM; + goto complete; + } + + i = 0; + DL_FOREACH(record->sender_key_states_head, cur_node) { + record_structure.senderkeystates[i] = malloc(sizeof(Textsecure__SenderKeyStateStructure)); + if(!record_structure.senderkeystates[i]) { + result = AX_ERR_NOMEM; + break; + } + textsecure__sender_key_state_structure__init(record_structure.senderkeystates[i]); + + result = sender_key_state_serialize_prepare(cur_node->state, record_structure.senderkeystates[i]); + if(result < 0) { + break; + } + i++; + } + record_structure.n_senderkeystates = i; + if(result < 0) { + goto complete; + } + } + + len = textsecure__sender_key_record_structure__get_packed_size(&record_structure); + + result_buf = axolotl_buffer_alloc(len); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(result_buf); + result_size = textsecure__sender_key_record_structure__pack(&record_structure, data); + if(result_size != len) { + axolotl_buffer_free(result_buf); + result = AX_ERR_INVALID_PROTO_BUF; + result_buf = 0; + goto complete; + } + +complete: + if(record_structure.senderkeystates) { + for(i = 0; i < record_structure.n_senderkeystates; i++) { + if(record_structure.senderkeystates[i]) { + sender_key_state_serialize_prepare_free(record_structure.senderkeystates[i]); + } + } + free(record_structure.senderkeystates); + } + + if(result >= 0) { + *buffer = result_buf; + } + return result; +} + +int sender_key_record_deserialize(sender_key_record **record, const uint8_t *data, size_t len, axolotl_context *global_context) +{ + int result = 0; + sender_key_record *result_record = 0; + Textsecure__SenderKeyRecordStructure *record_structure = 0; + + record_structure = textsecure__sender_key_record_structure__unpack(0, len, data); + if(!record_structure) { + result = AX_ERR_INVALID_PROTO_BUF; + goto complete; + } + + result = sender_key_record_create(&result_record, global_context); + if(result < 0) { + goto complete; + } + + if(record_structure->n_senderkeystates > 0) { + unsigned int i; + sender_key_state_node *state_node = 0; + sender_key_state *state_element = 0; + for(i = 0; i < record_structure->n_senderkeystates; i++) { + result = sender_key_state_deserialize_protobuf(&state_element, record_structure->senderkeystates[i], global_context); + if(result < 0) { + goto complete; + } + + state_node = malloc(sizeof(sender_key_state_node)); + if(!state_node) { + result = AX_ERR_NOMEM; + goto complete; + } + + state_node->state = state_element; + DL_APPEND(result_record->sender_key_states_head, state_node); + } + } + +complete: + if(record_structure) { + textsecure__sender_key_record_structure__free_unpacked(record_structure, 0); + } + if(result_record) { + if(result < 0) { + AXOLOTL_UNREF(result_record); + } + else { + *record = result_record; + } + } + return result; +} + +int sender_key_record_copy(sender_key_record **record, sender_key_record *other_record, axolotl_context *global_context) +{ + int result = 0; + axolotl_buffer *buffer = 0; + uint8_t *data; + size_t len; + + assert(other_record); + assert(global_context); + + result = sender_key_record_serialize(&buffer, other_record); + if(result < 0) { + goto complete; + } + + data = axolotl_buffer_data(buffer); + len = axolotl_buffer_len(buffer); + + result = sender_key_record_deserialize(record, data, len, global_context); + if(result < 0) { + goto complete; + } + +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + return result; +} + +int sender_key_record_is_empty(sender_key_record *record) +{ + assert(record); + if(record->sender_key_states_head) { + return 0; + } + else { + return 1; + } +} + +int sender_key_record_get_sender_key_state(sender_key_record *record, sender_key_state **state) +{ + assert(record); + if(record->sender_key_states_head) { + *state = record->sender_key_states_head->state; + return 0; + } + else { + axolotl_log(record->global_context, AX_LOG_ERROR, "No key state in record!"); + return AX_ERR_INVALID_KEY_ID; + } +} + +int sender_key_record_get_sender_key_state_by_id(sender_key_record *record, sender_key_state **state, uint32_t key_id) +{ + sender_key_state_node *cur_node; + assert(record); + + DL_FOREACH(record->sender_key_states_head, cur_node) { + if(sender_key_state_get_key_id(cur_node->state) == key_id) { + *state = cur_node->state; + return 0; + } + } + + axolotl_log(record->global_context, AX_LOG_ERROR, "No keys for: %d", key_id); + return AX_ERR_INVALID_KEY_ID; +} + +static int sender_key_record_add_sender_key_state_impl(sender_key_record *record, + uint32_t id, uint32_t iteration, axolotl_buffer *chain_key, + ec_public_key *signature_public_key, ec_private_key *signature_private_key) +{ + int result = 0; + sender_chain_key *chain_key_element = 0; + sender_key_state *state = 0; + sender_key_state_node *state_node = 0; + int count; + assert(record); + + result = sender_chain_key_create(&chain_key_element, iteration, chain_key, record->global_context); + if(result < 0) { + goto complete; + } + + result = sender_key_state_create(&state, id, chain_key_element, + signature_public_key, signature_private_key, + record->global_context); + if(result < 0) { + goto complete; + } + + state_node = malloc(sizeof(sender_key_state_node)); + if(!state_node) { + result = AX_ERR_NOMEM; + goto complete; + } + + state_node->state = state; + DL_PREPEND(record->sender_key_states_head, state_node); + + DL_COUNT(record->sender_key_states_head, state_node, count); + while(count > MAX_STATES) { + state_node = record->sender_key_states_head->prev; + DL_DELETE(record->sender_key_states_head, state_node); + if(state_node->state) { + AXOLOTL_UNREF(state_node->state); + } + free(state_node); + --count; + } + +complete: + AXOLOTL_UNREF(chain_key_element); + if(result < 0) { + AXOLOTL_UNREF(state); + } + return result; +} + +int sender_key_record_add_sender_key_state(sender_key_record *record, + uint32_t id, uint32_t iteration, axolotl_buffer *chain_key, ec_public_key *signature_key) +{ + int result = sender_key_record_add_sender_key_state_impl( + record, id, iteration, chain_key, signature_key, 0); + return result; +} + +int sender_key_record_set_sender_key_state(sender_key_record *record, + uint32_t id, uint32_t iteration, axolotl_buffer *chain_key, ec_key_pair *signature_key_pair) +{ + int result = 0; + sender_key_state_node *cur_node; + sender_key_state_node *tmp_node; + assert(record); + + DL_FOREACH_SAFE(record->sender_key_states_head, cur_node, tmp_node) { + DL_DELETE(record->sender_key_states_head, cur_node); + if(cur_node->state) { + AXOLOTL_UNREF(cur_node->state); + } + free(cur_node); + } + record->sender_key_states_head = 0; + + result = sender_key_record_add_sender_key_state_impl( + record, id, iteration, chain_key, + ec_key_pair_get_public(signature_key_pair), + ec_key_pair_get_private(signature_key_pair)); + return result; +} + +void sender_key_record_destroy(axolotl_type_base *type) +{ + sender_key_record *record = (sender_key_record *)type; + sender_key_state_node *cur_node; + sender_key_state_node *tmp_node; + + DL_FOREACH_SAFE(record->sender_key_states_head, cur_node, tmp_node) { + DL_DELETE(record->sender_key_states_head, cur_node); + if(cur_node->state) { + AXOLOTL_UNREF(cur_node->state); + } + free(cur_node); + } + record->sender_key_states_head = 0; + + free(record); +} diff --git a/libs/libaxolotl/src/sender_key_record.h b/libs/libaxolotl/src/sender_key_record.h new file mode 100644 index 0000000000..a50d6451ac --- /dev/null +++ b/libs/libaxolotl/src/sender_key_record.h @@ -0,0 +1,30 @@ +#ifndef SENDER_KEY_RECORD_H +#define SENDER_KEY_RECORD_H + +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int sender_key_record_create(sender_key_record **record, + axolotl_context *global_context); +int sender_key_record_serialize(axolotl_buffer **buffer, sender_key_record *record); +int sender_key_record_deserialize(sender_key_record **record, const uint8_t *data, size_t len, axolotl_context *global_context); +int sender_key_record_copy(sender_key_record **record, sender_key_record *other_state, axolotl_context *global_context); + +int sender_key_record_is_empty(sender_key_record *record); +int sender_key_record_get_sender_key_state(sender_key_record *record, sender_key_state **state); +int sender_key_record_get_sender_key_state_by_id(sender_key_record *record, sender_key_state **state, uint32_t key_id); +int sender_key_record_add_sender_key_state(sender_key_record *record, + uint32_t id, uint32_t iteration, axolotl_buffer *chain_key, ec_public_key *signature_key); +int sender_key_record_set_sender_key_state(sender_key_record *record, + uint32_t id, uint32_t iteration, axolotl_buffer *chain_key, ec_key_pair *signature_key_pair); + +void sender_key_record_destroy(axolotl_type_base *type); + +#ifdef __cplusplus +} +#endif + +#endif /* SENDER_KEY_RECORD_H */ diff --git a/libs/libaxolotl/src/sender_key_state.c b/libs/libaxolotl/src/sender_key_state.c new file mode 100644 index 0000000000..fd65965fda --- /dev/null +++ b/libs/libaxolotl/src/sender_key_state.c @@ -0,0 +1,550 @@ +#include "sender_key_state.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "sender_key.h" +#include "axolotl_internal.h" +#include "utlist.h" +#include "LocalStorageProtocol.pb-c.h" + +#define MAX_MESSAGE_KEYS 2000 + +typedef struct sender_message_key_node { + sender_message_key *key; + struct sender_message_key_node *prev, *next; +} sender_message_key_node; + +struct sender_key_state +{ + axolotl_type_base base; + + uint32_t key_id; + sender_chain_key *chain_key; + ec_public_key *signature_public_key; + ec_private_key *signature_private_key; + sender_message_key_node *message_keys_head; + + axolotl_context *global_context; +}; + +int sender_key_state_create(sender_key_state **state, + uint32_t id, sender_chain_key *chain_key, + ec_public_key *signature_public_key, ec_private_key *signature_private_key, + axolotl_context *global_context) +{ + sender_key_state *result = 0; + + if(!chain_key || !signature_public_key) { + return AX_ERR_INVAL; + } + + result = malloc(sizeof(sender_key_state)); + if(!result) { + return AX_ERR_NOMEM; + } + memset(result, 0, sizeof(sender_key_state)); + AXOLOTL_INIT(result, sender_key_state_destroy); + + result->key_id = id; + + AXOLOTL_REF(chain_key); + result->chain_key = chain_key; + + AXOLOTL_REF(signature_public_key); + result->signature_public_key = signature_public_key; + + if(signature_private_key) { + AXOLOTL_REF(signature_private_key); + result->signature_private_key = signature_private_key; + } + + result->global_context = global_context; + + *state = result; + return 0; +} + +int sender_key_state_serialize(axolotl_buffer **buffer, sender_key_state *state) +{ + int result = 0; + size_t result_size = 0; + uint8_t *data; + size_t len; + Textsecure__SenderKeyStateStructure *state_structure = 0; + axolotl_buffer *result_buf = 0; + + state_structure = malloc(sizeof(Textsecure__SenderKeyStateStructure)); + if(!state_structure) { + result = AX_ERR_NOMEM; + goto complete; + } + textsecure__sender_key_state_structure__init(state_structure); + + result = sender_key_state_serialize_prepare(state, state_structure); + if(result < 0) { + goto complete; + } + + len = textsecure__sender_key_state_structure__get_packed_size(state_structure); + + result_buf = axolotl_buffer_alloc(len); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(result_buf); + result_size = textsecure__sender_key_state_structure__pack(state_structure, data); + if(result_size != len) { + axolotl_buffer_free(result_buf); + result = AX_ERR_INVALID_PROTO_BUF; + result_buf = 0; + goto complete; + } + +complete: + if(state_structure) { + sender_key_state_serialize_prepare_free(state_structure); + } + if(result >= 0) { + *buffer = result_buf; + } + return result; +} + +int sender_key_state_deserialize(sender_key_state **state, const uint8_t *data, size_t len, axolotl_context *global_context) +{ + int result = 0; + Textsecure__SenderKeyStateStructure *state_structure = 0; + sender_key_state *result_state = 0; + + state_structure = textsecure__sender_key_state_structure__unpack(0, len, data); + if(!state_structure) { + result = AX_ERR_INVALID_PROTO_BUF; + goto complete; + } + + result = sender_key_state_deserialize_protobuf(&result_state, state_structure, global_context); + if(result < 0) { + goto complete; + } + +complete: + if(state_structure) { + textsecure__sender_key_state_structure__free_unpacked(state_structure, 0); + } + if(result_state) { + if(result < 0) { + AXOLOTL_UNREF(result_state); + } + else { + *state = result_state; + } + } + + return result; +} + +int sender_key_state_serialize_prepare(sender_key_state *state, Textsecure__SenderKeyStateStructure *state_structure) +{ + int result = 0; + size_t i = 0; + Textsecure__SenderKeyStateStructure__SenderChainKey *chain_key_structure = 0; + Textsecure__SenderKeyStateStructure__SenderSigningKey *signing_key_structure = 0; + sender_message_key_node *cur_node = 0; + axolotl_buffer *chain_key_seed = 0; + + assert(state); + assert(state_structure); + + /* Sender key ID */ + state_structure->has_senderkeyid = 1; + state_structure->senderkeyid = state->key_id; + + /* Sender chain key */ + chain_key_structure = malloc(sizeof(Textsecure__SenderKeyStateStructure__SenderChainKey)); + if(!chain_key_structure) { + result = AX_ERR_NOMEM; + goto complete; + } + textsecure__sender_key_state_structure__sender_chain_key__init(chain_key_structure); + state_structure->senderchainkey = chain_key_structure; + + chain_key_structure->iteration = sender_chain_key_get_iteration(state->chain_key); + chain_key_structure->has_iteration = 1; + + chain_key_seed = sender_chain_key_get_seed(state->chain_key); + chain_key_structure->seed.data = axolotl_buffer_data(chain_key_seed); + chain_key_structure->seed.len = axolotl_buffer_len(chain_key_seed); + chain_key_structure->has_seed = 1; + + /* Sender signing key */ + signing_key_structure = malloc(sizeof(Textsecure__SenderKeyStateStructure__SenderSigningKey)); + if(!signing_key_structure) { + result = AX_ERR_NOMEM; + goto complete; + } + textsecure__sender_key_state_structure__sender_signing_key__init(signing_key_structure); + state_structure->sendersigningkey = signing_key_structure; + + if(state->signature_public_key) { + result = ec_public_key_serialize_protobuf(&(signing_key_structure->public_), state->signature_public_key); + if(result < 0) { + goto complete; + } + signing_key_structure->has_public_ = 1; + } + + if(state->signature_private_key) { + result = ec_private_key_serialize_protobuf(&(signing_key_structure->private_), state->signature_private_key); + if(result < 0) { + goto complete; + } + signing_key_structure->has_private_ = 1; + } + + /* Sender message keys */ + if(state->message_keys_head) { + unsigned int count; + DL_COUNT(state->message_keys_head, cur_node, count); + + if(count > SIZE_MAX / sizeof(Textsecure__SenderKeyStateStructure__SenderMessageKey *)) { + result = AX_ERR_NOMEM; + goto complete; + } + + state_structure->sendermessagekeys = malloc(sizeof(Textsecure__SenderKeyStateStructure__SenderMessageKey *) * count); + if(!state_structure->sendermessagekeys) { + result = AX_ERR_NOMEM; + goto complete; + } + + i = 0; + DL_FOREACH(state->message_keys_head, cur_node) { + axolotl_buffer *seed = 0; + state_structure->sendermessagekeys[i] = malloc(sizeof(Textsecure__SenderKeyStateStructure__SenderMessageKey)); + if(!state_structure->sendermessagekeys[i]) { + result = AX_ERR_NOMEM; + break; + } + textsecure__sender_key_state_structure__sender_message_key__init(state_structure->sendermessagekeys[i]); + + state_structure->sendermessagekeys[i]->iteration = sender_message_key_get_iteration(cur_node->key); + state_structure->sendermessagekeys[i]->has_iteration = 1; + + seed = sender_message_key_get_seed(cur_node->key); + state_structure->sendermessagekeys[i]->seed.data = axolotl_buffer_data(seed); + state_structure->sendermessagekeys[i]->seed.len = axolotl_buffer_len(seed); + state_structure->sendermessagekeys[i]->has_seed = 1; + + if(result < 0) { + break; + } + i++; + } + state_structure->n_sendermessagekeys = i; + if(result < 0) { + goto complete; + } + } + +complete: + return result; +} + +void sender_key_state_serialize_prepare_free(Textsecure__SenderKeyStateStructure *state_structure) +{ + unsigned int i = 0; + if(state_structure->senderchainkey) { + free(state_structure->senderchainkey); + } + if(state_structure->sendersigningkey) { + if(state_structure->sendersigningkey->public_.data) { + free(state_structure->sendersigningkey->public_.data); + } + if(state_structure->sendersigningkey->private_.data) { + free(state_structure->sendersigningkey->private_.data); + } + free(state_structure->sendersigningkey); + } + + if(state_structure->sendermessagekeys) { + for(i = 0; i < state_structure->n_sendermessagekeys; i++) { + if(state_structure->sendermessagekeys[i]) { + free(state_structure->sendermessagekeys[i]); + } + } + free(state_structure->sendermessagekeys); + } + free(state_structure); +} + +int sender_key_state_deserialize_protobuf(sender_key_state **state, Textsecure__SenderKeyStateStructure *state_structure, axolotl_context *global_context) +{ + int result = 0; + sender_key_state *result_state = 0; + sender_chain_key *chain_key = 0; + ec_public_key *signature_public_key = 0; + ec_private_key *signature_private_key = 0; + + if(state_structure->senderchainkey + && state_structure->senderchainkey->has_iteration + && state_structure->senderchainkey->has_seed) { + axolotl_buffer *seed_buffer = axolotl_buffer_create( + state_structure->senderchainkey->seed.data, + state_structure->senderchainkey->seed.len); + if(!seed_buffer) { + result = AX_ERR_NOMEM; + goto complete; + } + + result = sender_chain_key_create(&chain_key, + state_structure->senderchainkey->iteration, + seed_buffer, + global_context); + axolotl_buffer_free(seed_buffer); + if(result < 0) { + goto complete; + } + } + + if(state_structure->sendersigningkey) { + if(state_structure->sendersigningkey->has_public_) { + result = curve_decode_point(&signature_public_key, + state_structure->sendersigningkey->public_.data, + state_structure->sendersigningkey->public_.len, + global_context); + if(result < 0) { + goto complete; + } + } + if(state_structure->sendersigningkey->has_private_) { + result = curve_decode_private_point(&signature_private_key, + state_structure->sendersigningkey->private_.data, + state_structure->sendersigningkey->private_.len, + global_context); + if(result < 0) { + goto complete; + } + } + } + + if(state_structure->has_senderkeyid && chain_key && signature_public_key) { + unsigned int i; + result = sender_key_state_create(&result_state, + state_structure->senderkeyid, chain_key, + signature_public_key, signature_private_key, + global_context); + + if(state_structure->n_sendermessagekeys > 0) { + for(i = 0; i < state_structure->n_sendermessagekeys; i++) { + axolotl_buffer *seed_buffer; + sender_message_key *message_key; + Textsecure__SenderKeyStateStructure__SenderMessageKey *message_key_structure = + state_structure->sendermessagekeys[i]; + + if(!message_key_structure->has_iteration || !message_key_structure->has_seed) { + continue; + } + + seed_buffer = axolotl_buffer_create( + message_key_structure->seed.data, + message_key_structure->seed.len); + if(!seed_buffer) { + result = AX_ERR_NOMEM; + goto complete; + } + + result = sender_message_key_create(&message_key, + message_key_structure->iteration, seed_buffer, + global_context); + axolotl_buffer_free(seed_buffer); + if(result < 0) { + goto complete; + } + + result = sender_key_state_add_sender_message_key(result_state, message_key); + if(result < 0) { + goto complete; + } + AXOLOTL_UNREF(message_key); + } + } + } + +complete: + if(chain_key) { + AXOLOTL_UNREF(chain_key); + } + if(signature_public_key) { + AXOLOTL_UNREF(signature_public_key); + } + if(signature_private_key) { + AXOLOTL_UNREF(signature_private_key); + } + if(result >= 0) { + *state = result_state; + } + else { + if(result_state) { + AXOLOTL_UNREF(result_state); + } + } + return result; +} + +int sender_key_state_copy(sender_key_state **state, sender_key_state *other_state, axolotl_context *global_context) +{ + int result = 0; + axolotl_buffer *buffer = 0; + uint8_t *data; + size_t len; + + assert(other_state); + assert(global_context); + + result = sender_key_state_serialize(&buffer, other_state); + if(result < 0) { + goto complete; + } + + data = axolotl_buffer_data(buffer); + len = axolotl_buffer_len(buffer); + + result = sender_key_state_deserialize(state, data, len, global_context); + if(result < 0) { + goto complete; + } + +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + return result; +} + +uint32_t sender_key_state_get_key_id(sender_key_state *state) +{ + assert(state); + return state->key_id; +} + +sender_chain_key *sender_key_state_get_chain_key(sender_key_state *state) +{ + assert(state); + return state->chain_key; +} + +void sender_key_state_set_chain_key(sender_key_state *state, sender_chain_key *chain_key) +{ + assert(state); + assert(chain_key); + + if(state->chain_key) { + AXOLOTL_UNREF(state->chain_key); + } + AXOLOTL_REF(chain_key); + state->chain_key = chain_key; +} + +ec_public_key *sender_key_state_get_signing_key_public(sender_key_state *state) +{ + assert(state); + return state->signature_public_key; +} + +ec_private_key *sender_key_state_get_signing_key_private(sender_key_state *state) +{ + assert(state); + return state->signature_private_key; +} + +int sender_key_state_has_sender_message_key(sender_key_state *state, uint32_t iteration) +{ + sender_message_key_node *cur_node = 0; + assert(state); + + DL_FOREACH(state->message_keys_head, cur_node) { + if(sender_message_key_get_iteration(cur_node->key) == iteration) { + return 1; + } + } + + return 0; +} + +int sender_key_state_add_sender_message_key(sender_key_state *state, sender_message_key *message_key) +{ + int result = 0; + sender_message_key_node *node = 0; + int count; + assert(state); + assert(message_key); + + node = malloc(sizeof(sender_message_key_node)); + if(!node) { + result = AX_ERR_NOMEM; + goto complete; + } + + AXOLOTL_REF(message_key); + node->key = message_key; + DL_APPEND(state->message_keys_head, node); + + DL_COUNT(state->message_keys_head, node, count); + while(count > MAX_MESSAGE_KEYS) { + node = state->message_keys_head; + DL_DELETE(state->message_keys_head, node); + if(node->key) { + AXOLOTL_UNREF(node->key); + } + free(node); + --count; + } + +complete: + return result; +} + +sender_message_key *sender_key_state_remove_sender_message_key(sender_key_state *state, uint32_t iteration) +{ + sender_message_key *result = 0; + sender_message_key_node *cur_node = 0; + sender_message_key_node *tmp_node = 0; + assert(state); + + DL_FOREACH_SAFE(state->message_keys_head, cur_node, tmp_node) { + if(sender_message_key_get_iteration(cur_node->key) == iteration) { + DL_DELETE(state->message_keys_head, cur_node); + result = cur_node->key; + free(cur_node); + break; + } + } + + return result; +} + +void sender_key_state_destroy(axolotl_type_base *type) +{ + sender_key_state *state = (sender_key_state *)type; + sender_message_key_node *cur_node; + sender_message_key_node *tmp_node; + + AXOLOTL_UNREF(state->chain_key); + AXOLOTL_UNREF(state->signature_public_key); + AXOLOTL_UNREF(state->signature_private_key); + + DL_FOREACH_SAFE(state->message_keys_head, cur_node, tmp_node) { + DL_DELETE(state->message_keys_head, cur_node); + if(cur_node->key) { + AXOLOTL_UNREF(cur_node->key); + } + free(cur_node); + } + state->message_keys_head = 0; + + free(state); +} diff --git a/libs/libaxolotl/src/sender_key_state.h b/libs/libaxolotl/src/sender_key_state.h new file mode 100644 index 0000000000..117afa43be --- /dev/null +++ b/libs/libaxolotl/src/sender_key_state.h @@ -0,0 +1,34 @@ +#ifndef SENDER_KEY_STATE_H +#define SENDER_KEY_STATE_H + +#include <stdint.h> +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int sender_key_state_create(sender_key_state **state, + uint32_t id, sender_chain_key *chain_key, + ec_public_key *signature_public_key, ec_private_key *signature_private_key, + axolotl_context *global_context); +int sender_key_state_serialize(axolotl_buffer **buffer, sender_key_state *state); +int sender_key_state_deserialize(sender_key_state **state, const uint8_t *data, size_t len, axolotl_context *global_context); +int sender_key_state_copy(sender_key_state **state, sender_key_state *other_state, axolotl_context *global_context); + +uint32_t sender_key_state_get_key_id(sender_key_state *state); +sender_chain_key *sender_key_state_get_chain_key(sender_key_state *state); +void sender_key_state_set_chain_key(sender_key_state *state, sender_chain_key *chain_key); +ec_public_key *sender_key_state_get_signing_key_public(sender_key_state *state); +ec_private_key *sender_key_state_get_signing_key_private(sender_key_state *state); +int sender_key_state_has_sender_message_key(sender_key_state *state, uint32_t iteration); +int sender_key_state_add_sender_message_key(sender_key_state *state, sender_message_key *message_key); +sender_message_key *sender_key_state_remove_sender_message_key(sender_key_state *state, uint32_t iteration); + +void sender_key_state_destroy(axolotl_type_base *type); + +#ifdef __cplusplus +} +#endif + +#endif /* SENDER_KEY_STATE_H */ diff --git a/libs/libaxolotl/src/session_builder.c b/libs/libaxolotl/src/session_builder.c new file mode 100644 index 0000000000..b962c21b5f --- /dev/null +++ b/libs/libaxolotl/src/session_builder.c @@ -0,0 +1,867 @@ +#include "session_builder.h" + +#include <assert.h> +#include <string.h> +#include "axolotl_internal.h" +#include "session_pre_key.h" +#include "session_record.h" +#include "session_state.h" +#include "ratchet.h" +#include "protocol.h" +#include "key_helper.h" + +#define MIN(a,b) (((a)<(b))?(a):(b)) + +struct session_builder +{ + axolotl_store_context *store; + const axolotl_address *remote_address; + axolotl_context *global_context; +}; + +static int session_builder_process_pre_key_whisper_message_v2(session_builder *builder, + session_record *record, pre_key_whisper_message *message, uint32_t *unsigned_pre_key_id); +static int session_builder_process_pre_key_whisper_message_v3(session_builder *builder, + session_record *record, pre_key_whisper_message *message, uint32_t *unsigned_pre_key_id); +static int session_builder_process_initiate(session_builder *builder, + key_exchange_message *message, key_exchange_message **response_message); +static int session_builder_process_response(session_builder *builder, + key_exchange_message *message); + +int session_builder_create(session_builder **builder, + axolotl_store_context *store, const axolotl_address *remote_address, + axolotl_context *global_context) +{ + session_builder *result = 0; + + assert(store); + assert(global_context); + + result = malloc(sizeof(session_builder)); + if(!result) { + return AX_ERR_NOMEM; + } + memset(result, 0, sizeof(session_builder)); + + result->store = store; + result->remote_address = remote_address; + result->global_context = global_context; + + *builder = result; + return 0; +} + +int session_builder_process_pre_key_whisper_message(session_builder *builder, + session_record *record, pre_key_whisper_message *message, uint32_t *unsigned_pre_key_id) +{ + int result = 0; + int has_unsigned_pre_key_id_result = 0; + uint32_t unsigned_pre_key_id_result = 0; + int message_version = pre_key_whisper_message_get_message_version(message); + ec_public_key *their_identity_key = pre_key_whisper_message_get_identity_key(message); + + result = axolotl_identity_is_trusted_identity(builder->store, + builder->remote_address->name, builder->remote_address->name_len, + their_identity_key); + if(result < 0) { + goto complete; + } + if(result == 0) { + result = AX_ERR_UNTRUSTED_IDENTITY; + goto complete; + } + + if(message_version == 2) { + result = session_builder_process_pre_key_whisper_message_v2(builder, record, message, &unsigned_pre_key_id_result); + } + else if(message_version == 3) { + result = session_builder_process_pre_key_whisper_message_v3(builder, record, message, &unsigned_pre_key_id_result); + } + else { + axolotl_log(builder->global_context, AX_LOG_WARNING, "Unknown version: %d", message_version); + result = AX_ERR_INVALID_VERSION; + } + if(result < 0) { + goto complete; + } + has_unsigned_pre_key_id_result = result; + + result = axolotl_identity_save_identity(builder->store, + builder->remote_address->name, builder->remote_address->name_len, + their_identity_key); + if(result < 0) { + goto complete; + } + + result = has_unsigned_pre_key_id_result; + +complete: + if(result >= 0) { + *unsigned_pre_key_id = unsigned_pre_key_id_result; + } + return result; +} + +static int session_builder_process_pre_key_whisper_message_v2(session_builder *builder, + session_record *record, pre_key_whisper_message *message, uint32_t *unsigned_pre_key_id) +{ + int result = 0; + uint32_t unsigned_pre_key_id_result = 0; + session_pre_key *our_pre_key = 0; + ratchet_identity_key_pair *our_identity_key = 0; + bob_axolotl_parameters *parameters = 0; + uint32_t pre_key_id = 0; + int contains_session = 0; + session_state *state = 0; + int contains_key = 0; + uint32_t local_registration_id = 0; + + if(!pre_key_whisper_message_has_pre_key_id(message)) { + axolotl_log(builder->global_context, AX_LOG_WARNING, "V2 message requires one time pre key ID!"); + result = AX_ERR_INVALID_KEY_ID; + goto complete; + } + + pre_key_id = pre_key_whisper_message_get_pre_key_id(message); + + contains_key = axolotl_pre_key_contains_key(builder->store, pre_key_id); + if(contains_key < 0) { + result = contains_key; + goto complete; + } + + contains_session = axolotl_session_contains_session(builder->store, builder->remote_address); + if(contains_session < 0) { + result = contains_session; + goto complete; + } + + if(!contains_key && contains_session) { + axolotl_log(builder->global_context, AX_LOG_INFO, "We've already processed the prekey part of this V2 session, letting bundled message fall through..."); + result = 0; + goto complete; + } + + result = axolotl_pre_key_load_key(builder->store, &our_pre_key, pre_key_id); + if(result < 0) { + goto complete; + } + + result = axolotl_identity_get_key_pair(builder->store, &our_identity_key); + if(result < 0) { + goto complete; + } + + result = bob_axolotl_parameters_create( + ¶meters, + our_identity_key, + session_pre_key_get_key_pair(our_pre_key), + 0, + session_pre_key_get_key_pair(our_pre_key), + pre_key_whisper_message_get_identity_key(message), + pre_key_whisper_message_get_base_key(message)); + if(result < 0) { + goto complete; + } + + if(!session_record_is_fresh(record)) { + session_record_archive_current_state(record); + } + + state = session_record_get_state(record); + + result = ratcheting_session_bob_initialize( + state, + pre_key_whisper_message_get_message_version(message), + parameters, builder->global_context); + if(result < 0) { + goto complete; + } + + result = axolotl_identity_get_local_registration_id(builder->store, &local_registration_id); + if(result < 0) { + goto complete; + } + + session_state_set_local_registration_id(state, local_registration_id); + session_state_set_remote_registration_id(state, + pre_key_whisper_message_get_registration_id(message)); + session_state_set_alice_base_key(state, + pre_key_whisper_message_get_base_key(message));; + + if(pre_key_whisper_message_get_pre_key_id(message) != PRE_KEY_MEDIUM_MAX_VALUE) { + unsigned_pre_key_id_result = pre_key_whisper_message_get_pre_key_id(message); + result = 1; + } + else { + result = 0; + } + +complete: + AXOLOTL_UNREF(parameters); + AXOLOTL_UNREF(our_identity_key); + AXOLOTL_UNREF(our_pre_key); + if(result >= 0) { + *unsigned_pre_key_id = unsigned_pre_key_id_result; + } + return result; +} + +static int session_builder_process_pre_key_whisper_message_v3(session_builder *builder, + session_record *record, pre_key_whisper_message *message, uint32_t *unsigned_pre_key_id) +{ + int result = 0; + uint32_t unsigned_pre_key_id_result = 0; + session_signed_pre_key *our_signed_pre_key = 0; + ratchet_identity_key_pair *our_identity_key = 0; + bob_axolotl_parameters *parameters = 0; + session_pre_key *session_our_one_time_pre_key = 0; + ec_key_pair *our_one_time_pre_key = 0; + session_state *state = 0; + uint32_t local_registration_id = 0; + + int has_session_state = session_record_has_session_state(record, + pre_key_whisper_message_get_message_version(message), + pre_key_whisper_message_get_base_key(message)); + if(has_session_state) { + axolotl_log(builder->global_context, AX_LOG_INFO, "We've already setup a session for this V3 message, letting bundled message fall through..."); + result = 0; + goto complete; + } + + result = axolotl_signed_pre_key_load_key(builder->store, + &our_signed_pre_key, + pre_key_whisper_message_get_signed_pre_key_id(message)); + if(result < 0) { + goto complete; + } + + result = axolotl_identity_get_key_pair(builder->store, &our_identity_key); + if(result < 0) { + goto complete; + } + + if(pre_key_whisper_message_has_pre_key_id(message)) { + result = axolotl_pre_key_load_key(builder->store, + &session_our_one_time_pre_key, + pre_key_whisper_message_get_pre_key_id(message)); + if(result < 0) { + goto complete; + } + our_one_time_pre_key = session_pre_key_get_key_pair(session_our_one_time_pre_key); + } + + result = bob_axolotl_parameters_create( + ¶meters, + our_identity_key, + session_signed_pre_key_get_key_pair(our_signed_pre_key), + our_one_time_pre_key, + session_signed_pre_key_get_key_pair(our_signed_pre_key), + pre_key_whisper_message_get_identity_key(message), + pre_key_whisper_message_get_base_key(message)); + if(result < 0) { + goto complete; + } + + if(!session_record_is_fresh(record)) { + session_record_archive_current_state(record); + } + + state = session_record_get_state(record); + + result = ratcheting_session_bob_initialize( + state, + pre_key_whisper_message_get_message_version(message), + parameters, builder->global_context); + if(result < 0) { + goto complete; + } + + result = axolotl_identity_get_local_registration_id(builder->store, &local_registration_id); + if(result < 0) { + goto complete; + } + + session_state_set_local_registration_id(state, local_registration_id); + session_state_set_remote_registration_id(state, + pre_key_whisper_message_get_registration_id(message)); + session_state_set_alice_base_key(state, + pre_key_whisper_message_get_base_key(message));; + + if(pre_key_whisper_message_has_pre_key_id(message) && + pre_key_whisper_message_get_pre_key_id(message) != PRE_KEY_MEDIUM_MAX_VALUE) { + unsigned_pre_key_id_result = pre_key_whisper_message_get_pre_key_id(message); + result = 1; + } + else { + result = 0; + } + +complete: + AXOLOTL_UNREF(parameters); + AXOLOTL_UNREF(our_identity_key); + AXOLOTL_UNREF(our_signed_pre_key); + AXOLOTL_UNREF(session_our_one_time_pre_key); + if(result >= 0) { + *unsigned_pre_key_id = unsigned_pre_key_id_result; + } + return result; +} + +int session_builder_process_pre_key_bundle(session_builder *builder, session_pre_key_bundle *bundle) +{ + int result = 0; + session_record *record = 0; + ec_key_pair *our_base_key = 0; + ratchet_identity_key_pair *our_identity_key = 0; + alice_axolotl_parameters *parameters = 0; + ec_public_key *signed_pre_key = 0; + ec_public_key *pre_key = 0; + int supports_v3 = 0; + ec_public_key *their_identity_key = 0; + ec_public_key *their_signed_pre_key = 0; + ec_public_key *their_one_time_pre_key = 0; + int has_their_one_time_pre_key_id = 0; + uint32_t their_one_time_pre_key_id = 0; + session_state *state = 0; + uint32_t local_registration_id = 0; + + assert(builder); + assert(builder->store); + assert(bundle); + axolotl_lock(builder->global_context); + + result = axolotl_identity_is_trusted_identity(builder->store, + builder->remote_address->name, builder->remote_address->name_len, + session_pre_key_bundle_get_identity_key(bundle)); + if(result < 0) { + goto complete; + } + if(result == 0) { + result = AX_ERR_UNTRUSTED_IDENTITY; + goto complete; + } + + signed_pre_key = session_pre_key_bundle_get_signed_pre_key(bundle); + pre_key = session_pre_key_bundle_get_pre_key(bundle); + + if(signed_pre_key) { + ec_public_key *identity_key = session_pre_key_bundle_get_identity_key(bundle); + axolotl_buffer *signature = session_pre_key_bundle_get_signed_pre_key_signature(bundle); + + axolotl_buffer *serialized_signed_pre_key = 0; + result = ec_public_key_serialize(&serialized_signed_pre_key, signed_pre_key); + if(result < 0) { + goto complete; + } + + result = curve_verify_signature(identity_key, + axolotl_buffer_data(serialized_signed_pre_key), + axolotl_buffer_len(serialized_signed_pre_key), + axolotl_buffer_data(signature), + axolotl_buffer_len(signature)); + + axolotl_buffer_free(serialized_signed_pre_key); + + if(result == 0) { + axolotl_log(builder->global_context, AX_LOG_WARNING, "invalid signature on device key!"); + result = AX_ERR_INVALID_KEY; + } + if(result < 0) { + goto complete; + } + } + + if(!signed_pre_key && !pre_key) { + result = AX_ERR_INVALID_KEY; + axolotl_log(builder->global_context, AX_LOG_WARNING, "both signed and unsigned pre keys are absent!"); + goto complete; + } + + supports_v3 = (signed_pre_key != 0); + + result = axolotl_session_load_session(builder->store, &record, builder->remote_address); + if(result < 0) { + goto complete; + } + + result = curve_generate_key_pair(builder->global_context, &our_base_key); + if(result < 0) { + goto complete; + } + + their_identity_key = session_pre_key_bundle_get_identity_key(bundle); + their_signed_pre_key = supports_v3 ? signed_pre_key : pre_key; + + their_one_time_pre_key = pre_key; + + if(their_one_time_pre_key) { + has_their_one_time_pre_key_id = 1; + their_one_time_pre_key_id = session_pre_key_bundle_get_pre_key_id(bundle); + } + + result = axolotl_identity_get_key_pair(builder->store, &our_identity_key); + if(result < 0) { + goto complete; + } + + result = alice_axolotl_parameters_create(¶meters, + our_identity_key, + our_base_key, + their_identity_key, + their_signed_pre_key, + supports_v3 ? their_one_time_pre_key : 0, + their_signed_pre_key); + if(result < 0) { + goto complete; + } + + if(!session_record_is_fresh(record)) { + session_record_archive_current_state(record); + } + + state = session_record_get_state(record); + + result = ratcheting_session_alice_initialize( + state, + supports_v3 ? 3 : 2, parameters, + builder->global_context); + if(result < 0) { + goto complete; + } + + session_state_set_unacknowledged_pre_key_message(state, + has_their_one_time_pre_key_id ? &their_one_time_pre_key_id : 0, + session_pre_key_bundle_get_signed_pre_key_id(bundle), + ec_key_pair_get_public(our_base_key)); + + result = axolotl_identity_get_local_registration_id(builder->store, &local_registration_id); + if(result < 0) { + goto complete; + } + + session_state_set_local_registration_id(state, local_registration_id); + session_state_set_remote_registration_id(state, + session_pre_key_bundle_get_registration_id(bundle)); + session_state_set_alice_base_key(state, ec_key_pair_get_public(our_base_key)); + + axolotl_session_store_session(builder->store, builder->remote_address, record); + axolotl_identity_save_identity(builder->store, + builder->remote_address->name, builder->remote_address->name_len, + their_identity_key); + +complete: + AXOLOTL_UNREF(record); + AXOLOTL_UNREF(our_base_key); + AXOLOTL_UNREF(our_identity_key); + AXOLOTL_UNREF(parameters); + axolotl_unlock(builder->global_context); + return result; +} + +int session_builder_process_key_exchange_message(session_builder *builder, key_exchange_message *message, key_exchange_message **response_message) +{ + int result = 0; + key_exchange_message *result_response_message = 0; + + assert(builder); + assert(builder->store); + axolotl_lock(builder->global_context); + + result = axolotl_identity_is_trusted_identity(builder->store, + builder->remote_address->name, builder->remote_address->name_len, + key_exchange_message_get_identity_key(message)); + if(result < 0) { + goto complete; + } + if(result == 0) { + result = AX_ERR_UNTRUSTED_IDENTITY; + goto complete; + } + result = 0; + + if(key_exchange_message_is_initiate(message)) { + result = session_builder_process_initiate(builder, message, &result_response_message); + } + else { + result = session_builder_process_response(builder, message); + } + +complete: + if(result >= 0) { + *response_message = result_response_message; + } + else { + AXOLOTL_UNREF(result_response_message); + } + axolotl_unlock(builder->global_context); + return result; +} + +static int session_builder_process_initiate(session_builder *builder, key_exchange_message *message, key_exchange_message **response_message) +{ + int result = 0; + key_exchange_message *result_response_message = 0; + + uint32_t flags = KEY_EXCHANGE_RESPONSE_FLAG; + session_record *record = 0; + session_state *state = 0; + ratchet_identity_key_pair *identity_key_pair = 0; + ec_key_pair *our_base_key = 0; + ec_key_pair *our_ratchet_key = 0; + symmetric_axolotl_parameters *parameters = 0; + ratchet_identity_key_pair *parameters_identity_key = 0; + ec_key_pair *parameters_base_key = 0; + axolotl_buffer *parameters_public_base_key_serialized = 0; + ec_key_pair *parameters_ratchet_key = 0; + axolotl_buffer *base_key_signature = 0; + + result = axolotl_session_load_session(builder->store, &record, builder->remote_address); + if(result < 0) { + goto complete; + } + + if(key_exchange_message_get_version(message) >= 3) { + ec_public_key *message_identity_key = key_exchange_message_get_identity_key(message); + ec_public_key *message_base_key = key_exchange_message_get_base_key(message); + axolotl_buffer *message_base_key_serialized = 0; + uint8_t *message_base_key_signature = 0; + + ec_public_key_serialize(&message_base_key_serialized, message_base_key); + + message_base_key_signature = key_exchange_message_get_base_key_signature(message); + + result = curve_verify_signature(message_identity_key, + axolotl_buffer_data(message_base_key_serialized), + axolotl_buffer_len(message_base_key_serialized), + message_base_key_signature, CURVE_SIGNATURE_LEN); + axolotl_buffer_free(message_base_key_serialized); + if(result < 0) { + goto complete; + } + if(result != 1) { + axolotl_log(builder->global_context, AX_LOG_WARNING, "Bad signature!"); + result = AX_ERR_INVALID_KEY; + goto complete; + } + } + + state = session_record_get_state(record); + if(!session_state_has_pending_key_exchange(state)) { + result = axolotl_identity_get_key_pair(builder->store, &identity_key_pair); + if(result < 0) { + goto complete; + } + + result = curve_generate_key_pair(builder->global_context, &our_base_key); + if(result < 0) { + goto complete; + } + + result = curve_generate_key_pair(builder->global_context, &our_ratchet_key); + if(result < 0) { + goto complete; + } + + result = symmetric_axolotl_parameters_create( + ¶meters, + identity_key_pair, + our_base_key, + our_ratchet_key, + key_exchange_message_get_base_key(message), + key_exchange_message_get_ratchet_key(message), + key_exchange_message_get_identity_key(message)); + if(result < 0) { + goto complete; + } + } + else { + result = symmetric_axolotl_parameters_create( + ¶meters, + session_state_get_pending_key_exchange_identity_key(state), + session_state_get_pending_key_exchange_base_key(state), + session_state_get_pending_key_exchange_ratchet_key(state), + key_exchange_message_get_base_key(message), + key_exchange_message_get_ratchet_key(message), + key_exchange_message_get_identity_key(message)); + if(result < 0) { + goto complete; + } + + flags |= KEY_EXCHANGE_SIMULTAENOUS_INITIATE_FLAG; + } + + if(!session_record_is_fresh(record)) { + session_record_archive_current_state(record); + } + + state = session_record_get_state(record); + result = ratcheting_session_symmetric_initialize(state, + MIN(key_exchange_message_get_max_version(message), (uint32_t)CIPHERTEXT_CURRENT_VERSION), + parameters, builder->global_context); + if(result < 0) { + goto complete; + } + + result = axolotl_session_store_session(builder->store, builder->remote_address, record); + if(result < 0) { + goto complete; + } + + result = axolotl_identity_save_identity(builder->store, + builder->remote_address->name, builder->remote_address->name_len, + key_exchange_message_get_identity_key(message)); + if(result < 0) { + goto complete; + } + + parameters_identity_key = symmetric_axolotl_parameters_get_our_identity_key(parameters); + parameters_base_key = symmetric_axolotl_parameters_get_our_base_key(parameters); + parameters_ratchet_key = symmetric_axolotl_parameters_get_our_ratchet_key(parameters); + + result = ec_public_key_serialize(¶meters_public_base_key_serialized, + ec_key_pair_get_public(parameters_base_key)); + if(result < 0) { + goto complete; + } + + result = curve_calculate_signature(builder->global_context, + &base_key_signature, + ratchet_identity_key_pair_get_private(parameters_identity_key), + axolotl_buffer_data(parameters_public_base_key_serialized), + axolotl_buffer_len(parameters_public_base_key_serialized)); + if(result < 0) { + goto complete; + } + + result = key_exchange_message_create(&result_response_message, + session_state_get_session_version(state), + key_exchange_message_get_sequence(message), + flags, + ec_key_pair_get_public(parameters_base_key), + axolotl_buffer_data(base_key_signature), + ec_key_pair_get_public(parameters_ratchet_key), + ratchet_identity_key_pair_get_public(parameters_identity_key)); + +complete: + axolotl_buffer_free(parameters_public_base_key_serialized); + axolotl_buffer_free(base_key_signature); + AXOLOTL_UNREF(our_base_key); + AXOLOTL_UNREF(our_ratchet_key); + AXOLOTL_UNREF(identity_key_pair); + AXOLOTL_UNREF(parameters); + AXOLOTL_UNREF(record); + if(result >= 0) { + *response_message = result_response_message; + } + else { + AXOLOTL_UNREF(result_response_message); + } + return result; +} + +static int session_builder_process_response(session_builder *builder, key_exchange_message *message) +{ + int result = 0; + session_record *record = 0; + session_state *state = 0; + int has_pending_key_exchange = 0; + int is_simultaneous_initiate_response = 0; + symmetric_axolotl_parameters *parameters = 0; + + result = axolotl_session_load_session(builder->store, &record, builder->remote_address); + if(result < 0) { + goto complete; + } + + state = session_record_get_state(record); + has_pending_key_exchange = session_state_has_pending_key_exchange(state); + is_simultaneous_initiate_response = key_exchange_message_is_response_for_simultaneous_initiate(message); + + if(!has_pending_key_exchange || + session_state_get_pending_key_exchange_sequence(state) != key_exchange_message_get_sequence(message)) { + axolotl_log(builder->global_context, AX_LOG_INFO, "No matching sequence for response. Is simultaneous initiate response: %d", is_simultaneous_initiate_response); + if(!is_simultaneous_initiate_response) { + result = AX_ERR_STALE_KEY_EXCHANGE; + goto complete; + } + else { + result = AX_SUCCESS; + goto complete; + } + } + + result = symmetric_axolotl_parameters_create( + ¶meters, + session_state_get_pending_key_exchange_identity_key(state), + session_state_get_pending_key_exchange_base_key(state), + session_state_get_pending_key_exchange_ratchet_key(state), + key_exchange_message_get_base_key(message), + key_exchange_message_get_ratchet_key(message), + key_exchange_message_get_identity_key(message)); + if(result < 0) { + goto complete; + } + + if(!session_record_is_fresh(record)) { + session_record_archive_current_state(record); + } + + state = session_record_get_state(record); + result = ratcheting_session_symmetric_initialize(state, + MIN(key_exchange_message_get_max_version(message), (uint32_t)CIPHERTEXT_CURRENT_VERSION), + parameters, builder->global_context); + if(result < 0) { + goto complete; + } + + result = axolotl_session_store_session(builder->store, builder->remote_address, record); + if(result < 0) { + goto complete; + } + + if(session_state_get_session_version(state) >= 3) { + ec_public_key *message_identity_key = key_exchange_message_get_identity_key(message); + ec_public_key *message_base_key = key_exchange_message_get_base_key(message); + axolotl_buffer *message_base_key_serialized = 0; + uint8_t *message_base_key_signature = 0; + + ec_public_key_serialize(&message_base_key_serialized, message_base_key); + + message_base_key_signature = key_exchange_message_get_base_key_signature(message); + + result = curve_verify_signature(message_identity_key, + axolotl_buffer_data(message_base_key_serialized), + axolotl_buffer_len(message_base_key_serialized), + message_base_key_signature, CURVE_SIGNATURE_LEN); + axolotl_buffer_free(message_base_key_serialized); + if(result < 0) { + goto complete; + } + if(result != 1) { + axolotl_log(builder->global_context, AX_LOG_WARNING, "Base key signature doesn't match!"); + result = AX_ERR_INVALID_KEY; + goto complete; + } + } + + result = axolotl_session_store_session(builder->store, builder->remote_address, record); + if(result < 0) { + goto complete; + } + + result = axolotl_identity_save_identity(builder->store, + builder->remote_address->name, builder->remote_address->name_len, + key_exchange_message_get_identity_key(message)); + if(result < 0) { + goto complete; + } + +complete: + AXOLOTL_UNREF(parameters); + AXOLOTL_UNREF(record); + return result; +} + +int session_builder_process(session_builder *builder, key_exchange_message **message) +{ + int result = 0; + key_exchange_message *result_message = 0; + int random_value = 0; + uint32_t sequence = 0; + uint32_t flags = KEY_EXCHANGE_INITIATE_FLAG; + ec_key_pair *base_key = 0; + ec_key_pair *ratchet_key = 0; + ratchet_identity_key_pair *identity_key = 0; + axolotl_buffer *base_key_public_serialized = 0; + axolotl_buffer *base_key_signature = 0; + session_record *record = 0; + session_state *state = 0; + + assert(builder); + assert(builder->store); + axolotl_lock(builder->global_context); + + result = axolotl_key_helper_get_random_sequence(&random_value, 65534, builder->global_context); + if(result < 0) { + goto complete; + } + sequence = ((uint32_t)random_value) + 1; + + result = curve_generate_key_pair(builder->global_context, &base_key); + if(result < 0) { + goto complete; + } + + result = curve_generate_key_pair(builder->global_context, &ratchet_key); + if(result < 0) { + goto complete; + } + + result = axolotl_identity_get_key_pair(builder->store, &identity_key); + if(result < 0) { + goto complete; + } + + result = ec_public_key_serialize(&base_key_public_serialized, ec_key_pair_get_public(base_key)); + if(result < 0) { + goto complete; + } + + result = curve_calculate_signature(builder->global_context, &base_key_signature, + ratchet_identity_key_pair_get_private(identity_key), + axolotl_buffer_data(base_key_public_serialized), + axolotl_buffer_len(base_key_public_serialized)); + if(result < 0) { + goto complete; + } + + if(axolotl_buffer_len(base_key_signature) != CURVE_SIGNATURE_LEN) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + result = axolotl_session_load_session(builder->store, &record, builder->remote_address); + if(result < 0) { + goto complete; + } + + state = session_record_get_state(record); + session_state_set_pending_key_exchange(state, sequence, base_key, ratchet_key, identity_key); + + result = axolotl_session_store_session(builder->store, builder->remote_address, record); + if(result < 0) { + goto complete; + } + + result = key_exchange_message_create(&result_message, + 2, sequence, flags, + ec_key_pair_get_public(base_key), + axolotl_buffer_data(base_key_signature), + ec_key_pair_get_public(ratchet_key), + ratchet_identity_key_pair_get_public(identity_key)); + +complete: + AXOLOTL_UNREF(record); + axolotl_buffer_free(base_key_signature); + axolotl_buffer_free(base_key_public_serialized); + AXOLOTL_UNREF(identity_key); + AXOLOTL_UNREF(ratchet_key); + AXOLOTL_UNREF(base_key); + if(result >= 0) { + *message = result_message; + } + else { + result = AX_ERR_INVALID_KEY; + AXOLOTL_UNREF(result_message); + } + axolotl_unlock(builder->global_context); + return result; +} + +void session_builder_free(session_builder *builder) +{ + if(builder) { + free(builder); + } +} diff --git a/libs/libaxolotl/src/session_builder.h b/libs/libaxolotl/src/session_builder.h new file mode 100644 index 0000000000..cfd67a1824 --- /dev/null +++ b/libs/libaxolotl/src/session_builder.h @@ -0,0 +1,101 @@ +#ifndef SESSION_BUILDER_H +#define SESSION_BUILDER_H + +#include <stdint.h> +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Session builder is responsible for setting up encrypted sessions. + * Once a session has been established, session_cipher + * can be used to encrypt/decrypt messages in that session. + * + * Sessions are built from one of three different possible vectors: + * - A session_pre_key_bundle retrieved from a server + * - A pre_key_whisper_message received from a client + * - A key_exchange_message sent to or received from a client + * + * Sessions are constructed per AXOLOTL address + * (recipient name + device ID tuple). Remote logical users are identified by + * their recipient name, and each logical recipient can have multiple + * physical devices. + */ + +/** + * Constructs a session builder. + * + * The store and global contexts must remain valid for the lifetime of the + * session builder. + * + * When finished, free the returned instance by calling session_builder_free(). + * + * @param builder set to a freshly allocated session builder instance + * @param store the axolotl_store_context to store all state information in + * @param remote_address the address of the remote user to build a session with + * @param global_context the global library context + * @return 0 on success, or negative on failure + */ +int session_builder_create(session_builder **builder, + axolotl_store_context *store, const axolotl_address *remote_address, + axolotl_context *global_context); + +/** + * Build a new session from a received pre_key_whisper_message. + * + * After a session is constructed in this way, the embedded whisper_message + * can be decrypted. + * + * @param message The received pre_key_whisper_message. + * @param unsigned_pre_key_id set to the unsigned pre key ID, if available. + * Return value indicates whether or not this value is available. + * @retval 0 Success, no unsigned pre key value available + * @retval 1 Success, an unsigned pre key is available + * @retval AX_ERR_INVALID_KEY_ID when there is no local pre_key_record that + * corresponds to the PreKey ID in the message. + * @retval AX_ERR_INVALID_KEY when the message is formatted incorrectly. + * @retval AX_ERR_UNTRUSTED_IDENTITY when the identity key of the sender is untrusted. + */ +int session_builder_process_pre_key_whisper_message(session_builder *builder, + session_record *record, pre_key_whisper_message *message, uint32_t *unsigned_pre_key_id); + +/** + * Build a new session from a session_pre_key_bundle retrieved from a server. + * + * @param bundle A pre key bundle for the destination recipient, retrieved from a server. + * @retval AX_SUCCESS Success + * @retval AX_ERR_INVALID_KEY when the session_pre_key_bundle is badly formatted. + * @retval AX_ERR_UNTRUSTED_IDENTITY when the sender's identity key is not trusted. + */ +int session_builder_process_pre_key_bundle(session_builder *builder, session_pre_key_bundle *bundle); + +/** + * Build a new session from a key_exchange_message received from a remote client. + * + * @param message The received key_exchange_message. + * @param response_message Set to the key_exchange_message to respond with, + * or set to 0 if no response is necessary. + * @retval AX_SUCCESS Success + * @retval AX_ERR_INVALID_KEY if the received key_exchange_message is badly formatted. + * @retval AX_ERR_UNTRUSTED_IDENTITY + * @retval AX_ERR_STALE_KEY_EXCHANGE + */ +int session_builder_process_key_exchange_message(session_builder *builder, key_exchange_message *message, key_exchange_message **response_message); + +/** + * Initiate a new session by sending an initial key_exchange_message to the recipient. + * + * @param message Set to the key_exchange_message to deliver. + * @return AX_SUCCESS on success, negative on error + */ +int session_builder_process(session_builder *builder, key_exchange_message **message); + +void session_builder_free(session_builder *builder); + +#ifdef __cplusplus +} +#endif + +#endif /* SESSION_BUILDER_H */ diff --git a/libs/libaxolotl/src/session_cipher.c b/libs/libaxolotl/src/session_cipher.c new file mode 100644 index 0000000000..c2065ef5dc --- /dev/null +++ b/libs/libaxolotl/src/session_cipher.c @@ -0,0 +1,884 @@ +#include "session_cipher.h" + +#include <assert.h> +#include <string.h> +#include "axolotl_internal.h" +#include "session_builder.h" +#include "session_record.h" +#include "session_state.h" +#include "ratchet.h" +#include "protocol.h" + +struct session_cipher +{ + axolotl_store_context *store; + const axolotl_address *remote_address; + session_builder *builder; + axolotl_context *global_context; + int (*decrypt_callback)(session_cipher *cipher, axolotl_buffer *plaintext, void *decrypt_context); + int inside_callback; + void *user_data; +}; + +static int session_cipher_decrypt_from_record_and_whisper_message(session_cipher *cipher, + session_record *record, whisper_message *ciphertext, axolotl_buffer **plaintext); +static int session_cipher_decrypt_from_state_and_whisper_message(session_cipher *cipher, + session_state *state, whisper_message *ciphertext, axolotl_buffer **plaintext); + +static int session_cipher_get_or_create_chain_key(session_cipher *cipher, + ratchet_chain_key **chain_key, + session_state *state, ec_public_key *their_ephemeral); +static int session_cipher_get_or_create_message_keys(ratchet_message_keys *message_keys, + session_state *state, ec_public_key *their_ephemeral, + ratchet_chain_key *chain_key, uint32_t counter, + axolotl_context *global_context); + +static int session_cipher_get_ciphertext(session_cipher *cipher, + axolotl_buffer **ciphertext, + uint32_t version, ratchet_message_keys *message_keys, + const uint8_t *plaintext, size_t plaintext_len); +static int session_cipher_get_plaintext(session_cipher *cipher, + axolotl_buffer **plaintext, + uint32_t version, ratchet_message_keys *message_keys, + const uint8_t *ciphertext, size_t ciphertext_len); + +static int session_cipher_decrypt_callback(session_cipher *cipher, axolotl_buffer *plaintext, void *decrypt_context); + +int session_cipher_create(session_cipher **cipher, + axolotl_store_context *store, const axolotl_address *remote_address, + axolotl_context *global_context) +{ + int result = 0; + session_builder *builder = 0; + session_cipher *result_cipher; + + assert(store); + assert(global_context); + + result = session_builder_create(&builder, store, remote_address, global_context); + if(result < 0) { + return result; + } + + result_cipher = malloc(sizeof(session_cipher)); + if(!result_cipher) { + return AX_ERR_NOMEM; + } + memset(result_cipher, 0, sizeof(session_cipher)); + + result_cipher->store = store; + result_cipher->remote_address = remote_address; + result_cipher->builder = builder; + result_cipher->global_context = global_context; + + *cipher = result_cipher; + return 0; +} + +void session_cipher_set_user_data(session_cipher *cipher, void *user_data) +{ + assert(cipher); + cipher->user_data = user_data; +} + +void *session_cipher_get_user_data(session_cipher *cipher) +{ + assert(cipher); + return cipher->user_data; +} + +void session_cipher_set_decryption_callback(session_cipher *cipher, + int (*callback)(session_cipher *cipher, axolotl_buffer *plaintext, void *decrypt_context)) +{ + assert(cipher); + cipher->decrypt_callback = callback; +} + +int session_cipher_encrypt(session_cipher *cipher, + const uint8_t *padded_message, size_t padded_message_len, + ciphertext_message **encrypted_message) +{ + int result = 0; + session_record *record = 0; + session_state *state = 0; + ratchet_chain_key *chain_key = 0; + ratchet_chain_key *next_chain_key = 0; + ratchet_message_keys message_keys; + ec_public_key *sender_ephemeral = 0; + uint32_t previous_counter = 0; + uint32_t session_version = 0; + axolotl_buffer *ciphertext = 0; + uint32_t chain_key_index = 0; + ec_public_key *local_identity_key = 0; + ec_public_key *remote_identity_key = 0; + whisper_message *message = 0; + pre_key_whisper_message *pre_key_message = 0; + uint8_t *ciphertext_data = 0; + size_t ciphertext_len = 0; + + assert(cipher); + axolotl_lock(cipher->global_context); + + if(cipher->inside_callback == 1) { + result = AX_ERR_INVAL; + goto complete; + } + + result = axolotl_session_load_session(cipher->store, &record, cipher->remote_address); + if(result < 0) { + goto complete; + } + + state = session_record_get_state(record); + if(!state) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + chain_key = session_state_get_sender_chain_key(state); + if(!chain_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + result = ratchet_chain_key_get_message_keys(chain_key, &message_keys); + if(result < 0) { + goto complete; + } + + sender_ephemeral = session_state_get_sender_ratchet_key(state); + if(!sender_ephemeral) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + previous_counter = session_state_get_previous_counter(state); + session_version = session_state_get_session_version(state); + + result = session_cipher_get_ciphertext(cipher, + &ciphertext, + session_version, &message_keys, + padded_message, padded_message_len); + if(result < 0) { + goto complete; + } + ciphertext_data = axolotl_buffer_data(ciphertext); + ciphertext_len = axolotl_buffer_len(ciphertext); + + chain_key_index = ratchet_chain_key_get_index(chain_key); + + local_identity_key = session_state_get_local_identity_key(state); + if(!local_identity_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + remote_identity_key = session_state_get_remote_identity_key(state); + if(!remote_identity_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + result = whisper_message_create(&message, + session_version, + message_keys.mac_key, sizeof(message_keys.mac_key), + sender_ephemeral, + chain_key_index, previous_counter, + ciphertext_data, ciphertext_len, + local_identity_key, remote_identity_key, + cipher->global_context); + if(result < 0) { + goto complete; + } + + if(session_state_has_unacknowledged_pre_key_message(state) == 1) { + uint32_t local_registration_id = session_state_get_local_registration_id(state); + int has_pre_key_id = 0; + uint32_t pre_key_id = 0; + uint32_t signed_pre_key_id; + ec_public_key *base_key; + + if(session_state_unacknowledged_pre_key_message_has_pre_key_id(state)) { + has_pre_key_id = 1; + pre_key_id = session_state_unacknowledged_pre_key_message_get_pre_key_id(state); + } + signed_pre_key_id = session_state_unacknowledged_pre_key_message_get_signed_pre_key_id(state); + base_key = session_state_unacknowledged_pre_key_message_get_base_key(state); + + if(!base_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + result = pre_key_whisper_message_create(&pre_key_message, + session_version, local_registration_id, (has_pre_key_id ? &pre_key_id : 0), + signed_pre_key_id, base_key, local_identity_key, + message, + cipher->global_context); + if(result < 0) { + goto complete; + } + AXOLOTL_UNREF(message); + message = 0; + } + + result = ratchet_chain_key_create_next(chain_key, &next_chain_key); + if(result < 0) { + goto complete; + } + + result = session_state_set_sender_chain_key(state, next_chain_key); + if(result < 0) { + goto complete; + } + + result = axolotl_session_store_session(cipher->store, cipher->remote_address, record); + +complete: + if(result >= 0) { + if(pre_key_message) { + *encrypted_message = (ciphertext_message *)pre_key_message; + } + else { + *encrypted_message = (ciphertext_message *)message; + } + } + else { + AXOLOTL_UNREF(pre_key_message); + AXOLOTL_UNREF(message); + } + axolotl_buffer_free(ciphertext); + AXOLOTL_UNREF(next_chain_key); + AXOLOTL_UNREF(record); + axolotl_explicit_bzero(&message_keys, sizeof(ratchet_message_keys)); + axolotl_unlock(cipher->global_context); + return result; +} + +int session_cipher_decrypt_pre_key_whisper_message(session_cipher *cipher, + pre_key_whisper_message *ciphertext, void *decrypt_context, + axolotl_buffer **plaintext) +{ + int result = 0; + axolotl_buffer *result_buf = 0; + session_record *record = 0; + int has_unsigned_pre_key_id = 0; + uint32_t unsigned_pre_key_id = 0; + + assert(cipher); + axolotl_lock(cipher->global_context); + + if(cipher->inside_callback == 1) { + result = AX_ERR_INVAL; + goto complete; + } + + result = axolotl_session_load_session(cipher->store, &record, cipher->remote_address); + if(result < 0) { + goto complete; + } + + result = session_builder_process_pre_key_whisper_message(cipher->builder, record, ciphertext, &unsigned_pre_key_id); + if(result < 0) { + goto complete; + } + has_unsigned_pre_key_id = result; + + result = session_cipher_decrypt_from_record_and_whisper_message(cipher, record, + pre_key_whisper_message_get_whisper_message(ciphertext), + &result_buf); + if(result < 0) { + goto complete; + } + + result = session_cipher_decrypt_callback(cipher, result_buf, decrypt_context); + if(result < 0) { + goto complete; + } + + result = axolotl_session_store_session(cipher->store, cipher->remote_address, record); + if(result < 0) { + goto complete; + } + + if(has_unsigned_pre_key_id) { + result = axolotl_pre_key_remove_key(cipher->store, unsigned_pre_key_id); + if(result < 0) { + goto complete; + } + } + +complete: + AXOLOTL_UNREF(record); + if(result >= 0) { + *plaintext = result_buf; + } + else { + axolotl_buffer_free(result_buf); + } + axolotl_unlock(cipher->global_context); + return result; +} + +int session_cipher_decrypt_whisper_message(session_cipher *cipher, + whisper_message *ciphertext, void *decrypt_context, + axolotl_buffer **plaintext) +{ + int result = 0; + axolotl_buffer *result_buf = 0; + session_record *record = 0; + + assert(cipher); + axolotl_lock(cipher->global_context); + + if(cipher->inside_callback == 1) { + result = AX_ERR_INVAL; + goto complete; + } + + result = axolotl_session_contains_session(cipher->store, cipher->remote_address); + if(result == 0) { + axolotl_log(cipher->global_context, AX_LOG_WARNING, "No session for: %s:%d", cipher->remote_address->name, cipher->remote_address->device_id); + result = AX_ERR_NO_SESSION; + goto complete; + } + else if(result < 0) { + goto complete; + } + + result = axolotl_session_load_session(cipher->store, &record, + cipher->remote_address); + if(result < 0) { + goto complete; + } + + result = session_cipher_decrypt_from_record_and_whisper_message( + cipher, record, ciphertext, &result_buf); + if(result < 0) { + goto complete; + } + + result = session_cipher_decrypt_callback(cipher, result_buf, decrypt_context); + if(result < 0) { + goto complete; + } + + result = axolotl_session_store_session(cipher->store, + cipher->remote_address, record); + +complete: + AXOLOTL_UNREF(record); + if(result >= 0) { + *plaintext = result_buf; + } + else { + axolotl_buffer_free(result_buf); + } + axolotl_unlock(cipher->global_context); + return result; +} + +static int session_cipher_decrypt_from_record_and_whisper_message(session_cipher *cipher, + session_record *record, whisper_message *ciphertext, axolotl_buffer **plaintext) +{ + int result = 0; + axolotl_buffer *result_buf = 0; + session_state *state = 0; + session_state *state_copy = 0; + session_record_state_node *previous_states_node = 0; + + assert(cipher); + axolotl_lock(cipher->global_context); + + state = session_record_get_state(record); + if(state) { + result = session_state_copy(&state_copy, state, cipher->global_context); + if(result < 0) { + goto complete; + } + + //TODO Collect and log invalid message errors if totally unsuccessful + + result = session_cipher_decrypt_from_state_and_whisper_message(cipher, state_copy, ciphertext, &result_buf); + if(result < 0 && result != AX_ERR_INVALID_MESSAGE) { + goto complete; + } + + if(result >= AX_SUCCESS) { + session_record_set_state(record, state_copy); + goto complete; + } + AXOLOTL_UNREF(state_copy); + } + + previous_states_node = session_record_get_previous_states_head(record); + while(previous_states_node) { + state = session_record_get_previous_states_element(previous_states_node); + + result = session_state_copy(&state_copy, state, cipher->global_context); + if(result < 0) { + goto complete; + } + + result = session_cipher_decrypt_from_state_and_whisper_message(cipher, state_copy, ciphertext, &result_buf); + if(result < 0 && result != AX_ERR_INVALID_MESSAGE) { + goto complete; + } + + if(result >= AX_SUCCESS) { + session_record_get_previous_states_remove(record, previous_states_node); + result = session_record_promote_state(record, state_copy); + goto complete; + } + + AXOLOTL_UNREF(state_copy); + previous_states_node = session_record_get_previous_states_next(previous_states_node); + } + + axolotl_log(cipher->global_context, AX_LOG_WARNING, "No valid sessions"); + result = AX_ERR_INVALID_MESSAGE; + +complete: + AXOLOTL_UNREF(state_copy); + if(result >= 0) { + *plaintext = result_buf; + } + else { + axolotl_buffer_free(result_buf); + } + axolotl_unlock(cipher->global_context); + return result; +} + +static int session_cipher_decrypt_from_state_and_whisper_message(session_cipher *cipher, + session_state *state, whisper_message *ciphertext, axolotl_buffer **plaintext) +{ + int result = 0; + axolotl_buffer *result_buf = 0; + ec_public_key *their_ephemeral = 0; + uint32_t counter = 0; + ratchet_chain_key *chain_key = 0; + ratchet_message_keys message_keys; + uint8_t message_version = 0; + uint32_t session_version = 0; + ec_public_key *remote_identity_key = 0; + ec_public_key *local_identity_key = 0; + axolotl_buffer *ciphertext_body = 0; + + if(!session_state_has_sender_chain(state)) { + axolotl_log(cipher->global_context, AX_LOG_WARNING, "Uninitialized session!"); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + message_version = whisper_message_get_message_version(ciphertext); + session_version = session_state_get_session_version(state); + + if(message_version != session_version) { + axolotl_log(cipher->global_context, AX_LOG_WARNING, "Message version %d, but session version %d", message_version, session_version); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + their_ephemeral = whisper_message_get_sender_ratchet_key(ciphertext); + if(!their_ephemeral) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + counter = whisper_message_get_counter(ciphertext); + + result = session_cipher_get_or_create_chain_key(cipher, &chain_key, state, their_ephemeral); + if(result < 0) { + goto complete; + } + + result = session_cipher_get_or_create_message_keys(&message_keys, state, + their_ephemeral, chain_key, counter, cipher->global_context); + if(result < 0) { + goto complete; + } + + remote_identity_key = session_state_get_remote_identity_key(state); + if(!remote_identity_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + local_identity_key = session_state_get_local_identity_key(state); + if(!local_identity_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + result = whisper_message_verify_mac(ciphertext, message_version, + remote_identity_key, local_identity_key, + message_keys.mac_key, sizeof(message_keys.mac_key), + cipher->global_context); + if(result != 1) { + if(result == 0) { + axolotl_log(cipher->global_context, AX_LOG_WARNING, "Message mac not verified"); + result = AX_ERR_INVALID_MESSAGE; + } + else if(result < 0) { + axolotl_log(cipher->global_context, AX_LOG_WARNING, "Error attempting to verify message mac"); + } + goto complete; + } + + ciphertext_body = whisper_message_get_body(ciphertext); + if(!ciphertext_body) { + axolotl_log(cipher->global_context, AX_LOG_WARNING, "Message body does not exist"); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + result = session_cipher_get_plaintext(cipher, &result_buf, message_version, &message_keys, + axolotl_buffer_data(ciphertext_body), axolotl_buffer_len(ciphertext_body)); + if(result < 0) { + goto complete; + } + + session_state_clear_unacknowledged_pre_key_message(state); + +complete: + AXOLOTL_UNREF(chain_key); + if(result >= 0) { + *plaintext = result_buf; + } + else { + axolotl_buffer_free(result_buf); + } + axolotl_explicit_bzero(&message_keys, sizeof(ratchet_message_keys)); + return result; +} + +static int session_cipher_get_or_create_chain_key(session_cipher *cipher, + ratchet_chain_key **chain_key, + session_state *state, ec_public_key *their_ephemeral) +{ + int result = 0; + ratchet_chain_key *result_key = 0; + ratchet_root_key *receiver_root_key = 0; + ratchet_chain_key *receiver_chain_key = 0; + ratchet_root_key *sender_root_key = 0; + ratchet_chain_key *sender_chain_key = 0; + ec_key_pair *our_new_ephemeral = 0; + ratchet_root_key *root_key = 0; + ec_key_pair *our_ephemeral = 0; + ratchet_chain_key *previous_sender_chain_key = 0; + uint32_t index = 0; + + result_key = session_state_get_receiver_chain_key(state, their_ephemeral); + if(result_key) { + AXOLOTL_REF(result_key); + goto complete; + } + + root_key = session_state_get_root_key(state); + if(!root_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + our_ephemeral = session_state_get_sender_ratchet_key_pair(state); + if(!our_ephemeral) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + result = ratchet_root_key_create_chain(root_key, + &receiver_root_key, &receiver_chain_key, + their_ephemeral, ec_key_pair_get_private(our_ephemeral)); + if(result < 0) { + goto complete; + } + + result = curve_generate_key_pair(cipher->global_context, &our_new_ephemeral); + if(result < 0) { + goto complete; + } + + result = ratchet_root_key_create_chain(receiver_root_key, + &sender_root_key, &sender_chain_key, + their_ephemeral, ec_key_pair_get_private(our_new_ephemeral)); + if(result < 0) { + goto complete; + } + + session_state_set_root_key(state, sender_root_key); + + result = session_state_add_receiver_chain(state, their_ephemeral, receiver_chain_key); + if(result < 0) { + goto complete; + } + + previous_sender_chain_key = session_state_get_sender_chain_key(state); + if(!previous_sender_chain_key) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + index = ratchet_chain_key_get_index(previous_sender_chain_key); + if(index > 0) { --index; } + + session_state_set_previous_counter(state, index); + session_state_set_sender_chain(state, our_new_ephemeral, sender_chain_key); + + result_key = receiver_chain_key; + AXOLOTL_REF(result_key); + +complete: + AXOLOTL_UNREF(receiver_root_key); + AXOLOTL_UNREF(receiver_chain_key); + AXOLOTL_UNREF(sender_root_key); + AXOLOTL_UNREF(sender_chain_key); + AXOLOTL_UNREF(our_new_ephemeral); + if(result >= 0) { + *chain_key = result_key; + } + else { + AXOLOTL_UNREF(result_key); + } + return result; +} + +static int session_cipher_get_or_create_message_keys(ratchet_message_keys *message_keys, + session_state *state, ec_public_key *their_ephemeral, + ratchet_chain_key *chain_key, uint32_t counter, axolotl_context *global_context) +{ + int result = 0; + ratchet_chain_key *cur_chain_key = 0; + ratchet_chain_key *next_chain_key = 0; + ratchet_message_keys message_keys_result; + + if(ratchet_chain_key_get_index(chain_key) > counter) { + result = session_state_remove_message_keys(state, &message_keys_result, their_ephemeral, counter); + if(result == 1) { + result = 0; + goto complete; + } + + axolotl_log(global_context, AX_LOG_WARNING, "Received message with old counter: %d, %d", + ratchet_chain_key_get_index(chain_key), counter); + result = AX_ERR_DUPLICATE_MESSAGE; + goto complete; + } + + if(counter - ratchet_chain_key_get_index(chain_key) > 2000) { + axolotl_log(global_context, AX_LOG_WARNING, "Over 2000 messages into the future!"); + result = AX_ERR_INVALID_MESSAGE; + goto complete; + } + + cur_chain_key = chain_key; + AXOLOTL_REF(cur_chain_key); + + while(ratchet_chain_key_get_index(cur_chain_key) < counter) { + result = ratchet_chain_key_get_message_keys(cur_chain_key, &message_keys_result); + if(result < 0) { + goto complete; + } + + result = session_state_set_message_keys(state, their_ephemeral, &message_keys_result); + if(result < 0) { + goto complete; + } + + result = ratchet_chain_key_create_next(cur_chain_key, &next_chain_key); + if(result < 0) { + goto complete; + } + AXOLOTL_UNREF(cur_chain_key); + cur_chain_key = next_chain_key; + next_chain_key = 0; + } + + result = ratchet_chain_key_create_next(cur_chain_key, &next_chain_key); + if(result < 0) { + goto complete; + } + + result = session_state_set_receiver_chain_key(state, their_ephemeral, next_chain_key); + if(result < 0) { + goto complete; + } + + result = ratchet_chain_key_get_message_keys(cur_chain_key, &message_keys_result); + if(result < 0) { + goto complete; + } + +complete: + if(result >= 0) { + memcpy(message_keys, &message_keys_result, sizeof(ratchet_message_keys)); + } + AXOLOTL_UNREF(cur_chain_key); + AXOLOTL_UNREF(next_chain_key); + axolotl_explicit_bzero(&message_keys_result, sizeof(ratchet_message_keys)); + return result; +} + +int session_cipher_get_remote_registration_id(session_cipher *cipher, uint32_t *remote_id) +{ + int result = 0; + uint32_t id_result = 0; + session_record *record = 0; + session_state *state = 0; + + assert(cipher); + axolotl_lock(cipher->global_context); + + result = axolotl_session_load_session(cipher->store, &record, cipher->remote_address); + if(result < 0) { + goto complete; + } + + state = session_record_get_state(record); + if(!state) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + id_result = session_state_get_remote_registration_id(state); + +complete: + if(result >= 0) { + *remote_id = id_result; + } + axolotl_unlock(cipher->global_context); + return result; +} + +int session_cipher_get_session_version(session_cipher *cipher, uint32_t *version) +{ + int result = 0; + uint32_t version_result = 0; + session_record *record = 0; + session_state *state = 0; + + assert(cipher); + axolotl_lock(cipher->global_context); + + result = axolotl_session_contains_session(cipher->store, cipher->remote_address); + if(result != 1) { + if(result == 0) { + axolotl_log(cipher->global_context, AX_LOG_WARNING, "No session for: %s:%d", cipher->remote_address->name, cipher->remote_address->device_id); + result = AX_ERR_NO_SESSION; + } + goto complete; + } + + result = axolotl_session_load_session(cipher->store, &record, cipher->remote_address); + if(result < 0) { + goto complete; + } + + state = session_record_get_state(record); + if(!state) { + result = AX_ERR_UNKNOWN; + goto complete; + } + + version_result = session_state_get_session_version(state); + +complete: + if(result >= 0) { + *version = version_result; + } + axolotl_unlock(cipher->global_context); + return result; +} + +static int session_cipher_get_ciphertext(session_cipher *cipher, + axolotl_buffer **ciphertext, + uint32_t version, ratchet_message_keys *message_keys, + const uint8_t *plaintext, size_t plaintext_len) +{ + int result = 0; + axolotl_buffer *output = 0; + + if(version >= 3) { + result = axolotl_encrypt(cipher->global_context, + &output, AX_CIPHER_AES_CBC_PKCS5, + message_keys->cipher_key, sizeof(message_keys->cipher_key), + message_keys->iv, sizeof(message_keys->iv), + plaintext, plaintext_len); + } + else { + uint8_t iv[16]; + memset(iv, 0, sizeof(iv)); + iv[3] = (uint8_t)(message_keys->counter); + iv[2] = (uint8_t)(message_keys->counter >> 8); + iv[1] = (uint8_t)(message_keys->counter >> 16); + iv[0] = (uint8_t)(message_keys->counter >> 24); + + result = axolotl_encrypt(cipher->global_context, + &output, AX_CIPHER_AES_CTR_NOPADDING, + message_keys->cipher_key, sizeof(message_keys->cipher_key), + iv, sizeof(iv), + plaintext, plaintext_len); + } + + if(result >= 0) { + *ciphertext = output; + } + + return result; +} + +static int session_cipher_get_plaintext(session_cipher *cipher, + axolotl_buffer **plaintext, + uint32_t version, ratchet_message_keys *message_keys, + const uint8_t *ciphertext, size_t ciphertext_len) +{ + int result = 0; + axolotl_buffer *output = 0; + + if(version >= 3) { + result = axolotl_decrypt(cipher->global_context, + &output, AX_CIPHER_AES_CBC_PKCS5, + message_keys->cipher_key, sizeof(message_keys->cipher_key), + message_keys->iv, sizeof(message_keys->iv), + ciphertext, ciphertext_len); + } + else { + uint8_t iv[16]; + memset(iv, 0, sizeof(iv)); + iv[3] = (uint8_t)(message_keys->counter); + iv[2] = (uint8_t)(message_keys->counter >> 8); + iv[1] = (uint8_t)(message_keys->counter >> 16); + iv[0] = (uint8_t)(message_keys->counter >> 24); + + result = axolotl_decrypt(cipher->global_context, + &output, AX_CIPHER_AES_CTR_NOPADDING, + message_keys->cipher_key, sizeof(message_keys->cipher_key), + iv, sizeof(iv), + ciphertext, ciphertext_len); + } + + if(result >= 0) { + *plaintext = output; + } + + return result; +} + +static int session_cipher_decrypt_callback(session_cipher *cipher, axolotl_buffer *plaintext, void *decrypt_context) +{ + int result = 0; + if(cipher->decrypt_callback) { + cipher->inside_callback = 1; + result = cipher->decrypt_callback(cipher, plaintext, decrypt_context); + cipher->inside_callback = 0; + } + return result; +} + +void session_cipher_free(session_cipher *cipher) +{ + if(cipher) { + session_builder_free(cipher->builder); + free(cipher); + } +} diff --git a/libs/libaxolotl/src/session_cipher.h b/libs/libaxolotl/src/session_cipher.h new file mode 100644 index 0000000000..9e4e61b6cf --- /dev/null +++ b/libs/libaxolotl/src/session_cipher.h @@ -0,0 +1,156 @@ +#ifndef SESSION_CIPHER_H +#define SESSION_CIPHER_H + +#include <stdint.h> +#include <stddef.h> +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The main entry point for Axolotl encrypt/decrypt operations. + * + * Once a session has been established with session_builder, + * this class can be used for all encrypt/decrypt operations within + * that session. + */ + +/** + * Construct a session cipher for encrypt/decrypt operations on a session. + * In order to use session_cipher, a session must have already been created + * and stored using session_builder. + * + * The store and global contexts must remain valid for the lifetime of the + * session cipher. + * + * When finished, free the returned instance by calling session_cipher_free(). + * + * @param cipher set to a freshly allocated session cipher instance + * @param store the axolotl_store_context to store all state information in + * @param remote_address the remote address that messages will be encrypted to or decrypted from. + * @param global_context the global library context + * @return 0 on success, or negative on failure + */ +int session_cipher_create(session_cipher **cipher, + axolotl_store_context *store, const axolotl_address *remote_address, + axolotl_context *global_context); + +/** + * Set the optional user data pointer for the session cipher. + * + * This is to give callback functions a way of accessing app specific + * context information for this cipher. + */ +void session_cipher_set_user_data(session_cipher *cipher, void *user_data); + +/** + * Get the optional user data pointer for the session cipher. + * + * This is to give callback functions a way of accessing app specific + * context information for this cipher. + */ +void *session_cipher_get_user_data(session_cipher *cipher); + +/** + * Set the callback function that is called during the decrypt process. + * + * The callback function is called from within + * session_cipher_decrypt_pre_key_whisper_message() and + * session_cipher_decrypt_whisper_message() after decryption is complete + * but before the updated session state has been committed to the session + * store. If the callback function returns a negative value, then the + * decrypt function will immediately fail with an error. + * + * This a callback allows some implementations to store the committed plaintext + * to their local message store first, in case they are concerned with a crash + * or write error happening between the time the session state is updated but + * before they're able to successfully store the plaintext to disk. + * + * @param callback the callback function to set + * @param user_data user data pointer provided to the callback + */ +void session_cipher_set_decryption_callback(session_cipher *cipher, + int (*callback)(session_cipher *cipher, axolotl_buffer *plaintext, void *decrypt_context)); + +/** + * Encrypt a message. + * + * @param padded_message The plaintext message bytes, optionally padded to a constant multiple. + * @param padded_message_len The length of the data pointed to by padded_message + * @param encrypted_message Set to a ciphertext message encrypted to the recipient+device tuple. + * + * @return AX_SUCCESS on success, negative on error + */ +int session_cipher_encrypt(session_cipher *cipher, + const uint8_t *padded_message, size_t padded_message_len, + ciphertext_message **encrypted_message); + +/** + * Decrypt a message. + * + * @param ciphertext The pre_key_whisper_message to decrypt. + * @param decrypt_context Optional context pointer associated with the + * ciphertext, which is passed to the decryption callback function + * @param plaintext Set to a newly allocated buffer containing the plaintext. + * + * @retval AX_SUCCESS Success + * @retval AX_ERR_INVALID_MESSAGE if the input is not valid ciphertext. + * @retval AX_ERR_DUPLICATE_MESSAGE if the input is a message that has already been received. + * @retval AX_ERR_LEGACY_MESSAGE if the input is a message formatted by a protocol version that + * is no longer supported. + * @retval AX_ERR_INVALID_KEY_ID when there is no local pre_key_record + * that corresponds to the pre key ID in the message. + * @retval AX_ERR_INVALID_KEY when the message is formatted incorrectly. + * @retval AX_ERR_UNTRUSTED_IDENTITY when the identity key of the sender is untrusted. + */ +int session_cipher_decrypt_pre_key_whisper_message(session_cipher *cipher, + pre_key_whisper_message *ciphertext, void *decrypt_context, + axolotl_buffer **plaintext); + +/** + * Decrypt a message. + * + * @param ciphertext The whisper_message to decrypt. + * @param decrypt_context Optional context pointer associated with the + * ciphertext, which is passed to the decryption callback function + * @param plaintext Set to a newly allocated buffer containing the plaintext. + * + * @retval AX_SUCCESS Success + * @retval AX_ERR_INVALID_MESSAGE if the input is not valid ciphertext. + * @retval AX_ERR_DUPLICATE_MESSAGE if the input is a message that has already been received. + * @retval AX_ERR_LEGACY_MESSAGE if the input is a message formatted by a protocol version that + * is no longer supported. + * @retval AX_ERR_NO_SESSION if there is no established session for this contact. + */ +int session_cipher_decrypt_whisper_message(session_cipher *cipher, + whisper_message *ciphertext, void *decrypt_context, + axolotl_buffer **plaintext); + +/** + * Gets the remote registration ID for this session cipher. + * + * @param remote_id Set to the value of the remote registration ID + * + * @return AX_SUCCESS on success, negative on error + */ +int session_cipher_get_remote_registration_id(session_cipher *cipher, uint32_t *remote_id); + +/** + * Gets the version of the session associated with this session cipher. + * + * @param version Set to the value of the session version + * + * @retval AX_SUCCESS Success + * @retval AX_ERR_NO_SESSION if no session could be found + */ +int session_cipher_get_session_version(session_cipher *cipher, uint32_t *version); + +void session_cipher_free(session_cipher *cipher); + +#ifdef __cplusplus +} +#endif + +#endif /* SESSION_CIPHER_H */ diff --git a/libs/libaxolotl/src/session_pre_key.c b/libs/libaxolotl/src/session_pre_key.c new file mode 100644 index 0000000000..333d2df6ce --- /dev/null +++ b/libs/libaxolotl/src/session_pre_key.c @@ -0,0 +1,552 @@ +#include "session_pre_key.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "axolotl_internal.h" +#include "curve.h" +#include "LocalStorageProtocol.pb-c.h" + +struct session_pre_key { + axolotl_type_base base; + uint32_t id; + ec_key_pair *key_pair; +}; + +struct session_signed_pre_key { + axolotl_type_base base; + uint32_t id; + ec_key_pair *key_pair; + uint64_t timestamp; + size_t signature_len; + uint8_t signature[]; +}; + +struct session_pre_key_bundle { + axolotl_type_base base; + uint32_t registration_id; + int device_id; + uint32_t pre_key_id; + ec_public_key *pre_key_public; + uint32_t signed_pre_key_id; + ec_public_key *signed_pre_key_public; + axolotl_buffer *signed_pre_key_signature; + ec_public_key *identity_key; +}; + +/*------------------------------------------------------------------------*/ + +int session_pre_key_create(session_pre_key **pre_key, uint32_t id, ec_key_pair *key_pair) +{ + session_pre_key *result = 0; + + assert(key_pair); + + result = malloc(sizeof(session_pre_key)); + if(!result) { + return AX_ERR_NOMEM; + } + memset(result, 0, sizeof(session_pre_key)); + AXOLOTL_INIT(result, session_pre_key_destroy); + + result->id = id; + + AXOLOTL_REF(key_pair); + result->key_pair = key_pair; + + *pre_key = result; + return 0; +} + +int session_pre_key_serialize(axolotl_buffer **buffer, const session_pre_key *pre_key) +{ + int result = 0; + size_t result_size = 0; + Textsecure__PreKeyRecordStructure record = TEXTSECURE__PRE_KEY_RECORD_STRUCTURE__INIT; + axolotl_buffer *public_buf = 0; + axolotl_buffer *private_buf = 0; + axolotl_buffer *result_buf = 0; + ec_public_key *public_key = 0; + ec_private_key *private_key = 0; + size_t len = 0; + uint8_t *data = 0; + + public_key = ec_key_pair_get_public(pre_key->key_pair); + result = ec_public_key_serialize(&public_buf, public_key); + if(result < 0) { + goto complete; + } + + private_key = ec_key_pair_get_private(pre_key->key_pair); + result = ec_private_key_serialize(&private_buf, private_key); + if(result < 0) { + goto complete; + } + + record.has_id = 1; + record.id = pre_key->id; + + record.has_publickey = 1; + record.publickey.data = axolotl_buffer_data(public_buf); + record.publickey.len = axolotl_buffer_len(public_buf); + + record.has_privatekey = 1; + record.privatekey.data = axolotl_buffer_data(private_buf); + record.privatekey.len = axolotl_buffer_len(private_buf); + + len = textsecure__pre_key_record_structure__get_packed_size(&record); + + result_buf = axolotl_buffer_alloc(len); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(result_buf); + result_size = textsecure__pre_key_record_structure__pack(&record, data); + if(result_size != len) { + axolotl_buffer_free(result_buf); + result = AX_ERR_INVALID_PROTO_BUF; + result_buf = 0; + goto complete; + } + +complete: + if(public_buf) { + axolotl_buffer_free(public_buf); + } + if(private_buf) { + axolotl_buffer_free(private_buf); + } + if(result >= 0) { + *buffer = result_buf; + } + return result; +} + +int session_pre_key_deserialize(session_pre_key **pre_key, const uint8_t *data, size_t len, axolotl_context *global_context) +{ + int result = 0; + Textsecure__PreKeyRecordStructure *record = 0; + ec_public_key *public_key = 0; + ec_private_key *private_key = 0; + ec_key_pair *key_pair = 0; + session_pre_key *result_pre_key = 0; + + record = textsecure__pre_key_record_structure__unpack(0, len, data); + if(!record) { + result = AX_ERR_INVALID_PROTO_BUF; + goto complete; + } + + if(!record->has_id || !record->has_publickey || !record->has_privatekey) { + result = AX_ERR_INVALID_KEY; + goto complete; + } + + result = curve_decode_point(&public_key, record->publickey.data, record->publickey.len, global_context); + if(result < 0) { + goto complete; + } + + result = curve_decode_private_point(&private_key, record->privatekey.data, record->privatekey.len, global_context); + if(result < 0) { + goto complete; + } + + result = ec_key_pair_create(&key_pair, public_key, private_key); + if(result < 0) { + goto complete; + } + + result = session_pre_key_create(&result_pre_key, record->id, key_pair); + if(result < 0) { + goto complete; + } + +complete: + if(record) { + textsecure__pre_key_record_structure__free_unpacked(record, 0); + } + if(public_key) { + AXOLOTL_UNREF(public_key); + } + if(private_key) { + AXOLOTL_UNREF(private_key); + } + if(key_pair) { + AXOLOTL_UNREF(key_pair); + } + if(result >= 0) { + *pre_key = result_pre_key; + } + return result; +} + +uint32_t session_pre_key_get_id(const session_pre_key *pre_key) +{ + return pre_key->id; +} + +ec_key_pair *session_pre_key_get_key_pair(const session_pre_key *pre_key) +{ + return pre_key->key_pair; +} + +void session_pre_key_destroy(axolotl_type_base *type) +{ + session_pre_key *pre_key = (session_pre_key *)type; + + if(pre_key->key_pair) { + AXOLOTL_UNREF(pre_key->key_pair); + } + + free(pre_key); +} + +/*------------------------------------------------------------------------*/ + +int session_signed_pre_key_create(session_signed_pre_key **pre_key, + uint32_t id, uint64_t timestamp, ec_key_pair *key_pair, + const uint8_t *signature, size_t signature_len) +{ + session_signed_pre_key *result = 0; + + assert(key_pair); + assert(signature); + assert(signature_len > 0); + + if(signature_len > (SIZE_MAX - sizeof(session_signed_pre_key)) / sizeof(uint8_t)) { + return AX_ERR_NOMEM; + } + + result = malloc(sizeof(session_signed_pre_key) + (sizeof(uint8_t) * signature_len)); + if(!result) { + return AX_ERR_NOMEM; + } + memset(result, 0, sizeof(session_signed_pre_key)); + AXOLOTL_INIT(result, session_signed_pre_key_destroy); + + result->id = id; + result->timestamp = timestamp; + + AXOLOTL_REF(key_pair); + result->key_pair = key_pair; + + result->signature_len = signature_len; + + memcpy(result->signature, signature, signature_len); + + *pre_key = result; + return 0; +} + +int session_signed_pre_key_serialize(axolotl_buffer **buffer, const session_signed_pre_key *pre_key) +{ + int result = 0; + size_t result_size = 0; + Textsecure__SignedPreKeyRecordStructure record = TEXTSECURE__SIGNED_PRE_KEY_RECORD_STRUCTURE__INIT; + axolotl_buffer *public_buf = 0; + axolotl_buffer *private_buf = 0; + axolotl_buffer *signature_buf = 0; + axolotl_buffer *result_buf = 0; + ec_public_key *public_key = 0; + ec_private_key *private_key = 0; + size_t len = 0; + uint8_t *data = 0; + + public_key = ec_key_pair_get_public(pre_key->key_pair); + result = ec_public_key_serialize(&public_buf, public_key); + if(result < 0) { + goto complete; + } + + private_key = ec_key_pair_get_private(pre_key->key_pair); + result = ec_private_key_serialize(&private_buf, private_key); + if(result < 0) { + goto complete; + } + + signature_buf = axolotl_buffer_create(pre_key->signature, pre_key->signature_len); + if(!signature_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + record.has_id = 1; + record.id = pre_key->id; + + record.has_timestamp = 1; + record.timestamp = pre_key->timestamp; + + record.has_publickey = 1; + record.publickey.data = axolotl_buffer_data(public_buf); + record.publickey.len = axolotl_buffer_len(public_buf); + + record.has_privatekey = 1; + record.privatekey.data = axolotl_buffer_data(private_buf); + record.privatekey.len = axolotl_buffer_len(private_buf); + + record.has_signature = 1; + record.signature.data = axolotl_buffer_data(signature_buf); + record.signature.len = axolotl_buffer_len(signature_buf); + + len = textsecure__signed_pre_key_record_structure__get_packed_size(&record); + + result_buf = axolotl_buffer_alloc(len); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(result_buf); + result_size = textsecure__signed_pre_key_record_structure__pack(&record, data); + if(result_size != len) { + axolotl_buffer_free(result_buf); + result = AX_ERR_INVALID_PROTO_BUF; + result_buf = 0; + goto complete; + } + +complete: + if(public_buf) { + axolotl_buffer_free(public_buf); + } + if(private_buf) { + axolotl_buffer_free(private_buf); + } + if(signature_buf) { + axolotl_buffer_free(signature_buf); + } + if(result >= 0) { + *buffer = result_buf; + } + return result; +} + +int session_signed_pre_key_deserialize(session_signed_pre_key **pre_key, const uint8_t *data, size_t len, axolotl_context *global_context) +{ + int result = 0; + Textsecure__SignedPreKeyRecordStructure *record = 0; + ec_public_key *public_key = 0; + ec_private_key *private_key = 0; + ec_key_pair *key_pair = 0; + session_signed_pre_key *result_pre_key = 0; + + record = textsecure__signed_pre_key_record_structure__unpack(0, len, data); + if(!record) { + result = AX_ERR_INVALID_PROTO_BUF; + goto complete; + } + + if(!record->has_id || !record->has_timestamp + || !record->has_publickey || !record->has_privatekey + || !record->has_signature) { + result = AX_ERR_INVALID_KEY; + goto complete; + } + + result = curve_decode_point(&public_key, record->publickey.data, record->publickey.len, global_context); + if(result < 0) { + goto complete; + } + + result = curve_decode_private_point(&private_key, record->privatekey.data, record->privatekey.len, global_context); + if(result < 0) { + goto complete; + } + + result = ec_key_pair_create(&key_pair, public_key, private_key); + if(result < 0) { + goto complete; + } + + result = session_signed_pre_key_create(&result_pre_key, + record->id, record->timestamp, key_pair, + record->signature.data, record->signature.len); + if(result < 0) { + goto complete; + } + +complete: + if(record) { + textsecure__signed_pre_key_record_structure__free_unpacked(record, 0); + } + if(public_key) { + AXOLOTL_UNREF(public_key); + } + if(private_key) { + AXOLOTL_UNREF(private_key); + } + if(key_pair) { + AXOLOTL_UNREF(key_pair); + } + if(result >= 0) { + *pre_key = result_pre_key; + } + return result; +} + +uint32_t session_signed_pre_key_get_id(const session_signed_pre_key *pre_key) +{ + return pre_key->id; +} + +uint64_t session_signed_pre_key_get_timestamp(const session_signed_pre_key *pre_key) +{ + return pre_key->timestamp; +} + +ec_key_pair *session_signed_pre_key_get_key_pair(const session_signed_pre_key *pre_key) +{ + return pre_key->key_pair; +} + +const uint8_t *session_signed_pre_key_get_signature(const session_signed_pre_key *pre_key) +{ + return pre_key->signature; +} + +size_t session_signed_pre_key_get_signature_len(const session_signed_pre_key *pre_key) +{ + return pre_key->signature_len; +} + +void session_signed_pre_key_destroy(axolotl_type_base *type) +{ + session_signed_pre_key *pre_key = (session_signed_pre_key *)type; + + if(pre_key->key_pair) { + AXOLOTL_UNREF(pre_key->key_pair); + } + + free(pre_key); +} + +/*------------------------------------------------------------------------*/ + +int session_pre_key_bundle_create(session_pre_key_bundle **bundle, + uint32_t registration_id, int device_id, uint32_t pre_key_id, + ec_public_key *pre_key_public, + uint32_t signed_pre_key_id, ec_public_key *signed_pre_key_public, + const uint8_t *signed_pre_key_signature_data, size_t signed_pre_key_signature_len, + ec_public_key *identity_key) +{ + int result = 0; + session_pre_key_bundle *result_bundle = 0; + + result_bundle = malloc(sizeof(session_pre_key_bundle)); + if(!result_bundle) { + result = AX_ERR_NOMEM; + goto complete; + } + + memset(result_bundle, 0, sizeof(session_pre_key_bundle)); + AXOLOTL_INIT(result_bundle, session_pre_key_bundle_destroy); + + result_bundle->registration_id = registration_id; + result_bundle->device_id = device_id; + result_bundle->pre_key_id = pre_key_id; + + if(pre_key_public) { + AXOLOTL_REF(pre_key_public); + result_bundle->pre_key_public = pre_key_public; + } + + result_bundle->signed_pre_key_id = signed_pre_key_id; + + if(signed_pre_key_public) { + AXOLOTL_REF(signed_pre_key_public); + result_bundle->signed_pre_key_public = signed_pre_key_public; + } + + if(signed_pre_key_signature_data && signed_pre_key_signature_len > 0) { + result_bundle->signed_pre_key_signature = axolotl_buffer_create( + signed_pre_key_signature_data, signed_pre_key_signature_len); + } + + if(identity_key) { + AXOLOTL_REF(identity_key); + result_bundle->identity_key = identity_key; + } + +complete: + if(result >= 0) { + *bundle = result_bundle; + } + else { + if(result_bundle) { + AXOLOTL_UNREF(result_bundle); + } + } + return result; +} + +uint32_t session_pre_key_bundle_get_registration_id(const session_pre_key_bundle *bundle) +{ + assert(bundle); + return bundle->registration_id; +} + +int session_pre_key_bundle_get_device_id(const session_pre_key_bundle *bundle) +{ + assert(bundle); + return bundle->device_id; +} + +uint32_t session_pre_key_bundle_get_pre_key_id(const session_pre_key_bundle *bundle) +{ + assert(bundle); + return bundle->pre_key_id; +} + +ec_public_key *session_pre_key_bundle_get_pre_key(const session_pre_key_bundle *bundle) +{ + assert(bundle); + return bundle->pre_key_public; +} + +uint32_t session_pre_key_bundle_get_signed_pre_key_id(const session_pre_key_bundle *bundle) +{ + assert(bundle); + return bundle->signed_pre_key_id; +} + +ec_public_key *session_pre_key_bundle_get_signed_pre_key(const session_pre_key_bundle *bundle) +{ + assert(bundle); + return bundle->signed_pre_key_public; +} + +axolotl_buffer *session_pre_key_bundle_get_signed_pre_key_signature(const session_pre_key_bundle *bundle) +{ + assert(bundle); + return bundle->signed_pre_key_signature; +} + +ec_public_key *session_pre_key_bundle_get_identity_key(const session_pre_key_bundle *bundle) +{ + assert(bundle); + return bundle->identity_key; +} + +void session_pre_key_bundle_destroy(axolotl_type_base *type) +{ + session_pre_key_bundle *bundle = (session_pre_key_bundle *)type; + + if(bundle->pre_key_public) { + AXOLOTL_UNREF(bundle->pre_key_public); + } + if(bundle->signed_pre_key_public) { + AXOLOTL_UNREF(bundle->signed_pre_key_public); + } + if(bundle->signed_pre_key_signature) { + axolotl_buffer_free(bundle->signed_pre_key_signature); + } + if(bundle->identity_key) { + AXOLOTL_UNREF(bundle->identity_key); + } + + free(bundle); +} diff --git a/libs/libaxolotl/src/session_pre_key.h b/libs/libaxolotl/src/session_pre_key.h new file mode 100644 index 0000000000..4b5f1b179a --- /dev/null +++ b/libs/libaxolotl/src/session_pre_key.h @@ -0,0 +1,65 @@ +#ifndef SESSION_PRE_KEY_H +#define SESSION_PRE_KEY_H + +#include <stdint.h> +#include <stddef.h> +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PRE_KEY_MEDIUM_MAX_VALUE 0xFFFFFF + +/*------------------------------------------------------------------------*/ + +int session_pre_key_create(session_pre_key **pre_key, uint32_t id, ec_key_pair *key_pair); +int session_pre_key_serialize(axolotl_buffer **buffer, const session_pre_key *pre_key); +int session_pre_key_deserialize(session_pre_key **pre_key, const uint8_t *data, size_t len, axolotl_context *global_context); + +uint32_t session_pre_key_get_id(const session_pre_key *pre_key); +ec_key_pair *session_pre_key_get_key_pair(const session_pre_key *pre_key); + +void session_pre_key_destroy(axolotl_type_base *type); + +/*------------------------------------------------------------------------*/ + +int session_signed_pre_key_create(session_signed_pre_key **pre_key, + uint32_t id, uint64_t timestamp, ec_key_pair *key_pair, + const uint8_t *signature, size_t signature_len); +int session_signed_pre_key_serialize(axolotl_buffer **buffer, const session_signed_pre_key *pre_key); +int session_signed_pre_key_deserialize(session_signed_pre_key **pre_key, const uint8_t *data, size_t len, axolotl_context *global_context); + +uint32_t session_signed_pre_key_get_id(const session_signed_pre_key *pre_key); +uint64_t session_signed_pre_key_get_timestamp(const session_signed_pre_key *pre_key); +ec_key_pair *session_signed_pre_key_get_key_pair(const session_signed_pre_key *pre_key); +const uint8_t *session_signed_pre_key_get_signature(const session_signed_pre_key *pre_key); +size_t session_signed_pre_key_get_signature_len(const session_signed_pre_key *pre_key); + +void session_signed_pre_key_destroy(axolotl_type_base *type); + +/*------------------------------------------------------------------------*/ + +int session_pre_key_bundle_create(session_pre_key_bundle **bundle, + uint32_t registration_id, int device_id, uint32_t pre_key_id, + ec_public_key *pre_key_public, + uint32_t signed_pre_key_id, ec_public_key *signed_pre_key_public, + const uint8_t *signed_pre_key_signature_data, size_t signed_pre_key_signature_len, + ec_public_key *identity_key); + +uint32_t session_pre_key_bundle_get_registration_id(const session_pre_key_bundle *bundle); +int session_pre_key_bundle_get_device_id(const session_pre_key_bundle *bundle); +uint32_t session_pre_key_bundle_get_pre_key_id(const session_pre_key_bundle *bundle); +ec_public_key *session_pre_key_bundle_get_pre_key(const session_pre_key_bundle *bundle); +uint32_t session_pre_key_bundle_get_signed_pre_key_id(const session_pre_key_bundle *bundle); +ec_public_key *session_pre_key_bundle_get_signed_pre_key(const session_pre_key_bundle *bundle); +axolotl_buffer *session_pre_key_bundle_get_signed_pre_key_signature(const session_pre_key_bundle *bundle); +ec_public_key *session_pre_key_bundle_get_identity_key(const session_pre_key_bundle *bundle); + +void session_pre_key_bundle_destroy(axolotl_type_base *type); + +#ifdef __cplusplus +} +#endif + +#endif /* SESSION_PRE_KEY_H */ diff --git a/libs/libaxolotl/src/session_record.c b/libs/libaxolotl/src/session_record.c new file mode 100644 index 0000000000..c6b5cba5a4 --- /dev/null +++ b/libs/libaxolotl/src/session_record.c @@ -0,0 +1,427 @@ +#include "session_record.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "axolotl_internal.h" +#include "session_state.h" +#include "utlist.h" +#include "LocalStorageProtocol.pb-c.h" + +#define ARCHIVED_STATES_MAX_LENGTH 40 + +struct session_record_state_node +{ + session_state *state; + struct session_record_state_node *prev, *next; +}; + +struct session_record +{ + axolotl_type_base base; + session_state *state; + session_record_state_node *previous_states_head; + int is_fresh; + axolotl_context *global_context; +}; + +static void session_record_free_previous_states(session_record *record); + +int session_record_create(session_record **record, session_state *state, axolotl_context *global_context) +{ + session_record *result = malloc(sizeof(session_record)); + if(!result) { + return AX_ERR_NOMEM; + } + memset(result, 0, sizeof(session_record)); + AXOLOTL_INIT(result, session_record_destroy); + + if(!state) { + int ret = session_state_create(&result->state, global_context); + if(ret < 0) { + AXOLOTL_UNREF(result); + return ret; + } + result->is_fresh = 1; + } + else { + AXOLOTL_REF(state); + result->state = state; + result->is_fresh = 0; + } + result->global_context = global_context; + + *record = result; + return 0; +} + +int session_record_serialize(axolotl_buffer **buffer, const session_record *record) +{ + int result = 0; + size_t result_size = 0; + unsigned int i = 0; + Textsecure__RecordStructure record_structure = TEXTSECURE__RECORD_STRUCTURE__INIT; + session_record_state_node *cur_node = 0; + axolotl_buffer *result_buf = 0; + size_t len = 0; + uint8_t *data = 0; + + if(record->state) { + record_structure.currentsession = malloc(sizeof(Textsecure__SessionStructure)); + if(!record_structure.currentsession) { + result = AX_ERR_NOMEM; + goto complete; + } + textsecure__session_structure__init(record_structure.currentsession); + result = session_state_serialize_prepare(record->state, record_structure.currentsession); + if(result < 0) { + goto complete; + } + } + + if(record->previous_states_head) { + unsigned int count; + DL_COUNT(record->previous_states_head, cur_node, count); + + if(count > SIZE_MAX / sizeof(Textsecure__SessionStructure *)) { + result = AX_ERR_NOMEM; + goto complete; + } + + record_structure.previoussessions = malloc(sizeof(Textsecure__SessionStructure *) * count); + if(!record_structure.previoussessions) { + result = AX_ERR_NOMEM; + goto complete; + } + + i = 0; + DL_FOREACH(record->previous_states_head, cur_node) { + record_structure.previoussessions[i] = malloc(sizeof(Textsecure__SessionStructure)); + if(!record_structure.previoussessions[i]) { + result = AX_ERR_NOMEM; + break; + } + textsecure__session_structure__init(record_structure.previoussessions[i]); + result = session_state_serialize_prepare(cur_node->state, record_structure.previoussessions[i]); + if(result < 0) { + break; + } + i++; + } + record_structure.n_previoussessions = i; + if(result < 0) { + goto complete; + } + } + + len = textsecure__record_structure__get_packed_size(&record_structure); + + result_buf = axolotl_buffer_alloc(len); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(result_buf); + result_size = textsecure__record_structure__pack(&record_structure, data); + if(result_size != len) { + axolotl_buffer_free(result_buf); + result = AX_ERR_INVALID_PROTO_BUF; + result_buf = 0; + goto complete; + } + +complete: + if(record_structure.currentsession) { + session_state_serialize_prepare_free(record_structure.currentsession); + } + if(record_structure.previoussessions) { + for(i = 0; i < record_structure.n_previoussessions; i++) { + if(record_structure.previoussessions[i]) { + session_state_serialize_prepare_free(record_structure.previoussessions[i]); + } + } + free(record_structure.previoussessions); + } + + if(result >= 0) { + *buffer = result_buf; + } + return result; +} + +int session_record_deserialize(session_record **record, const uint8_t *data, size_t len, axolotl_context *global_context) +{ + int result = 0; + session_record *result_record = 0; + session_state *current_state = 0; + session_record_state_node *previous_states_head = 0; + Textsecure__RecordStructure *record_structure = 0; + + record_structure = textsecure__record_structure__unpack(0, len, data); + if(!record_structure) { + result = AX_ERR_INVALID_PROTO_BUF; + goto complete; + } + + if(record_structure->currentsession) { + result = session_state_deserialize_protobuf(¤t_state, record_structure->currentsession, global_context); + if(result < 0) { + goto complete; + } + } + + result = session_record_create(&result_record, current_state, global_context); + if(result < 0) { + goto complete; + } + AXOLOTL_UNREF(current_state); + current_state = 0; + result_record->is_fresh = 0; + + if(record_structure->n_previoussessions > 0) { + unsigned int i; + for(i = 0; i < record_structure->n_previoussessions; i++) { + Textsecure__SessionStructure *session_structure = + record_structure->previoussessions[i]; + + session_record_state_node *node = malloc(sizeof(session_record_state_node)); + if(!node) { + result = AX_ERR_NOMEM; + goto complete; + } + + result = session_state_deserialize_protobuf(&node->state, session_structure, global_context); + if(result < 0) { + free(node); + goto complete; + } + + DL_APPEND(previous_states_head, node); + } + } + result_record->previous_states_head = previous_states_head; + previous_states_head = 0; + +complete: + if(record_structure) { + textsecure__record_structure__free_unpacked(record_structure, 0); + } + if(current_state) { + AXOLOTL_UNREF(current_state); + } + if(previous_states_head) { + session_record_state_node *cur_node; + session_record_state_node *tmp_node; + DL_FOREACH_SAFE(previous_states_head, cur_node, tmp_node) { + DL_DELETE(previous_states_head, cur_node); + free(cur_node); + } + } + if(result_record) { + if(result < 0) { + AXOLOTL_UNREF(result_record); + } + else { + *record = result_record; + } + } + + return result; +} + +int session_record_copy(session_record **record, session_record *other_record, axolotl_context *global_context) +{ + int result = 0; + axolotl_buffer *buffer = 0; + size_t len = 0; + uint8_t *data = 0; + + assert(other_record); + assert(global_context); + + result = session_record_serialize(&buffer, other_record); + if(result < 0) { + goto complete; + } + + data = axolotl_buffer_data(buffer); + len = axolotl_buffer_len(buffer); + + result = session_record_deserialize(record, data, len, global_context); + if(result < 0) { + goto complete; + } + +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + return result; +} + +int session_record_has_session_state(session_record *record, uint32_t version, const ec_public_key *alice_base_key) +{ + session_record_state_node *cur_node = 0; + + assert(record); + assert(record->state); + + if(session_state_get_session_version(record->state) == version && + ec_public_key_compare( + session_state_get_alice_base_key(record->state), + alice_base_key) == 0) { + return 1; + } + + DL_FOREACH(record->previous_states_head, cur_node) { + if(session_state_get_session_version(cur_node->state) == version && + ec_public_key_compare( + session_state_get_alice_base_key(cur_node->state), + alice_base_key) == 0) { + return 1; + } + } + + return 0; +} + +session_state *session_record_get_state(session_record *record) +{ + return record->state; +} + +void session_record_set_state(session_record *record, session_state *state) +{ + assert(record); + assert(state); + if(record->state) { + AXOLOTL_UNREF(record->state); + } + AXOLOTL_REF(state); + record->state = state; +} + +session_record_state_node *session_record_get_previous_states_head(const session_record *record) +{ + assert(record); + return record->previous_states_head; +} + +session_state *session_record_get_previous_states_element(const session_record_state_node *node) +{ + assert(node); + return node->state; +} + +session_record_state_node *session_record_get_previous_states_next(const session_record_state_node *node) +{ + assert(node); + return node->next; +} + +session_record_state_node *session_record_get_previous_states_remove(session_record *record, session_record_state_node *node) +{ + session_record_state_node *next_node = 0; + + assert(record); + assert(node); + + next_node = node->next; + DL_DELETE(record->previous_states_head, node); + AXOLOTL_UNREF(node->state); + free(node); + return next_node; +} + +int session_record_is_fresh(session_record *record) +{ + assert(record); + return record->is_fresh; +} + +int session_record_archive_current_state(session_record *record) +{ + int result = 0; + session_state *new_state = 0; + + assert(record); + + result = session_state_create(&new_state, record->global_context); + if(result < 0) { + goto complete; + } + + result = session_record_promote_state(record, new_state); + +complete: + AXOLOTL_UNREF(new_state); + return result; +} + +int session_record_promote_state(session_record *record, session_state *promoted_state) +{ + int count = 0; + session_record_state_node *cur_node = 0; + session_record_state_node *tmp_node = 0; + + assert(record); + assert(promoted_state); + + // Move the previously current state to the list of previous states + if(record->state) { + session_record_state_node *node = malloc(sizeof(session_record_state_node)); + if(!node) { + return AX_ERR_NOMEM; + } + + node->state = record->state; + DL_PREPEND(record->previous_states_head, node); + record->state = 0; + } + + // Make the promoted state the current state + AXOLOTL_REF(promoted_state); + record->state = promoted_state; + + // Remove any previous nodes beyond the maximum length + DL_FOREACH_SAFE(record->previous_states_head, cur_node, tmp_node) { + count++; + if(count > ARCHIVED_STATES_MAX_LENGTH) { + DL_DELETE(record->previous_states_head, cur_node); + if(cur_node->state) { + AXOLOTL_UNREF(cur_node->state); + } + free(cur_node); + } + } + + return 0; +} + +static void session_record_free_previous_states(session_record *record) +{ + session_record_state_node *cur_node; + session_record_state_node *tmp_node; + DL_FOREACH_SAFE(record->previous_states_head, cur_node, tmp_node) { + DL_DELETE(record->previous_states_head, cur_node); + if(cur_node->state) { + AXOLOTL_UNREF(cur_node->state); + } + free(cur_node); + } + record->previous_states_head = 0; +} + +void session_record_destroy(axolotl_type_base *type) +{ + session_record *record = (session_record *)type; + + if(record->state) { + AXOLOTL_UNREF(record->state); + } + session_record_free_previous_states(record); + + free(record); +} diff --git a/libs/libaxolotl/src/session_record.h b/libs/libaxolotl/src/session_record.h new file mode 100644 index 0000000000..99f113d2eb --- /dev/null +++ b/libs/libaxolotl/src/session_record.h @@ -0,0 +1,50 @@ +#ifndef SESSION_RECORD_H +#define SESSION_RECORD_H + +#include <stdint.h> +#include <stddef.h> +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int session_record_create(session_record **record, session_state *state, axolotl_context *global_context); +int session_record_serialize(axolotl_buffer **buffer, const session_record *record); +int session_record_deserialize(session_record **record, const uint8_t *data, size_t len, axolotl_context *global_context); +int session_record_copy(session_record **record, session_record *other_record, axolotl_context *global_context); + +int session_record_has_session_state(session_record *record, uint32_t version, const ec_public_key *alice_base_key); +session_state *session_record_get_state(session_record *record); +void session_record_set_state(session_record *record, session_state *state); + +session_record_state_node *session_record_get_previous_states_head(const session_record *record); +session_state *session_record_get_previous_states_element(const session_record_state_node *node); +session_record_state_node *session_record_get_previous_states_next(const session_record_state_node *node); + +/** + * Removes the specified node in the previous states list. + * @param node the node to remove + * @return the node immediately following the removed node, or null if at the end of the list + */ +session_record_state_node *session_record_get_previous_states_remove(session_record *record, session_record_state_node *node); + +int session_record_is_fresh(session_record *record); + +/** + * Move the current session_state into the list of "previous" session states, + * and replace the current session_state with a fresh reset instance. + * + * @return 0 on success, negative on failure + */ +int session_record_archive_current_state(session_record *record); + +int session_record_promote_state(session_record *record, session_state *promoted_state); + +void session_record_destroy(axolotl_type_base *type); + +#ifdef __cplusplus +} +#endif + +#endif /* SESSION_RECORD_H */ diff --git a/libs/libaxolotl/src/session_state.c b/libs/libaxolotl/src/session_state.c new file mode 100644 index 0000000000..db787ebf72 --- /dev/null +++ b/libs/libaxolotl/src/session_state.c @@ -0,0 +1,1879 @@ +#include "session_state.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "hkdf.h" +#include "curve.h" +#include "ratchet.h" +#include "axolotl_internal.h" +#include "LocalStorageProtocol.pb-c.h" + +#include "utlist.h" + +#define MAX_MESSAGE_KEYS 2000 + +typedef struct message_keys_node +{ + ratchet_message_keys message_key; + struct message_keys_node *prev, *next; +} message_keys_node; + +typedef struct session_state_sender_chain +{ + ec_key_pair *sender_ratchet_key_pair; + ratchet_chain_key *chain_key; + message_keys_node *message_keys_head; +} session_state_sender_chain; + +typedef struct session_state_receiver_chain +{ + ec_public_key *sender_ratchet_key; + ratchet_chain_key *chain_key; + message_keys_node *message_keys_head; + struct session_state_receiver_chain *prev, *next; +} session_state_receiver_chain; + +typedef struct session_pending_key_exchange +{ + uint32_t sequence; + ec_key_pair *local_base_key; + ec_key_pair *local_ratchet_key; + ratchet_identity_key_pair *local_identity_key; +} session_pending_key_exchange; + +typedef struct session_pending_pre_key +{ + int has_pre_key_id; + uint32_t pre_key_id; + uint32_t signed_pre_key_id; + ec_public_key *base_key; +} session_pending_pre_key; + +struct session_state +{ + axolotl_type_base base; + + uint32_t session_version; + ec_public_key *local_identity_public; + ec_public_key *remote_identity_public; + + ratchet_root_key *root_key; + uint32_t previous_counter; + + int has_sender_chain; + session_state_sender_chain sender_chain; + + session_state_receiver_chain *receiver_chain_head; + + int has_pending_key_exchange; + session_pending_key_exchange pending_key_exchange; + + int has_pending_pre_key; + session_pending_pre_key pending_pre_key; + + uint32_t remote_registration_id; + uint32_t local_registration_id; + + int needs_refresh; + ec_public_key *alice_base_key; + + axolotl_context *global_context; +}; + +static int session_state_serialize_prepare_sender_chain( + session_state_sender_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure); +static int session_state_serialize_prepare_receiver_chain( + session_state_receiver_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure); +static void session_state_serialize_prepare_chain_free( + Textsecure__SessionStructure__Chain *chain_structure); +static int session_state_serialize_prepare_chain_chain_key( + ratchet_chain_key *chain_key, + Textsecure__SessionStructure__Chain *chain_structure); +static int session_state_serialize_prepare_chain_message_keys_list( + message_keys_node *message_keys_head, + Textsecure__SessionStructure__Chain *chain_structure); +static int session_state_serialize_prepare_message_keys( + ratchet_message_keys *message_key, + Textsecure__SessionStructure__Chain__MessageKey *message_key_structure); +static void session_state_serialize_prepare_message_keys_free( + Textsecure__SessionStructure__Chain__MessageKey *message_key_structure); +static int session_state_serialize_prepare_pending_key_exchange( + session_pending_key_exchange *exchange, + Textsecure__SessionStructure__PendingKeyExchange *exchange_structure); +static void session_state_serialize_prepare_pending_key_exchange_free( + Textsecure__SessionStructure__PendingKeyExchange *exchange_structure); +static int session_state_serialize_prepare_pending_pre_key( + session_pending_pre_key *pre_key, + Textsecure__SessionStructure__PendingPreKey *pre_key_structure); +static void session_state_serialize_prepare_pending_pre_key_free( + Textsecure__SessionStructure__PendingPreKey *pre_key_structure); + +static int session_state_deserialize_protobuf_pending_key_exchange( + session_pending_key_exchange *result_exchange, + Textsecure__SessionStructure__PendingKeyExchange *exchange_structure, + axolotl_context *global_context); +static int session_state_deserialize_protobuf_pending_pre_key( + session_pending_pre_key *result_pre_key, + Textsecure__SessionStructure__PendingPreKey *pre_key_structure, + axolotl_context *global_context); +static int session_state_deserialize_protobuf_sender_chain( + uint32_t session_version, + session_state_sender_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure, + axolotl_context *global_context); +static int session_state_deserialize_protobuf_receiver_chain( + uint32_t session_version, + session_state_receiver_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure, + axolotl_context *global_context); + +static void session_state_free_sender_chain(session_state *state); +static void session_state_free_receiver_chain_node(session_state_receiver_chain *node); +static void session_state_free_receiver_chain(session_state *state); +static session_state_receiver_chain *session_state_find_receiver_chain(const session_state *state, const ec_public_key *sender_ephemeral); + +int session_state_create(session_state **state, axolotl_context *global_context) +{ + session_state *result = malloc(sizeof(session_state)); + if(!result) { + return AX_ERR_NOMEM; + } + memset(result, 0, sizeof(session_state)); + AXOLOTL_INIT(result, session_state_destroy); + result->session_version = 2; + result->global_context = global_context; + + *state = result; + return 0; +} + +int session_state_serialize(axolotl_buffer **buffer, session_state *state) +{ + int result = 0; + size_t result_size = 0; + Textsecure__SessionStructure *state_structure = 0; + axolotl_buffer *result_buf = 0; + size_t len = 0; + uint8_t *data = 0; + + state_structure = malloc(sizeof(Textsecure__SessionStructure)); + if(!state_structure) { + result = AX_ERR_NOMEM; + goto complete; + } + textsecure__session_structure__init(state_structure); + + result = session_state_serialize_prepare(state, state_structure); + if(result < 0) { + goto complete; + } + + len = textsecure__session_structure__get_packed_size(state_structure); + + result_buf = axolotl_buffer_alloc(len); + if(!result_buf) { + result = AX_ERR_NOMEM; + goto complete; + } + + data = axolotl_buffer_data(result_buf); + result_size = textsecure__session_structure__pack(state_structure, data); + if(result_size != len) { + axolotl_buffer_free(result_buf); + result = AX_ERR_INVALID_PROTO_BUF; + result_buf = 0; + goto complete; + } + +complete: + if(state_structure) { + session_state_serialize_prepare_free(state_structure); + } + if(result >= 0) { + *buffer = result_buf; + } + return result; +} + +int session_state_deserialize(session_state **state, const uint8_t *data, size_t len, axolotl_context *global_context) +{ + int result = 0; + session_state *result_state = 0; + Textsecure__SessionStructure *session_structure = 0; + + session_structure = textsecure__session_structure__unpack(0, len, data); + if(!session_structure) { + result = AX_ERR_INVALID_PROTO_BUF; + goto complete; + } + + result = session_state_deserialize_protobuf(&result_state, session_structure, global_context); + if(result < 0) { + goto complete; + } + +complete: + if(session_structure) { + textsecure__session_structure__free_unpacked(session_structure, 0); + } + if(result_state) { + if(result < 0) { + AXOLOTL_UNREF(result_state); + } + else { + *state = result_state; + } + } + return result; +} + +int session_state_serialize_prepare(session_state *state, Textsecure__SessionStructure *session_structure) +{ + int result = 0; + + assert(state); + assert(session_structure); + + session_structure->has_sessionversion = 1; + session_structure->sessionversion = state->session_version; + + if(state->local_identity_public) { + result = ec_public_key_serialize_protobuf( + &session_structure->localidentitypublic, state->local_identity_public); + if(result < 0) { + goto complete; + } + session_structure->has_localidentitypublic = 1; + } + + if(state->remote_identity_public) { + result = ec_public_key_serialize_protobuf( + &session_structure->remoteidentitypublic, state->remote_identity_public); + if(result < 0) { + goto complete; + } + session_structure->has_remoteidentitypublic = 1; + } + + if(state->root_key) { + result = ratchet_root_key_get_key_protobuf( + state->root_key, &session_structure->rootkey); + if(result < 0) { + goto complete; + } + session_structure->has_rootkey = 1; + } + + session_structure->has_previouscounter = 1; + session_structure->previouscounter = state->previous_counter; + + + if(state->has_sender_chain) { + session_structure->senderchain = malloc(sizeof(Textsecure__SessionStructure__Chain)); + if(!session_structure->senderchain) { + result = AX_ERR_NOMEM; + goto complete; + } + textsecure__session_structure__chain__init(session_structure->senderchain); + result = session_state_serialize_prepare_sender_chain( + &state->sender_chain, session_structure->senderchain); + if(result < 0) { + goto complete; + } + } + + if(state->receiver_chain_head) { + size_t i = 0; + + unsigned int count; + session_state_receiver_chain *cur_node; + DL_COUNT(state->receiver_chain_head, cur_node, count); + + if(count > SIZE_MAX / sizeof(Textsecure__SessionStructure__Chain *)) { + result = AX_ERR_NOMEM; + goto complete; + } + + session_structure->receiverchains = malloc(sizeof(Textsecure__SessionStructure__Chain *) * count); + if(!session_structure->receiverchains) { + result = AX_ERR_NOMEM; + goto complete; + } + + DL_FOREACH(state->receiver_chain_head, cur_node) { + session_structure->receiverchains[i] = malloc(sizeof(Textsecure__SessionStructure__Chain)); + if(!session_structure->receiverchains[i]) { + result = AX_ERR_NOMEM; + break; + } + textsecure__session_structure__chain__init(session_structure->receiverchains[i]); + result = session_state_serialize_prepare_receiver_chain(cur_node, session_structure->receiverchains[i]); + if(result < 0) { + break; + } + i++; + } + session_structure->n_receiverchains = i; + if(result < 0) { + goto complete; + } + } + + if(state->has_pending_key_exchange) { + session_structure->pendingkeyexchange = malloc(sizeof(Textsecure__SessionStructure__PendingKeyExchange)); + if(!session_structure->pendingkeyexchange) { + result = AX_ERR_NOMEM; + goto complete; + } + textsecure__session_structure__pending_key_exchange__init(session_structure->pendingkeyexchange); + result = session_state_serialize_prepare_pending_key_exchange( + &state->pending_key_exchange, + session_structure->pendingkeyexchange); + if(result < 0) { + goto complete; + } + } + + if(state->has_pending_pre_key) { + session_structure->pendingprekey = malloc(sizeof(Textsecure__SessionStructure__PendingPreKey)); + if(!session_structure->pendingprekey) { + result = AX_ERR_NOMEM; + goto complete; + } + textsecure__session_structure__pending_pre_key__init(session_structure->pendingprekey); + result = session_state_serialize_prepare_pending_pre_key( + &state->pending_pre_key, + session_structure->pendingprekey); + if(result < 0) { + goto complete; + } + } + + session_structure->has_remoteregistrationid = 1; + session_structure->remoteregistrationid = state->remote_registration_id; + + session_structure->has_localregistrationid = 1; + session_structure->localregistrationid = state->local_registration_id; + + session_structure->has_needsrefresh = 1; + session_structure->needsrefresh = state->needs_refresh; + + if(state->alice_base_key) { + result = ec_public_key_serialize_protobuf( + &session_structure->alicebasekey, state->alice_base_key); + if(result < 0) { + goto complete; + } + session_structure->has_alicebasekey = 1; + } + +complete: + return result; +} + +static int session_state_serialize_prepare_sender_chain( + session_state_sender_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure) +{ + int result = 0; + + if(chain->sender_ratchet_key_pair) { + ec_public_key *public_key = 0; + ec_private_key *private_key = 0; + + public_key = ec_key_pair_get_public(chain->sender_ratchet_key_pair); + result = ec_public_key_serialize_protobuf(&chain_structure->senderratchetkey, public_key); + if(result < 0) { + goto complete; + } + chain_structure->has_senderratchetkey = 1; + + private_key = ec_key_pair_get_private(chain->sender_ratchet_key_pair); + result = ec_private_key_serialize_protobuf(&chain_structure->senderratchetkeyprivate, private_key); + if(result < 0) { + goto complete; + } + chain_structure->has_senderratchetkeyprivate = 1; + } + + if(chain->chain_key) { + result = session_state_serialize_prepare_chain_chain_key(chain->chain_key, chain_structure); + if(result < 0) { + goto complete; + } + } + + if(chain->message_keys_head) { + result = session_state_serialize_prepare_chain_message_keys_list(chain->message_keys_head, chain_structure); + if(result < 0) { + goto complete; + } + } + +complete: + return result; +} + +static int session_state_serialize_prepare_receiver_chain( + session_state_receiver_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure) +{ + int result = 0; + + if(chain->sender_ratchet_key) { + result = ec_public_key_serialize_protobuf(&chain_structure->senderratchetkey, chain->sender_ratchet_key); + if(result < 0) { + goto complete; + } + chain_structure->has_senderratchetkey = 1; + } + + if(chain->chain_key) { + result = session_state_serialize_prepare_chain_chain_key(chain->chain_key, chain_structure); + if(result < 0) { + goto complete; + } + } + + if(chain->message_keys_head) { + result = session_state_serialize_prepare_chain_message_keys_list(chain->message_keys_head, chain_structure); + if(result < 0) { + goto complete; + } + } + +complete: + return result; +} + +static int session_state_serialize_prepare_chain_chain_key( + ratchet_chain_key *chain_key, + Textsecure__SessionStructure__Chain *chain_structure) +{ + int result = 0; + chain_structure->chainkey = malloc(sizeof(Textsecure__SessionStructure__Chain__ChainKey)); + if(!chain_structure->chainkey) { + result = AX_ERR_NOMEM; + goto complete; + } + textsecure__session_structure__chain__chain_key__init(chain_structure->chainkey); + + chain_structure->chainkey->has_index = 1; + chain_structure->chainkey->index = ratchet_chain_key_get_index(chain_key); + + result = ratchet_chain_key_get_key_protobuf(chain_key, &chain_structure->chainkey->key); + if(result < 0) { + goto complete; + } + chain_structure->chainkey->has_key = 1; + +complete: + return result; +} + +static int session_state_serialize_prepare_chain_message_keys_list( + message_keys_node *message_keys_head, + Textsecure__SessionStructure__Chain *chain_structure) +{ + int result = 0; + size_t i = 0; + + unsigned int count; + message_keys_node *cur_node; + DL_COUNT(message_keys_head, cur_node, count); + + if(count == 0) { + goto complete; + } + + if(count > SIZE_MAX / sizeof(Textsecure__SessionStructure__Chain__MessageKey *)) { + result = AX_ERR_NOMEM; + goto complete; + } + + chain_structure->messagekeys = malloc(sizeof(Textsecure__SessionStructure__Chain__MessageKey *) * count); + if(!chain_structure->messagekeys) { + result = AX_ERR_NOMEM; + goto complete; + } + + DL_FOREACH(message_keys_head, cur_node) { + chain_structure->messagekeys[i] = malloc(sizeof(Textsecure__SessionStructure__Chain__MessageKey)); + if(!chain_structure->messagekeys[i]) { + result = AX_ERR_NOMEM; + break; + } + textsecure__session_structure__chain__message_key__init(chain_structure->messagekeys[i]); + + result = session_state_serialize_prepare_message_keys(&cur_node->message_key, chain_structure->messagekeys[i]); + if(result < 0) { + break; + } + i++; + } + chain_structure->n_messagekeys = i; + if(result < 0) { + goto complete; + } + +complete: + return result; +} + +static int session_state_serialize_prepare_message_keys( + ratchet_message_keys *message_key, + Textsecure__SessionStructure__Chain__MessageKey *message_key_structure) +{ + int result = 0; + + message_key_structure->has_index = 1; + message_key_structure->index = message_key->counter; + + message_key_structure->cipherkey.data = malloc(sizeof(message_key->cipher_key)); + if(!message_key_structure->cipherkey.data) { + result = AX_ERR_NOMEM; + goto complete; + } + memcpy(message_key_structure->cipherkey.data, message_key->cipher_key, sizeof(message_key->cipher_key)); + message_key_structure->cipherkey.len = sizeof(message_key->cipher_key); + message_key_structure->has_cipherkey = 1; + + message_key_structure->mackey.data = malloc(sizeof(message_key->mac_key)); + if(!message_key_structure->mackey.data) { + result = AX_ERR_NOMEM; + goto complete; + } + memcpy(message_key_structure->mackey.data, message_key->mac_key, sizeof(message_key->mac_key)); + message_key_structure->mackey.len = sizeof(message_key->mac_key); + message_key_structure->has_mackey = 1; + + message_key_structure->iv.data = malloc(sizeof(message_key->iv)); + if(!message_key_structure->iv.data) { + result = AX_ERR_NOMEM; + goto complete; + } + memcpy(message_key_structure->iv.data, message_key->iv, sizeof(message_key->iv)); + message_key_structure->iv.len = sizeof(message_key->iv); + message_key_structure->has_iv = 1; + +complete: + return result; +} + +static void session_state_serialize_prepare_message_keys_free( + Textsecure__SessionStructure__Chain__MessageKey *message_key_structure) +{ + if(message_key_structure->has_cipherkey) { + free(message_key_structure->cipherkey.data); + } + if(message_key_structure->has_mackey) { + free(message_key_structure->mackey.data); + } + if(message_key_structure->has_iv) { + free(message_key_structure->iv.data); + } + free(message_key_structure); +} + +static int session_state_serialize_prepare_pending_key_exchange( + session_pending_key_exchange *exchange, + Textsecure__SessionStructure__PendingKeyExchange *exchange_structure) +{ + int result = 0; + + exchange_structure->has_sequence = 1; + exchange_structure->sequence = exchange->sequence; + + if(exchange->local_base_key) { + ec_public_key *public_key = 0; + ec_private_key *private_key = 0; + + public_key = ec_key_pair_get_public(exchange->local_base_key); + result = ec_public_key_serialize_protobuf(&exchange_structure->localbasekey, public_key); + if(result < 0) { + goto complete; + } + exchange_structure->has_localbasekey = 1; + + private_key = ec_key_pair_get_private(exchange->local_base_key); + result = ec_private_key_serialize_protobuf(&exchange_structure->localbasekeyprivate, private_key); + if(result < 0) { + goto complete; + } + exchange_structure->has_localbasekeyprivate = 1; + } + + if(exchange->local_ratchet_key) { + ec_public_key *public_key; + ec_private_key *private_key; + + public_key = ec_key_pair_get_public(exchange->local_ratchet_key); + result = ec_public_key_serialize_protobuf(&exchange_structure->localratchetkey, public_key); + if(result < 0) { + goto complete; + } + exchange_structure->has_localratchetkey = 1; + + private_key = ec_key_pair_get_private(exchange->local_ratchet_key); + result = ec_private_key_serialize_protobuf(&exchange_structure->localratchetkeyprivate, private_key); + if(result < 0) { + goto complete; + } + exchange_structure->has_localratchetkeyprivate = 1; + } + + if(exchange->local_identity_key) { + ec_public_key *public_key; + ec_private_key *private_key; + + public_key = ratchet_identity_key_pair_get_public(exchange->local_identity_key); + result = ec_public_key_serialize_protobuf(&exchange_structure->localidentitykey, public_key); + if(result < 0) { + goto complete; + } + exchange_structure->has_localidentitykey = 1; + + private_key = ratchet_identity_key_pair_get_private(exchange->local_identity_key); + result = ec_private_key_serialize_protobuf(&exchange_structure->localidentitykeyprivate, private_key); + if(result < 0) { + goto complete; + } + exchange_structure->has_localidentitykeyprivate = 1; + } + +complete: + return result; +} + +static int session_state_serialize_prepare_pending_pre_key( + session_pending_pre_key *pre_key, + Textsecure__SessionStructure__PendingPreKey *pre_key_structure) +{ + int result = 0; + + if(pre_key->has_pre_key_id) { + pre_key_structure->has_prekeyid = 1; + pre_key_structure->prekeyid = pre_key->pre_key_id; + } + + pre_key_structure->has_signedprekeyid = 1; + pre_key_structure->signedprekeyid = (int32_t)pre_key->signed_pre_key_id; + + if(pre_key->base_key) { + result = ec_public_key_serialize_protobuf(&pre_key_structure->basekey, pre_key->base_key); + if(result < 0) { + goto complete; + } + pre_key_structure->has_basekey = 1; + } + +complete: + return result; +} + +void session_state_serialize_prepare_free(Textsecure__SessionStructure *session_structure) +{ + assert(session_structure); + + if(session_structure->has_localidentitypublic) { + free(session_structure->localidentitypublic.data); + } + + if(session_structure->has_remoteidentitypublic) { + free(session_structure->remoteidentitypublic.data); + } + + if(session_structure->has_rootkey) { + free(session_structure->rootkey.data); + } + + if(session_structure->senderchain) { + session_state_serialize_prepare_chain_free(session_structure->senderchain); + } + + if(session_structure->receiverchains) { + unsigned int i; + for(i = 0; i < session_structure->n_receiverchains; i++) { + if(session_structure->receiverchains[i]) { + session_state_serialize_prepare_chain_free(session_structure->receiverchains[i]); + } + } + free(session_structure->receiverchains); + } + + if(session_structure->pendingkeyexchange) { + session_state_serialize_prepare_pending_key_exchange_free(session_structure->pendingkeyexchange); + } + + if(session_structure->pendingprekey) { + session_state_serialize_prepare_pending_pre_key_free(session_structure->pendingprekey); + } + + if(session_structure->has_alicebasekey) { + free(session_structure->alicebasekey.data); + } + + free(session_structure); +} + +static void session_state_serialize_prepare_chain_free( + Textsecure__SessionStructure__Chain *chain_structure) +{ + if(chain_structure->has_senderratchetkey) { + free(chain_structure->senderratchetkey.data); + } + if(chain_structure->has_senderratchetkeyprivate) { + free(chain_structure->senderratchetkeyprivate.data); + } + if(chain_structure->chainkey) { + if(chain_structure->chainkey->has_key) { + free(chain_structure->chainkey->key.data); + } + free(chain_structure->chainkey); + } + if(chain_structure->messagekeys) { + unsigned int i; + for(i = 0; i < chain_structure->n_messagekeys; i++) { + if(chain_structure->messagekeys[i]) { + session_state_serialize_prepare_message_keys_free(chain_structure->messagekeys[i]); + } + } + free(chain_structure->messagekeys); + } + free(chain_structure); +} + +static void session_state_serialize_prepare_pending_key_exchange_free( + Textsecure__SessionStructure__PendingKeyExchange *exchange_structure) +{ + if(exchange_structure->has_localbasekey) { + free(exchange_structure->localbasekey.data); + } + if(exchange_structure->has_localbasekeyprivate) { + free(exchange_structure->localbasekeyprivate.data); + } + if(exchange_structure->has_localratchetkey) { + free(exchange_structure->localratchetkey.data); + } + if(exchange_structure->has_localratchetkeyprivate) { + free(exchange_structure->localratchetkeyprivate.data); + } + if(exchange_structure->has_localidentitykey) { + free(exchange_structure->localidentitykey.data); + } + if(exchange_structure->has_localidentitykeyprivate) { + free(exchange_structure->localidentitykeyprivate.data); + } + free(exchange_structure); +} + +static void session_state_serialize_prepare_pending_pre_key_free( + Textsecure__SessionStructure__PendingPreKey *pre_key_structure) +{ + if(pre_key_structure->has_basekey) { + free(pre_key_structure->basekey.data); + } + + free(pre_key_structure); +} + +int session_state_deserialize_protobuf(session_state **state, Textsecure__SessionStructure *session_structure, axolotl_context *global_context) +{ + int result = 0; + session_state *result_state = 0; + + result = session_state_create(&result_state, global_context); + if(result < 0) { + goto complete; + } + + if(session_structure->has_sessionversion) { + result_state->session_version = session_structure->sessionversion; + } + + if(session_structure->has_localidentitypublic) { + result = curve_decode_point( + &result_state->local_identity_public, + session_structure->localidentitypublic.data, + session_structure->localidentitypublic.len, + global_context); + if(result < 0) { + goto complete; + } + } + + if(session_structure->has_remoteidentitypublic) { + result = curve_decode_point( + &result_state->remote_identity_public, + session_structure->remoteidentitypublic.data, + session_structure->remoteidentitypublic.len, + global_context); + if(result < 0) { + goto complete; + } + } + + if(session_structure->has_rootkey) { + hkdf_context *kdf = 0; + result = hkdf_create(&kdf, (int)result_state->session_version, global_context); + if(result < 0) { + goto complete; + } + + result = ratchet_root_key_create( + &result_state->root_key, kdf, + session_structure->rootkey.data, + session_structure->rootkey.len, global_context); + AXOLOTL_UNREF(kdf); + if(result < 0) { + goto complete; + } + } + + if(session_structure->has_previouscounter) { + result_state->previous_counter = session_structure->previouscounter; + } + + if(session_structure->senderchain) { + session_state_deserialize_protobuf_sender_chain( + result_state->session_version, + &result_state->sender_chain, session_structure->senderchain, + global_context); + if(result < 0) { + goto complete; + } + result_state->has_sender_chain = 1; + } + + if(session_structure->n_receiverchains > 0) { + unsigned int i; + for(i = 0; i < session_structure->n_receiverchains; i++) { + session_state_receiver_chain *node = malloc(sizeof(session_state_receiver_chain)); + if(!node) { + result = AX_ERR_NOMEM; + goto complete; + } + memset(node, 0, sizeof(session_state_receiver_chain)); + + result = session_state_deserialize_protobuf_receiver_chain( + result_state->session_version, + node, session_structure->receiverchains[i], + global_context); + if(result < 0) { + free(node); + goto complete; + } + + DL_APPEND(result_state->receiver_chain_head, node); + } + } + + if(session_structure->pendingkeyexchange) { + result = session_state_deserialize_protobuf_pending_key_exchange( + &result_state->pending_key_exchange, + session_structure->pendingkeyexchange, global_context); + if(result < 0) { + goto complete; + } + result_state->has_pending_key_exchange = 1; + } + + if(session_structure->pendingprekey) { + result = session_state_deserialize_protobuf_pending_pre_key( + &result_state->pending_pre_key, + session_structure->pendingprekey, global_context); + if(result < 0) { + goto complete; + } + result_state->has_pending_pre_key = 1; + } + + if(session_structure->has_remoteregistrationid) { + result_state->remote_registration_id = session_structure->remoteregistrationid; + } + + if(session_structure->has_localregistrationid) { + result_state->local_registration_id = session_structure->localregistrationid; + } + + if(session_structure->has_needsrefresh) { + result_state->needs_refresh = session_structure->needsrefresh; + } + + if(session_structure->has_alicebasekey) { + result = curve_decode_point( + &result_state->alice_base_key, + session_structure->alicebasekey.data, + session_structure->alicebasekey.len, + global_context); + if(result < 0) { + goto complete; + } + } + +complete: + if(result >= 0) { + *state = result_state; + } + else { + if(result_state) { + AXOLOTL_UNREF(result_state); + } + } + return result; +} + +static int session_state_deserialize_protobuf_pending_key_exchange( + session_pending_key_exchange *result_exchange, + Textsecure__SessionStructure__PendingKeyExchange *exchange_structure, + axolotl_context *global_context) +{ + int result = 0; + + ec_key_pair *local_base_key = 0; + ec_public_key *local_base_key_public = 0; + ec_private_key *local_base_key_private = 0; + + ec_key_pair *local_ratchet_key = 0; + ec_public_key *local_ratchet_key_public = 0; + ec_private_key *local_ratchet_key_private = 0; + + ratchet_identity_key_pair *local_identity_key = 0; + ec_public_key *local_identity_key_public = 0; + ec_private_key *local_identity_key_private = 0; + + if(exchange_structure->has_localbasekey && exchange_structure->has_localbasekeyprivate) { + result = curve_decode_point(&local_base_key_public, + exchange_structure->localbasekey.data, + exchange_structure->localbasekey.len, + global_context); + if(result < 0) { + goto complete; + } + + result = curve_decode_private_point(&local_base_key_private, + exchange_structure->localbasekeyprivate.data, + exchange_structure->localbasekeyprivate.len, + global_context); + if(result < 0) { + goto complete; + } + + result = ec_key_pair_create(&local_base_key, + local_base_key_public, local_base_key_private); + if(result < 0) { + goto complete; + } + } + + if(exchange_structure->has_localratchetkey && exchange_structure->has_localratchetkeyprivate) { + result = curve_decode_point(&local_ratchet_key_public, + exchange_structure->localratchetkey.data, + exchange_structure->localratchetkey.len, + global_context); + if(result < 0) { + goto complete; + } + + result = curve_decode_private_point(&local_ratchet_key_private, + exchange_structure->localratchetkeyprivate.data, + exchange_structure->localratchetkeyprivate.len, + global_context); + if(result < 0) { + goto complete; + } + + result = ec_key_pair_create(&local_ratchet_key, + local_ratchet_key_public, local_ratchet_key_private); + if(result < 0) { + goto complete; + } + } + + if(exchange_structure->has_localidentitykey && exchange_structure->has_localidentitykeyprivate) { + result = curve_decode_point(&local_identity_key_public, + exchange_structure->localidentitykey.data, + exchange_structure->localidentitykey.len, + global_context); + if(result < 0) { + goto complete; + } + + result = curve_decode_private_point(&local_identity_key_private, + exchange_structure->localidentitykeyprivate.data, + exchange_structure->localidentitykeyprivate.len, + global_context); + if(result < 0) { + goto complete; + } + + result = ratchet_identity_key_pair_create(&local_identity_key, + local_identity_key_public, + local_identity_key_private); + if(result < 0) { + goto complete; + } + } + + result_exchange->sequence = exchange_structure->sequence; + result_exchange->local_base_key = local_base_key; + result_exchange->local_ratchet_key = local_ratchet_key; + result_exchange->local_identity_key = local_identity_key; + +complete: + AXOLOTL_UNREF(local_base_key_public); + AXOLOTL_UNREF(local_base_key_private); + AXOLOTL_UNREF(local_ratchet_key_public); + AXOLOTL_UNREF(local_ratchet_key_private); + AXOLOTL_UNREF(local_identity_key_public); + AXOLOTL_UNREF(local_identity_key_private); + + if(result < 0) { + AXOLOTL_UNREF(local_base_key); + AXOLOTL_UNREF(local_ratchet_key); + AXOLOTL_UNREF(local_identity_key); + } + + return result; +} + +static int session_state_deserialize_protobuf_pending_pre_key( + session_pending_pre_key *result_pre_key, + Textsecure__SessionStructure__PendingPreKey *pre_key_structure, + axolotl_context *global_context) +{ + int result = 0; + + if(pre_key_structure->has_basekey) { + ec_public_key *base_key = 0; + result = curve_decode_point(&base_key, + pre_key_structure->basekey.data, + pre_key_structure->basekey.len, + global_context); + if(result < 0) { + goto complete; + } + result_pre_key->base_key = base_key; + } + + if(pre_key_structure->has_prekeyid) { + result_pre_key->has_pre_key_id = 1; + result_pre_key->pre_key_id = pre_key_structure->prekeyid; + } + + if(pre_key_structure->has_signedprekeyid) { + result_pre_key->signed_pre_key_id = (uint32_t)pre_key_structure->signedprekeyid; + } + +complete: + return result; +} + +static int session_state_deserialize_protobuf_sender_chain( + uint32_t session_version, + session_state_sender_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure, + axolotl_context *global_context) +{ + int result = 0; + hkdf_context *kdf = 0; + ec_key_pair *sender_ratchet_key_pair = 0; + ec_public_key *sender_ratchet_key_public = 0; + ec_private_key *sender_ratchet_key_private = 0; + ratchet_chain_key *sender_chain_key = 0; + + if(chain_structure->has_senderratchetkey && chain_structure->has_senderratchetkeyprivate) { + result = curve_decode_point(&sender_ratchet_key_public, + chain_structure->senderratchetkey.data, + chain_structure->senderratchetkey.len, + global_context); + if(result < 0) { + goto complete; + } + + result = curve_decode_private_point(&sender_ratchet_key_private, + chain_structure->senderratchetkeyprivate.data, + chain_structure->senderratchetkeyprivate.len, + global_context); + if(result < 0) { + goto complete; + } + + result = ec_key_pair_create(&sender_ratchet_key_pair, + sender_ratchet_key_public, sender_ratchet_key_private); + if(result < 0) { + goto complete; + } + } + + if(chain_structure->chainkey && chain_structure->chainkey->has_key && chain_structure->chainkey->has_index) { + result = hkdf_create(&kdf, (int)session_version, global_context); + if(result < 0) { + goto complete; + } + + result = ratchet_chain_key_create( + &sender_chain_key, kdf, + chain_structure->chainkey->key.data, + chain_structure->chainkey->key.len, + chain_structure->chainkey->index, + global_context); + if(result < 0) { + goto complete; + } + } + + chain->sender_ratchet_key_pair = sender_ratchet_key_pair; + chain->chain_key = sender_chain_key; + +complete: + AXOLOTL_UNREF(kdf); + AXOLOTL_UNREF(sender_ratchet_key_public); + AXOLOTL_UNREF(sender_ratchet_key_private); + if(result < 0) { + AXOLOTL_UNREF(sender_ratchet_key_pair); + AXOLOTL_UNREF(sender_chain_key); + } + return result; +} + +static int session_state_deserialize_protobuf_receiver_chain( + uint32_t session_version, + session_state_receiver_chain *chain, + Textsecure__SessionStructure__Chain *chain_structure, + axolotl_context *global_context) +{ + int result = 0; + + hkdf_context *kdf = 0; + ec_public_key *sender_ratchet_key = 0; + ratchet_chain_key *chain_key = 0; + message_keys_node *message_keys_head = 0; + + if(chain_structure->has_senderratchetkey) { + result = curve_decode_point(&sender_ratchet_key, + chain_structure->senderratchetkey.data, + chain_structure->senderratchetkey.len, + global_context); + if(result < 0) { + goto complete; + } + } + + if(chain_structure->chainkey && chain_structure->chainkey->has_key && chain_structure->chainkey->has_index) { + result = hkdf_create(&kdf, (int)session_version, global_context); + if(result < 0) { + goto complete; + } + + result = ratchet_chain_key_create( + &chain_key, kdf, + chain_structure->chainkey->key.data, + chain_structure->chainkey->key.len, + chain_structure->chainkey->index, + global_context); + if(result < 0) { + goto complete; + } + } + + if(chain_structure->n_messagekeys > 0) { + unsigned int i; + for(i = 0; i < chain_structure->n_messagekeys; i++) { + Textsecure__SessionStructure__Chain__MessageKey *key_structure = + chain_structure->messagekeys[i]; + + message_keys_node *node = malloc(sizeof(message_keys_node)); + if(!node) { + result = AX_ERR_NOMEM; + goto complete; + } + memset(node, 0, sizeof(message_keys_node)); + + if(key_structure->has_index) { + node->message_key.counter = key_structure->index; + } + if(key_structure->has_cipherkey && key_structure->cipherkey.len == sizeof(node->message_key.cipher_key)) { + memcpy(node->message_key.cipher_key, key_structure->cipherkey.data, key_structure->cipherkey.len); + } + if(key_structure->has_mackey && key_structure->mackey.len == sizeof(node->message_key.mac_key)) { + memcpy(node->message_key.mac_key, key_structure->mackey.data, key_structure->mackey.len); + } + if(key_structure->has_iv && key_structure->iv.len == sizeof(node->message_key.iv)) { + memcpy(node->message_key.iv, key_structure->iv.data, key_structure->iv.len); + } + + DL_APPEND(message_keys_head, node); + } + } + + chain->sender_ratchet_key = sender_ratchet_key; + chain->chain_key = chain_key; + chain->message_keys_head = message_keys_head; + +complete: + AXOLOTL_UNREF(kdf); + if(result < 0) { + AXOLOTL_UNREF(sender_ratchet_key); + AXOLOTL_UNREF(chain_key); + if(message_keys_head) { + message_keys_node *cur_node; + message_keys_node *tmp_node; + DL_FOREACH_SAFE(message_keys_head, cur_node, tmp_node) { + DL_DELETE(message_keys_head, cur_node); + axolotl_explicit_bzero(&cur_node->message_key, sizeof(ratchet_message_keys)); + free(cur_node); + } + } + } + return result; +} + +int session_state_copy(session_state **state, session_state *other_state, axolotl_context *global_context) +{ + int result = 0; + axolotl_buffer *buffer = 0; + size_t len = 0; + uint8_t *data = 0; + + assert(other_state); + assert(global_context); + + result = session_state_serialize(&buffer, other_state); + if(result < 0) { + goto complete; + } + + data = axolotl_buffer_data(buffer); + len = axolotl_buffer_len(buffer); + + result = session_state_deserialize(state, data, len, global_context); + if(result < 0) { + goto complete; + } + +complete: + if(buffer) { + axolotl_buffer_free(buffer); + } + return result; +} + +void session_state_set_session_version(session_state *state, uint32_t version) +{ + assert(state); + state->session_version = version; +} + +uint32_t session_state_get_session_version(const session_state *state) +{ + assert(state); + return state->session_version; +} + +void session_state_set_local_identity_key(session_state *state, ec_public_key *identity_key) +{ + assert(state); + assert(identity_key); + if(state->local_identity_public) { + AXOLOTL_UNREF(state->local_identity_public); + } + AXOLOTL_REF(identity_key); + state->local_identity_public = identity_key; +} + +ec_public_key *session_state_get_local_identity_key(const session_state *state) +{ + assert(state); + return state->local_identity_public; +} + +void session_state_set_remote_identity_key(session_state *state, ec_public_key *identity_key) +{ + assert(state); + assert(identity_key); + if(state->remote_identity_public) { + AXOLOTL_UNREF(state->remote_identity_public); + } + AXOLOTL_REF(identity_key); + state->remote_identity_public = identity_key; +} + +ec_public_key *session_state_get_remote_identity_key(const session_state *state) +{ + assert(state); + return state->remote_identity_public; +} + +void session_state_set_root_key(session_state *state, ratchet_root_key *root_key) +{ + assert(state); + assert(root_key); + if(state->root_key) { + AXOLOTL_UNREF(state->root_key); + } + AXOLOTL_REF(root_key); + state->root_key = root_key; +} + +ratchet_root_key *session_state_get_root_key(const session_state *state) +{ + assert(state); + return state->root_key; +} + +void session_state_set_previous_counter(session_state *state, uint32_t counter) +{ + assert(state); + state->previous_counter = counter; +} + +uint32_t session_state_get_previous_counter(const session_state *state) +{ + assert(state); + return state->previous_counter; +} + +void session_state_set_sender_chain(session_state *state, ec_key_pair *sender_ratchet_key_pair, ratchet_chain_key *chain_key) +{ + assert(state); + assert(sender_ratchet_key_pair); + assert(chain_key); + + state->has_sender_chain = 1; + + if(state->sender_chain.sender_ratchet_key_pair) { + AXOLOTL_UNREF(state->sender_chain.sender_ratchet_key_pair); + } + AXOLOTL_REF(sender_ratchet_key_pair); + state->sender_chain.sender_ratchet_key_pair = sender_ratchet_key_pair; + + if(state->sender_chain.chain_key) { + AXOLOTL_UNREF(state->sender_chain.chain_key); + } + AXOLOTL_REF(chain_key); + state->sender_chain.chain_key = chain_key; +} + +ec_public_key *session_state_get_sender_ratchet_key(const session_state *state) +{ + assert(state); + if(state->sender_chain.sender_ratchet_key_pair) { + return ec_key_pair_get_public(state->sender_chain.sender_ratchet_key_pair); + } + else { + return 0; + } +} + +ec_key_pair *session_state_get_sender_ratchet_key_pair(const session_state *state) +{ + assert(state); + return state->sender_chain.sender_ratchet_key_pair; +} + +ratchet_chain_key *session_state_get_sender_chain_key(const session_state *state) +{ + assert(state); + return state->sender_chain.chain_key; +} + +int session_state_set_sender_chain_key(session_state *state, ratchet_chain_key *chain_key) +{ + assert(state); + if(state->has_sender_chain) { + if(state->sender_chain.chain_key) { + AXOLOTL_UNREF(state->sender_chain.chain_key); + } + AXOLOTL_REF(chain_key); + state->sender_chain.chain_key = chain_key; + return 0; + } + else { + return AX_ERR_UNKNOWN; + } +} + +int session_state_has_sender_chain(const session_state *state) +{ + assert(state); + return state->has_sender_chain; +} + +int session_state_has_message_keys(session_state *state, ec_public_key *sender_ephemeral, uint32_t counter) +{ + session_state_receiver_chain *chain = 0; + message_keys_node *cur_node = 0; + + assert(state); + assert(sender_ephemeral); + + chain = session_state_find_receiver_chain(state, sender_ephemeral); + if(!chain) { + return 0; + } + + DL_FOREACH(chain->message_keys_head, cur_node) { + if(cur_node->message_key.counter == counter) { + return 1; + } + } + + return 0; +} + +int session_state_remove_message_keys(session_state *state, + ratchet_message_keys *message_keys_result, + ec_public_key *sender_ephemeral, uint32_t counter) +{ + session_state_receiver_chain *chain = 0; + message_keys_node *cur_node = 0; + message_keys_node *tmp_node = 0; + + assert(state); + assert(message_keys_result); + assert(sender_ephemeral); + + chain = session_state_find_receiver_chain(state, sender_ephemeral); + if(!chain) { + return 0; + } + + DL_FOREACH_SAFE(chain->message_keys_head, cur_node, tmp_node) { + if(cur_node->message_key.counter == counter) { + memcpy(message_keys_result, &(cur_node->message_key), sizeof(ratchet_message_keys)); + DL_DELETE(chain->message_keys_head, cur_node); + axolotl_explicit_bzero(&cur_node->message_key, sizeof(ratchet_message_keys)); + free(cur_node); + return 1; + } + } + + return 0; +} + +int session_state_set_message_keys(session_state *state, + ec_public_key *sender_ephemeral, ratchet_message_keys *message_keys) +{ + session_state_receiver_chain *chain = 0; + message_keys_node *node = 0; + int count; + + assert(state); + assert(sender_ephemeral); + assert(message_keys); + + chain = session_state_find_receiver_chain(state, sender_ephemeral); + if(!chain) { + return 0; + } + + node = malloc(sizeof(message_keys_node)); + if(!node) { + return AX_ERR_NOMEM; + } + memcpy(&(node->message_key), message_keys, sizeof(ratchet_message_keys)); + node->prev = 0; + node->next = 0; + + DL_APPEND(chain->message_keys_head, node); + + DL_COUNT(chain->message_keys_head, node, count); + while(count > MAX_MESSAGE_KEYS) { + node = chain->message_keys_head; + DL_DELETE(chain->message_keys_head, node); + axolotl_explicit_bzero(&node->message_key, sizeof(ratchet_message_keys)); + free(node); + --count; + } + + return 0; +} + +int session_state_add_receiver_chain(session_state *state, ec_public_key *sender_ratchet_key, ratchet_chain_key *chain_key) +{ + session_state_receiver_chain *node; + int count; + + assert(state); + assert(sender_ratchet_key); + assert(chain_key); + + node = malloc(sizeof(session_state_receiver_chain)); + if(!node) { + return AX_ERR_NOMEM; + } + memset(node, 0, sizeof(session_state_receiver_chain)); + + AXOLOTL_REF(sender_ratchet_key); + node->sender_ratchet_key = sender_ratchet_key; + AXOLOTL_REF(chain_key); + node->chain_key = chain_key; + + DL_APPEND(state->receiver_chain_head, node); + + DL_COUNT(state->receiver_chain_head, node, count); + while(count > 5) { + node = state->receiver_chain_head; + DL_DELETE(state->receiver_chain_head, node); + session_state_free_receiver_chain_node(node); + --count; + } + + return 0; +} + +int session_state_set_receiver_chain_key(session_state *state, ec_public_key *sender_ephemeral, ratchet_chain_key *chain_key) +{ + int result = 0; + session_state_receiver_chain *node; + + assert(state); + assert(sender_ephemeral); + assert(chain_key); + + node = session_state_find_receiver_chain(state, sender_ephemeral); + if(!node) { + axolotl_log(state->global_context, AX_LOG_WARNING, "Couldn't find receiver chain to set chain key on"); + result = AX_ERR_UNKNOWN; + goto complete; + } + + AXOLOTL_UNREF(node->chain_key); + AXOLOTL_REF(chain_key); + node->chain_key = chain_key; + +complete: + return result; +} + +static session_state_receiver_chain *session_state_find_receiver_chain(const session_state *state, const ec_public_key *sender_ephemeral) +{ + session_state_receiver_chain *result = 0; + + session_state_receiver_chain *cur_node; + DL_FOREACH(state->receiver_chain_head, cur_node) { + if(ec_public_key_compare(cur_node->sender_ratchet_key, sender_ephemeral) == 0) { + result = cur_node; + break; + } + } + + return result; +} + +ratchet_chain_key *session_state_get_receiver_chain_key(session_state *state, ec_public_key *sender_ephemeral) +{ + ratchet_chain_key *result = 0; + session_state_receiver_chain *node = session_state_find_receiver_chain(state, sender_ephemeral); + + if(node) { + result = node->chain_key; + } + + return result; +} + +void session_state_set_pending_key_exchange(session_state *state, + uint32_t sequence, + ec_key_pair *our_base_key, ec_key_pair *our_ratchet_key, + ratchet_identity_key_pair *our_identity_key) +{ + assert(state); + assert(our_base_key); + assert(our_ratchet_key); + assert(our_identity_key); + + if(state->pending_key_exchange.local_base_key) { + AXOLOTL_UNREF(state->pending_key_exchange.local_base_key); + state->pending_key_exchange.local_base_key = 0; + } + if(state->pending_key_exchange.local_ratchet_key) { + AXOLOTL_UNREF(state->pending_key_exchange.local_ratchet_key); + state->pending_key_exchange.local_ratchet_key = 0; + } + if(state->pending_key_exchange.local_identity_key) { + AXOLOTL_UNREF(state->pending_key_exchange.local_identity_key); + state->pending_key_exchange.local_identity_key = 0; + } + + AXOLOTL_REF(our_base_key); + AXOLOTL_REF(our_ratchet_key); + AXOLOTL_REF(our_identity_key); + + state->has_pending_key_exchange = 1; + state->pending_key_exchange.sequence = sequence; + state->pending_key_exchange.local_base_key = our_base_key; + state->pending_key_exchange.local_ratchet_key = our_ratchet_key; + state->pending_key_exchange.local_identity_key = our_identity_key; +} + +uint32_t session_state_get_pending_key_exchange_sequence(session_state *state) +{ + assert(state); + if(state->has_pending_key_exchange) { + return state->pending_key_exchange.sequence; + } + else { + return 0; + } +} + +ec_key_pair *session_state_get_pending_key_exchange_base_key(const session_state *state) +{ + assert(state); + if(state->has_pending_key_exchange) { + return state->pending_key_exchange.local_base_key; + } + else { + return 0; + } +} + +ec_key_pair *session_state_get_pending_key_exchange_ratchet_key(const session_state *state) +{ + assert(state); + if(state->has_pending_key_exchange) { + return state->pending_key_exchange.local_ratchet_key; + } + else { + return 0; + } +} + +ratchet_identity_key_pair *session_state_get_pending_key_exchange_identity_key(const session_state *state) +{ + assert(state); + if(state->has_pending_key_exchange) { + return state->pending_key_exchange.local_identity_key; + } + else { + return 0; + } +} + +int session_state_has_pending_key_exchange(const session_state *state) +{ + assert(state); + return state->has_pending_key_exchange; +} + +void session_state_set_unacknowledged_pre_key_message(session_state *state, + const uint32_t *pre_key_id, uint32_t signed_pre_key_id, ec_public_key *base_key) +{ + assert(state); + assert(base_key); + + if(state->pending_pre_key.base_key) { + AXOLOTL_UNREF(state->pending_pre_key.base_key); + state->pending_pre_key.base_key = 0; + } + + AXOLOTL_REF(base_key); + + state->has_pending_pre_key = 1; + if(pre_key_id) { + state->pending_pre_key.has_pre_key_id = 1; + state->pending_pre_key.pre_key_id = *pre_key_id; + } + else { + state->pending_pre_key.has_pre_key_id = 0; + state->pending_pre_key.pre_key_id = 0; + } + state->pending_pre_key.signed_pre_key_id = signed_pre_key_id; + state->pending_pre_key.base_key = base_key; +} + +int session_state_unacknowledged_pre_key_message_has_pre_key_id(const session_state *state) +{ + assert(state); + return state->pending_pre_key.has_pre_key_id; +} + +uint32_t session_state_unacknowledged_pre_key_message_get_pre_key_id(const session_state *state) +{ + assert(state); + assert(state->pending_pre_key.has_pre_key_id); + return state->pending_pre_key.pre_key_id; +} + +uint32_t session_state_unacknowledged_pre_key_message_get_signed_pre_key_id(const session_state *state) +{ + assert(state); + return state->pending_pre_key.signed_pre_key_id; +} + +ec_public_key *session_state_unacknowledged_pre_key_message_get_base_key(const session_state *state) +{ + assert(state); + return state->pending_pre_key.base_key; +} + +int session_state_has_unacknowledged_pre_key_message(const session_state *state) +{ + assert(state); + return state->has_pending_pre_key; +} + +void session_state_clear_unacknowledged_pre_key_message(session_state *state) +{ + assert(state); + if(state->pending_pre_key.base_key) { + AXOLOTL_UNREF(state->pending_pre_key.base_key); + } + memset(&state->pending_pre_key, 0, sizeof(state->pending_pre_key)); + state->has_pending_pre_key = 0; +} + +void session_state_set_remote_registration_id(session_state *state, uint32_t id) +{ + assert(state); + state->remote_registration_id = id; +} + +uint32_t session_state_get_remote_registration_id(const session_state *state) +{ + assert(state); + return state->remote_registration_id; +} + +void session_state_set_local_registration_id(session_state *state, uint32_t id) +{ + assert(state); + state->local_registration_id = id; +} + +uint32_t session_state_get_local_registration_id(const session_state *state) +{ + assert(state); + return state->local_registration_id; +} + +void session_state_set_needs_refresh(session_state *state, int value) +{ + assert(state); + assert(value == 0 || value == 1); + state->needs_refresh = value; +} + +int session_state_get_needs_refresh(const session_state *state) +{ + assert(state); + return state->needs_refresh; +} + +void session_state_set_alice_base_key(session_state *state, ec_public_key *key) +{ + assert(state); + assert(key); + + if(state->alice_base_key) { + AXOLOTL_UNREF(state->alice_base_key); + } + AXOLOTL_REF(key); + state->alice_base_key = key; +} + +ec_public_key *session_state_get_alice_base_key(const session_state *state) +{ + assert(state); + return state->alice_base_key; +} + +static void session_state_free_sender_chain(session_state *state) +{ + if(state->sender_chain.sender_ratchet_key_pair) { + AXOLOTL_UNREF(state->sender_chain.sender_ratchet_key_pair); + state->sender_chain.sender_ratchet_key_pair = 0; + } + if(state->sender_chain.chain_key) { + AXOLOTL_UNREF(state->sender_chain.chain_key); + state->sender_chain.chain_key = 0; + } + + if(state->sender_chain.message_keys_head) { + message_keys_node *cur_node; + message_keys_node *tmp_node; + DL_FOREACH_SAFE(state->sender_chain.message_keys_head, cur_node, tmp_node) { + DL_DELETE(state->sender_chain.message_keys_head, cur_node); + axolotl_explicit_bzero(&cur_node->message_key, sizeof(ratchet_message_keys)); + free(cur_node); + } + state->sender_chain.message_keys_head = 0; + } +} + +static void session_state_free_receiver_chain_node(session_state_receiver_chain *node) +{ + if(node->sender_ratchet_key) { + AXOLOTL_UNREF(node->sender_ratchet_key); + } + if(node->chain_key) { + AXOLOTL_UNREF(node->chain_key); + } + + if(node->message_keys_head) { + message_keys_node *cur_node; + message_keys_node *tmp_node; + DL_FOREACH_SAFE(node->message_keys_head, cur_node, tmp_node) { + DL_DELETE(node->message_keys_head, cur_node); + axolotl_explicit_bzero(&cur_node->message_key, sizeof(ratchet_message_keys)); + free(cur_node); + } + node->message_keys_head = 0; + } + + free(node); +} + +static void session_state_free_receiver_chain(session_state *state) +{ + session_state_receiver_chain *cur_node; + session_state_receiver_chain *tmp_node; + DL_FOREACH_SAFE(state->receiver_chain_head, cur_node, tmp_node) { + DL_DELETE(state->receiver_chain_head, cur_node); + session_state_free_receiver_chain_node(cur_node); + } + state->receiver_chain_head = 0; +} + +void session_state_destroy(axolotl_type_base *type) +{ + session_state *state = (session_state *)type; + + if(state->local_identity_public) { + AXOLOTL_UNREF(state->local_identity_public); + } + if(state->remote_identity_public) { + AXOLOTL_UNREF(state->remote_identity_public); + } + if(state->root_key) { + AXOLOTL_UNREF(state->root_key); + } + session_state_free_sender_chain(state); + session_state_free_receiver_chain(state); + if(state->has_pending_key_exchange) { + if(state->pending_key_exchange.local_base_key) { + AXOLOTL_UNREF(state->pending_key_exchange.local_base_key); + } + if(state->pending_key_exchange.local_ratchet_key) { + AXOLOTL_UNREF(state->pending_key_exchange.local_ratchet_key); + } + if(state->pending_key_exchange.local_identity_key) { + AXOLOTL_UNREF(state->pending_key_exchange.local_identity_key); + } + } + if(state->has_pending_pre_key) { + if(state->pending_pre_key.base_key) { + AXOLOTL_UNREF(state->pending_pre_key.base_key); + } + } + if(state->alice_base_key) { + AXOLOTL_UNREF(state->alice_base_key); + } + free(state); +} diff --git a/libs/libaxolotl/src/session_state.h b/libs/libaxolotl/src/session_state.h new file mode 100644 index 0000000000..119e523fc9 --- /dev/null +++ b/libs/libaxolotl/src/session_state.h @@ -0,0 +1,89 @@ +#ifndef SESSION_STATE_H +#define SESSION_STATE_H + +#include <stdint.h> +#include <stddef.h> +#include "axolotl_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------------------------------*/ + +int session_state_create(session_state **state, axolotl_context *global_context); +int session_state_serialize(axolotl_buffer **buffer, session_state *state); +int session_state_deserialize(session_state **state, const uint8_t *data, size_t len, axolotl_context *global_context); +int session_state_copy(session_state **state, session_state *other_state, axolotl_context *global_context); + +void session_state_set_session_version(session_state *state, uint32_t version); +uint32_t session_state_get_session_version(const session_state *state); + +void session_state_set_local_identity_key(session_state *state, ec_public_key *identity_key); +ec_public_key *session_state_get_local_identity_key(const session_state *state); + +void session_state_set_remote_identity_key(session_state *state, ec_public_key *identity_key); +ec_public_key *session_state_get_remote_identity_key(const session_state *state); + +void session_state_set_root_key(session_state *state, ratchet_root_key *root_key); +ratchet_root_key *session_state_get_root_key(const session_state *state); + +void session_state_set_previous_counter(session_state *state, uint32_t counter); +uint32_t session_state_get_previous_counter(const session_state *state); + +void session_state_set_sender_chain(session_state *state, ec_key_pair *sender_ratchet_key_pair, ratchet_chain_key *chain_key); +ec_public_key *session_state_get_sender_ratchet_key(const session_state *state); +ec_key_pair *session_state_get_sender_ratchet_key_pair(const session_state *state); +ratchet_chain_key *session_state_get_sender_chain_key(const session_state *state); +int session_state_set_sender_chain_key(session_state *state, ratchet_chain_key *chain_key); +int session_state_has_sender_chain(const session_state *state); + +int session_state_has_message_keys(session_state *state, ec_public_key *sender_ephemeral, uint32_t counter); +int session_state_remove_message_keys(session_state *state, + ratchet_message_keys *message_keys_result, + ec_public_key *sender_ephemeral, uint32_t counter); +int session_state_set_message_keys(session_state *state, + ec_public_key *sender_ephemeral, ratchet_message_keys *message_keys); + +int session_state_add_receiver_chain(session_state *state, ec_public_key *sender_ratchet_key, ratchet_chain_key *chain_key); +int session_state_set_receiver_chain_key(session_state *state, ec_public_key *sender_ephemeral, ratchet_chain_key *chain_key); +ratchet_chain_key *session_state_get_receiver_chain_key(session_state *state, ec_public_key *sender_ephemeral); + +void session_state_set_pending_key_exchange(session_state *state, + uint32_t sequence, + ec_key_pair *our_base_key, ec_key_pair *our_ratchet_key, + ratchet_identity_key_pair *our_identity_key); +uint32_t session_state_get_pending_key_exchange_sequence(session_state *state); +ec_key_pair *session_state_get_pending_key_exchange_base_key(const session_state *state); +ec_key_pair *session_state_get_pending_key_exchange_ratchet_key(const session_state *state); +ratchet_identity_key_pair *session_state_get_pending_key_exchange_identity_key(const session_state *state); +int session_state_has_pending_key_exchange(const session_state *state); + +void session_state_set_unacknowledged_pre_key_message(session_state *state, + const uint32_t *pre_key_id, uint32_t signed_pre_key_id, ec_public_key *base_key); +int session_state_unacknowledged_pre_key_message_has_pre_key_id(const session_state *state); +uint32_t session_state_unacknowledged_pre_key_message_get_pre_key_id(const session_state *state); +uint32_t session_state_unacknowledged_pre_key_message_get_signed_pre_key_id(const session_state *state); +ec_public_key *session_state_unacknowledged_pre_key_message_get_base_key(const session_state *state); +int session_state_has_unacknowledged_pre_key_message(const session_state *state); +void session_state_clear_unacknowledged_pre_key_message(session_state *state); + +void session_state_set_remote_registration_id(session_state *state, uint32_t id); +uint32_t session_state_get_remote_registration_id(const session_state *state); + +void session_state_set_local_registration_id(session_state *state, uint32_t id); +uint32_t session_state_get_local_registration_id(const session_state *state); + +void session_state_set_needs_refresh(session_state *state, int value); +int session_state_get_needs_refresh(const session_state *state); + +void session_state_set_alice_base_key(session_state *state, ec_public_key *key); +ec_public_key *session_state_get_alice_base_key(const session_state *state); + +void session_state_destroy(axolotl_type_base *type); + +#ifdef __cplusplus +} +#endif + +#endif /* SESSION_STATE_H */ diff --git a/libs/libaxolotl/src/stdafx.cxx b/libs/libaxolotl/src/stdafx.cxx new file mode 100644 index 0000000000..0801609375 --- /dev/null +++ b/libs/libaxolotl/src/stdafx.cxx @@ -0,0 +1,19 @@ +/*
+
+Copyright (C) 2012-16 Miranda NG team (http://miranda-ng.org)
+
+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 version 2
+of the License.
+
+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/>.
+*/
+
+#include "stdafx.h"
\ No newline at end of file diff --git a/libs/libaxolotl/src/stdafx.h b/libs/libaxolotl/src/stdafx.h new file mode 100644 index 0000000000..7d01dea2a1 --- /dev/null +++ b/libs/libaxolotl/src/stdafx.h @@ -0,0 +1,41 @@ +/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (ñ) 2012-16 Miranda NG project (http://miranda-ng.org),
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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 2
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <windows.h>
+
+#include <malloc.h>
+#include <stdio.h>
+#include <time.h>
+#include <stddef.h>
+#include <process.h>
+#include <io.h>
+#include <limits.h>
+#include <string.h>
+#include <locale.h>
+#include <direct.h>
+
+#include <m_system.h>
+#include <m_system_cpp.h>
+
+#include <m_string.h>
+#include <m_json.h>
diff --git a/libs/libaxolotl/src/utarray.h b/libs/libaxolotl/src/utarray.h new file mode 100644 index 0000000000..75e9dc5ed9 --- /dev/null +++ b/libs/libaxolotl/src/utarray.h @@ -0,0 +1,232 @@ +/* +Copyright (c) 2008-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* a dynamic array implementation using macros + */ +#ifndef UTARRAY_H +#define UTARRAY_H + +#define UTARRAY_VERSION 1.9.9 + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((__unused__)) +#else +#define _UNUSED_ +#endif + +#include <stddef.h> /* size_t */ +#include <string.h> /* memset, etc */ +#include <stdlib.h> /* exit */ + +#define oom() exit(-1) + +typedef void (ctor_f)(void *dst, const void *src); +typedef void (dtor_f)(void *elt); +typedef void (init_f)(void *elt); +typedef struct { + size_t sz; + init_f *init; + ctor_f *copy; + dtor_f *dtor; +} UT_icd; + +typedef struct { + unsigned i,n;/* i: index of next available slot, n: num slots */ + UT_icd icd; /* initializer, copy and destructor functions */ + char *d; /* n slots of size icd->sz*/ +} UT_array; + +#define utarray_init(a,_icd) do { \ + memset(a,0,sizeof(UT_array)); \ + (a)->icd=*_icd; \ +} while(0) + +#define utarray_done(a) do { \ + if ((a)->n) { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ + } \ + } \ + free((a)->d); \ + } \ + (a)->n=0; \ +} while(0) + +#define utarray_new(a,_icd) do { \ + a=(UT_array*)malloc(sizeof(UT_array)); \ + utarray_init(a,_icd); \ +} while(0) + +#define utarray_free(a) do { \ + utarray_done(a); \ + free(a); \ +} while(0) + +#define utarray_reserve(a,by) do { \ + if (((a)->i+by) > ((a)->n)) { \ + while(((a)->i+by) > ((a)->n)) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ + if ( ((a)->d=(char*)realloc((a)->d, (a)->n*(a)->icd.sz)) == NULL) oom(); \ + } \ +} while(0) + +#define utarray_push_back(a,p) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \ + else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \ +} while(0) + +#define utarray_pop_back(a) do { \ + if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \ + else { (a)->i--; } \ +} while(0) + +#define utarray_extend_back(a) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \ + else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \ + (a)->i++; \ +} while(0) + +#define utarray_len(a) ((a)->i) + +#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL) +#define _utarray_eltptr(a,j) ((char*)((a)->d + ((a)->icd.sz*(j) ))) + +#define utarray_insert(a,p,j) do { \ + if (j > (a)->i) utarray_resize(a,j); \ + utarray_reserve(a,1); \ + if ((j) < (a)->i) { \ + memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \ + else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \ + (a)->i++; \ +} while(0) + +#define utarray_inserta(a,w,j) do { \ + if (utarray_len(w) == 0) break; \ + if (j > (a)->i) utarray_resize(a,j); \ + utarray_reserve(a,utarray_len(w)); \ + if ((j) < (a)->i) { \ + memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \ + _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { \ + size_t _ut_i; \ + for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \ + (a)->icd.copy(_utarray_eltptr(a,j+_ut_i), _utarray_eltptr(w,_ut_i)); \ + } \ + } else { \ + memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \ + utarray_len(w)*((a)->icd.sz)); \ + } \ + (a)->i += utarray_len(w); \ +} while(0) + +#define utarray_resize(dst,num) do { \ + size_t _ut_i; \ + if (dst->i > (size_t)(num)) { \ + if ((dst)->icd.dtor) { \ + for(_ut_i=num; _ut_i < dst->i; _ut_i++) { \ + (dst)->icd.dtor(utarray_eltptr(dst,_ut_i)); \ + } \ + } \ + } else if (dst->i < (size_t)(num)) { \ + utarray_reserve(dst,num-dst->i); \ + if ((dst)->icd.init) { \ + for(_ut_i=dst->i; _ut_i < num; _ut_i++) { \ + (dst)->icd.init(utarray_eltptr(dst,_ut_i)); \ + } \ + } else { \ + memset(_utarray_eltptr(dst,dst->i),0,(dst)->icd.sz*(num-dst->i)); \ + } \ + } \ + dst->i = num; \ +} while(0) + +#define utarray_concat(dst,src) do { \ + utarray_inserta((dst),(src),utarray_len(dst)); \ +} while(0) + +#define utarray_erase(a,pos,len) do { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < len; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr((a),pos+_ut_i)); \ + } \ + } \ + if ((a)->i > (pos+len)) { \ + memmove( _utarray_eltptr((a),pos), _utarray_eltptr((a),pos+len), \ + (((a)->i)-(pos+len))*((a)->icd.sz)); \ + } \ + (a)->i -= (len); \ +} while(0) + +#define utarray_renew(a,u) do { \ + if (a) utarray_clear(a); \ + else utarray_new((a),(u)); \ +} while(0) + +#define utarray_clear(a) do { \ + if ((a)->i > 0) { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ + } \ + } \ + (a)->i = 0; \ + } \ +} while(0) + +#define utarray_sort(a,cmp) do { \ + qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \ +} while(0) + +#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp) + +#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL) +#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL)) +#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL)) +#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) +#define utarray_eltidx(a,e) (((char*)(e) >= (char*)((a)->d)) ? (((char*)(e) - (char*)((a)->d))/(size_t)(a)->icd.sz) : -1) + +/* last we pre-define a few icd for common utarrays of ints and strings */ +static void utarray_str_cpy(void *dst, const void *src) { + char **_src = (char**)src, **_dst = (char**)dst; + *_dst = (*_src == NULL) ? NULL : strdup(*_src); +} +static void utarray_str_dtor(void *elt) { + char **eltc = (char**)elt; + if (*eltc) free(*eltc); +} +static const UT_icd ut_str_icd _UNUSED_ = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; +static const UT_icd ut_int_icd _UNUSED_ = {sizeof(int),NULL,NULL,NULL}; +static const UT_icd ut_ptr_icd _UNUSED_ = {sizeof(void*),NULL,NULL,NULL}; + + +#endif /* UTARRAY_H */ diff --git a/libs/libaxolotl/src/uthash.h b/libs/libaxolotl/src/uthash.h new file mode 100644 index 0000000000..7205c67efa --- /dev/null +++ b/libs/libaxolotl/src/uthash.h @@ -0,0 +1,958 @@ +/* +Copyright (c) 2003-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#include <string.h> /* memcmp,strlen */ +#include <stddef.h> /* ptrdiff_t */ +#include <stdlib.h> /* exit() */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define DECLTYPE(x) +#endif +#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#define DECLTYPE(x) +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while(0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while(0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ +#if defined (_WIN32) +#if defined(_MSC_VER) && _MSC_VER >= 1600 +#include <stdint.h> +#elif defined(__WATCOMC__) +#include <stdint.h> +#else +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#endif +#else +#include <stdint.h> +#endif + +#define UTHASH_VERSION 1.9.9 + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ +#endif +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhe */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + unsigned _hf_bkt,_hf_hashv; \ + out=NULL; \ + if (head) { \ + HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ + keyptr,keylen,out); \ + } \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) +#define HASH_BLOOM_MAKE(tbl) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ + memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#else +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0 +#endif + +#define HASH_MAKE_TABLE(hh,head) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ + sizeof(UT_hash_table)); \ + if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl->buckets, 0, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ +} while(0) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + replaced=NULL; \ + HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \ + if (replaced!=NULL) { \ + HASH_DELETE(hh,head,replaced); \ + }; \ + HASH_ADD(hh,head,fieldname,keylen_in,add); \ +} while(0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.next = NULL; \ + (add)->hh.key = (char*)(keyptr); \ + (add)->hh.keylen = (unsigned)(keylen_in); \ + if (!(head)) { \ + head = (add); \ + (head)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh,head); \ + } else { \ + (head)->hh.tbl->tail->next = (add); \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail = &((add)->hh); \ + } \ + (head)->hh.tbl->num_items++; \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ + (add)->hh.hashv, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ + HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ + HASH_FSCK(hh,head); \ +} while(0) + +#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1)); \ +} while(0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ +do { \ + unsigned _hd_bkt; \ + struct UT_hash_handle *_hd_hh_del; \ + if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + head = NULL; \ + } else { \ + _hd_hh_del = &((delptr)->hh); \ + if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ + (head)->hh.tbl->tail = \ + (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho); \ + } \ + if ((delptr)->hh.prev) { \ + ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ + } else { \ + DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ + } \ + if (_hd_hh_del->next) { \ + ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ + (head)->hh.tbl->hho))->prev = \ + _hd_hh_del->prev; \ + } \ + HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh,head); \ +} while (0) + + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ + HASH_FIND(hh,head,findstr,strlen(findstr),out) +#define HASH_ADD_STR(head,strfield,add) \ + HASH_ADD(hh,head,strfield[0],strlen(add->strfield),add) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ + HASH_REPLACE(hh,head,strfield[0],strlen(add->strfield),add,replaced) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head) \ +do { \ + unsigned _bkt_i; \ + unsigned _count, _bkt_count; \ + char *_prev; \ + struct UT_hash_handle *_thh; \ + if (head) { \ + _count = 0; \ + for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ + _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("invalid hh_prev %p, actual %p\n", \ + _thh->hh_prev, _prev ); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("invalid bucket count %d, actual %d\n", \ + (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid hh item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + /* traverse hh in app order; check next/prev integrity, count */ \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev !=(char*)(_thh->prev)) { \ + HASH_OOPS("invalid prev %p, actual %p\n", \ + _thh->prev, _prev ); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ + (head)->hh.tbl->hho) : NULL ); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid app item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include <unistd.h> to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hb_keylen=keylen; \ + char *_hb_key=(char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen--) { (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; } \ + bkt = (hashv) & (num_bkts-1); \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _sx_i; \ + char *_hs_key=(char*)(key); \ + hashv = 0; \ + for(_sx_i=0; _sx_i < keylen; _sx_i++) \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + bkt = hashv & (num_bkts-1); \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _fn_i; \ + char *_hf_key=(char*)(key); \ + hashv = 2166136261UL; \ + for(_fn_i=0; _fn_i < keylen; _fn_i++) \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619; \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _ho_i; \ + char *_ho_key=(char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned char *_hj_key=(unsigned char*)(key); \ + hashv = 0xfeedbeef; \ + _hj_i = _hj_j = 0x9e3779b9; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12; \ + } \ + hashv += keylen; \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ + case 5: _hj_j += _hj_key[4]; \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned char *_sfh_key=(unsigned char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = keylen; \ + \ + int _sfh_rem = _sfh_len & 3; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabe; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = (uint32_t)(get16bits (_sfh_key+2)) << 11 ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)] << 18); \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * MurmurHash uses the faster approach only on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) +#define MUR_GETBLOCK(p,i) p[i] +#else /* non intel */ +#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0) +#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1) +#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2) +#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3) +#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) +#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) +#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) +#else /* assume little endian non-intel */ +#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) +#endif +#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ + (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ + (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ + MUR_ONE_THREE(p)))) +#endif +#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +#define MUR_FMIX(_h) \ +do { \ + _h ^= _h >> 16; \ + _h *= 0x85ebca6b; \ + _h ^= _h >> 13; \ + _h *= 0xc2b2ae35l; \ + _h ^= _h >> 16; \ +} while(0) + +#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ +do { \ + const uint8_t *_mur_data = (const uint8_t*)(key); \ + const int _mur_nblocks = (keylen) / 4; \ + uint32_t _mur_h1 = 0xf88D5353; \ + uint32_t _mur_c1 = 0xcc9e2d51; \ + uint32_t _mur_c2 = 0x1b873593; \ + uint32_t _mur_k1 = 0; \ + const uint8_t *_mur_tail; \ + const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \ + int _mur_i; \ + for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \ + _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + \ + _mur_h1 ^= _mur_k1; \ + _mur_h1 = MUR_ROTL32(_mur_h1,13); \ + _mur_h1 = _mur_h1*5+0xe6546b64; \ + } \ + _mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \ + _mur_k1=0; \ + switch((keylen) & 3) { \ + case 3: _mur_k1 ^= _mur_tail[2] << 16; \ + case 2: _mur_k1 ^= _mur_tail[1] << 8; \ + case 1: _mur_k1 ^= _mur_tail[0]; \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + _mur_h1 ^= _mur_k1; \ + } \ + _mur_h1 ^= (keylen); \ + MUR_FMIX(_mur_h1); \ + hashv = _mur_h1; \ + bkt = hashv & (num_bkts-1); \ +} while(0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* key comparison function; return 0 if keys equal */ +#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ +do { \ + if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ + else out=NULL; \ + while (out) { \ + if ((out)->hh.keylen == keylen_in) { \ + if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \ + } \ + if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \ + else out = NULL; \ + } \ +} while(0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,addhh) \ +do { \ + head.count++; \ + (addhh)->hh_next = head.hh_head; \ + (addhh)->hh_prev = NULL; \ + if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ + (head).hh_head=addhh; \ + if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ + && (addhh)->tbl->noexpand != 1) { \ + HASH_EXPAND_BUCKETS((addhh)->tbl); \ + } \ +} while(0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(hh,head,hh_del) \ + (head).count--; \ + if ((head).hh_head == hh_del) { \ + (head).hh_head = hh_del->hh_next; \ + } \ + if (hh_del->hh_prev) { \ + hh_del->hh_prev->hh_next = hh_del->hh_next; \ + } \ + if (hh_del->hh_next) { \ + hh_del->hh_next->hh_prev = hh_del->hh_prev; \ + } + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(tbl) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ + memset(_he_new_buckets, 0, \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + tbl->ideal_chain_maxlen = \ + (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ + ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ + tbl->nonideal_items = 0; \ + for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ + if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ + tbl->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / \ + tbl->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ + _he_thh; \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + tbl->num_buckets *= 2; \ + tbl->log2_num_buckets++; \ + tbl->buckets = _he_new_buckets; \ + tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ + (tbl->ineff_expands+1) : 0; \ + if (tbl->ineff_expands > 1) { \ + tbl->noexpand=1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ +} while(0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ + _hs_psize++; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + if (! (_hs_q) ) break; \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ + if (_hs_psize == 0) { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ + _hs_e = _hs_p; \ + if (_hs_p){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else if (( \ + cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ + ) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail ) { \ + _hs_tail->next = ((_hs_e) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e) { \ + _hs_e->prev = ((_hs_tail) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail){ \ + _hs_tail->next = NULL; \ + } \ + if ( _hs_nmerges <= 1 ) { \ + _hs_looping=0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2; \ + } \ + HASH_FSCK(hh,head); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt=NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if (src) { \ + for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ + if (!dst) { \ + DECLTYPE_ASSIGN(dst,_elt); \ + HASH_MAKE_TABLE(hh_dst,dst); \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ + (dst)->hh_dst.tbl->num_items++; \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst,dst); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if (head) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)=NULL; \ + } \ +} while(0) + +#define HASH_OVERHEAD(hh,head) \ + (size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + (sizeof(UT_hash_table)) + \ + (HASH_BLOOM_BYTELEN))) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1 +#define HASH_BLOOM_SIGNATURE 0xb12220f2 + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + char bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/libs/libaxolotl/src/utlist.h b/libs/libaxolotl/src/utlist.h new file mode 100644 index 0000000000..b5f3f04c10 --- /dev/null +++ b/libs/libaxolotl/src/utlist.h @@ -0,0 +1,757 @@ +/* +Copyright (c) 2007-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 1.9.9 + +#include <assert.h> + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ code), this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define LDECLTYPE(x) char* +#endif +#elif defined(__ICCARM__) +#define NO_DECLTYPE +#define LDECLTYPE(x) char* +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define _NEXT(elt,list,next) ((char*)((list)->next)) +#define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +/* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */ +#define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define _SV(elt,list) +#define _NEXT(elt,list,next) ((elt)->next) +#define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to) +/* #define _PREV(elt,list,prev) ((elt)->prev) */ +#define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) +#define _RS(list) +#define _CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ + LL_SORT2(list, cmp, next) + +#define LL_SORT2(list, cmp, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ + } \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + + +#define DL_SORT(list, cmp) \ + DL_SORT2(list, cmp, prev, next) + +#define DL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev, _ls_tail); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +#define CDL_SORT(list, cmp) \ + CDL_SORT2(list, cmp, prev, next) + +#define CDL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); \ + if (_NEXT(_ls_q,list,next) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = _NEXT(_ls_q,list,next); \ + } \ + _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ + _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ + _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev,_ls_tail); \ + _CASTASGN(_tmp,list); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ + LL_PREPEND2(head,add,next) + +#define LL_PREPEND2(head,add,next) \ +do { \ + (add)->next = head; \ + head = add; \ +} while (0) + +#define LL_CONCAT(head1,head2) \ + LL_CONCAT2(head1,head2,next) + +#define LL_CONCAT2(head1,head2,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head1) { \ + _tmp = head1; \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(head2); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#define LL_APPEND(head,add) \ + LL_APPEND2(head,add,next) + +#define LL_APPEND2(head,add,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = head; \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_DELETE(head,del) \ + LL_DELETE2(head,del,next) + +#define LL_DELETE2(head,del,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = head; \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = ((del)->next); \ + } \ + } \ +} while (0) + +/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ +#define LL_APPEND_VS2008(head,add) \ + LL_APPEND2_VS2008(head,add,next) + +#define LL_APPEND2_VS2008(head,add,next) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#define LL_DELETE_VS2008(head,del) \ + LL_DELETE2_VS2008(head,del,next) + +#define LL_DELETE2_VS2008(head,del,next) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && ((head)->next != (del))) { \ + head = (head)->next; \ + } \ + if ((head)->next) { \ + (head)->next = ((del)->next); \ + } \ + { \ + char **_head_alias = (char**)&(head); \ + *_head_alias = _tmp; \ + } \ + } \ +} while (0) +#ifdef NO_DECLTYPE +#undef LL_APPEND +#define LL_APPEND LL_APPEND_VS2008 +#undef LL_DELETE +#define LL_DELETE LL_DELETE_VS2008 +#undef LL_DELETE2 +#define LL_DELETE2 LL_DELETE2_VS2008 +#undef LL_APPEND2 +#define LL_APPEND2 LL_APPEND2_VS2008 +#undef LL_CONCAT /* no LL_CONCAT_VS2008 */ +#undef DL_CONCAT /* no DL_CONCAT_VS2008 */ +#endif +/* end VS2008 replacements */ + +#define LL_COUNT(head,el,counter) \ + LL_COUNT2(head,el,counter,next) \ + +#define LL_COUNT2(head,el,counter,next) \ +{ \ + counter = 0; \ + LL_FOREACH2(head,el,next){ ++counter; } \ +} + +#define LL_FOREACH(head,el) \ + LL_FOREACH2(head,el,next) + +#define LL_FOREACH2(head,el,next) \ + for(el=head;el;el=(el)->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + LL_FOREACH_SAFE2(head,el,tmp,next) + +#define LL_FOREACH_SAFE2(head,el,tmp,next) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ + LL_SEARCH_SCALAR2(head,out,field,val,next) + +#define LL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define LL_SEARCH(head,out,elt,cmp) \ + LL_SEARCH2(head,out,elt,cmp,next) + +#define LL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +#define LL_REPLACE_ELEM(head, el, add) \ +do { \ + LDECLTYPE(head) _tmp; \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + (add)->next = (el)->next; \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = head; \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ +} while (0) + +#define LL_PREPEND_ELEM(head, el, add) \ +do { \ + LDECLTYPE(head) _tmp; \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + (add)->next = (el); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = head; \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ +} while (0) \ + + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ + DL_PREPEND2(head,add,prev,next) + +#define DL_PREPEND2(head,add,prev,next) \ +do { \ + (add)->next = head; \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ + DL_APPEND2(head,add,prev,next) + +#define DL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_CONCAT(head1,head2) \ + DL_CONCAT2(head1,head2,prev,next) + +#define DL_CONCAT2(head1,head2,prev,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head2) { \ + if (head1) { \ + _tmp = (head2)->prev; \ + (head2)->prev = (head1)->prev; \ + (head1)->prev->next = (head2); \ + (head1)->prev = _tmp; \ + } else { \ + (head1)=(head2); \ + } \ + } \ +} while (0) + +#define DL_DELETE(head,del) \ + DL_DELETE2(head,del,prev,next) + +#define DL_DELETE2(head,del,prev,next) \ +do { \ + assert((del)->prev != NULL); \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del)==(head)) { \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0) + +#define DL_COUNT(head,el,counter) \ + DL_COUNT2(head,el,counter,next) \ + +#define DL_COUNT2(head,el,counter,next) \ +{ \ + counter = 0; \ + DL_FOREACH2(head,el,next){ ++counter; } \ +} + +#define DL_FOREACH(head,el) \ + DL_FOREACH2(head,el,next) + +#define DL_FOREACH2(head,el,next) \ + for(el=head;el;el=(el)->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + DL_FOREACH_SAFE2(head,el,tmp,next) + +#define DL_FOREACH_SAFE2(head,el,tmp,next) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH +#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 +#define DL_SEARCH2 LL_SEARCH2 + +#define DL_REPLACE_ELEM(head, el, add) \ +do { \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + (add)->next = (el)->next; \ + if ((el)->next == NULL) { \ + (add)->prev = (add); \ + } else { \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + } \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->prev->next = (add); \ + if ((el)->next == NULL) { \ + (head)->prev = (add); \ + } else { \ + (add)->next->prev = (add); \ + } \ + } \ +} while (0) + +#define DL_PREPEND_ELEM(head, el, add) \ +do { \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->prev->next = (add); \ + } \ +} while (0) \ + + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_PREPEND(head,add) \ + CDL_PREPEND2(head,add,prev,next) + +#define CDL_PREPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ +(head)=(add); \ +} while (0) + +#define CDL_DELETE(head,del) \ + CDL_DELETE2(head,del,prev,next) + +#define CDL_DELETE2(head,del,prev,next) \ +do { \ + if ( ((head)==(del)) && ((head)->next == (head))) { \ + (head) = 0L; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0) + +#define CDL_COUNT(head,el,counter) \ + CDL_COUNT2(head,el,counter,next) \ + +#define CDL_COUNT2(head, el, counter,next) \ +{ \ + counter = 0; \ + CDL_FOREACH2(head,el,next){ ++counter; } \ +} + +#define CDL_FOREACH(head,el) \ + CDL_FOREACH2(head,el,next) + +#define CDL_FOREACH2(head,el,next) \ + for(el=head;el;el=((el)->next==head ? 0L : (el)->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) + +#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ + for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ + (el) && ((tmp2)=(el)->next, 1); \ + ((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ + CDL_SEARCH_SCALAR2(head,out,field,val,next) + +#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define CDL_SEARCH(head,out,elt,cmp) \ + CDL_SEARCH2(head,out,elt,cmp,next) + +#define CDL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +#define CDL_REPLACE_ELEM(head, el, add) \ +do { \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + if ((el)->next == (el)) { \ + (add)->next = (add); \ + (add)->prev = (add); \ + (head) = (add); \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } \ +} while (0) + +#define CDL_PREPEND_ELEM(head, el, add) \ +do { \ + assert(head != NULL); \ + assert(el != NULL); \ + assert(add != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ +} while (0) \ + +#endif /* UTLIST_H */ + diff --git a/libs/libaxolotl/src/utstring.h b/libs/libaxolotl/src/utstring.h new file mode 100644 index 0000000000..867442c843 --- /dev/null +++ b/libs/libaxolotl/src/utstring.h @@ -0,0 +1,393 @@ +/* +Copyright (c) 2008-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* a dynamic string implementation using macros + */ +#ifndef UTSTRING_H +#define UTSTRING_H + +#define UTSTRING_VERSION 1.9.9 + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((__unused__)) +#else +#define _UNUSED_ +#endif + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> +#define oom() exit(-1) + +typedef struct { + char *d; + size_t n; /* allocd size */ + size_t i; /* index of first unused byte */ +} UT_string; + +#define utstring_reserve(s,amt) \ +do { \ + if (((s)->n - (s)->i) < (size_t)(amt)) { \ + (s)->d = (char*)realloc((s)->d, (s)->n + amt); \ + if ((s)->d == NULL) oom(); \ + (s)->n += amt; \ + } \ +} while(0) + +#define utstring_init(s) \ +do { \ + (s)->n = 0; (s)->i = 0; (s)->d = NULL; \ + utstring_reserve(s,100); \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_done(s) \ +do { \ + if ((s)->d != NULL) free((s)->d); \ + (s)->n = 0; \ +} while(0) + +#define utstring_free(s) \ +do { \ + utstring_done(s); \ + free(s); \ +} while(0) + +#define utstring_new(s) \ +do { \ + s = (UT_string*)calloc(sizeof(UT_string),1); \ + if (!s) oom(); \ + utstring_init(s); \ +} while(0) + +#define utstring_renew(s) \ +do { \ + if (s) { \ + utstring_clear(s); \ + } else { \ + utstring_new(s); \ + } \ +} while(0) + +#define utstring_clear(s) \ +do { \ + (s)->i = 0; \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_bincpy(s,b,l) \ +do { \ + utstring_reserve((s),(l)+1); \ + if (l) memcpy(&(s)->d[(s)->i], b, l); \ + (s)->i += l; \ + (s)->d[(s)->i]='\0'; \ +} while(0) + +#define utstring_concat(dst,src) \ +do { \ + utstring_reserve((dst),((src)->i)+1); \ + if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \ + (dst)->i += (src)->i; \ + (dst)->d[(dst)->i]='\0'; \ +} while(0) + +#define utstring_len(s) ((unsigned)((s)->i)) + +#define utstring_body(s) ((s)->d) + +_UNUSED_ static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) { + int n; + va_list cp; + while (1) { +#ifdef _WIN32 + cp = ap; +#else + va_copy(cp, ap); +#endif + n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp); + va_end(cp); + + if ((n > -1) && ((size_t) n < (s->n-s->i))) { + s->i += n; + return; + } + + /* Else try again with more space. */ + if (n > -1) utstring_reserve(s,n+1); /* exact */ + else utstring_reserve(s,(s->n)*2); /* 2x */ + } +} +#ifdef __GNUC__ +/* support printf format checking (2=the format string, 3=start of varargs) */ +static void utstring_printf(UT_string *s, const char *fmt, ...) + __attribute__ (( format( printf, 2, 3) )); +#endif +_UNUSED_ static void utstring_printf(UT_string *s, const char *fmt, ...) { + va_list ap; + va_start(ap,fmt); + utstring_printf_va(s,fmt,ap); + va_end(ap); +} + +/******************************************************************************* + * begin substring search functions * + ******************************************************************************/ +/* Build KMP table from left to right. */ +_UNUSED_ static void _utstring_BuildTable( + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + + i = 0; + j = i - 1; + P_KMP_Table[i] = j; + while (i < (long) P_NeedleLen) + { + while ( (j > -1) && (P_Needle[i] != P_Needle[j]) ) + { + j = P_KMP_Table[j]; + } + i++; + j++; + if (i < (long) P_NeedleLen) + { + if (P_Needle[i] == P_Needle[j]) + { + P_KMP_Table[i] = P_KMP_Table[j]; + } + else + { + P_KMP_Table[i] = j; + } + } + else + { + P_KMP_Table[i] = j; + } + } + + return; +} + + +/* Build KMP table from right to left. */ +_UNUSED_ static void _utstring_BuildTableR( + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + + i = P_NeedleLen - 1; + j = i + 1; + P_KMP_Table[i + 1] = j; + while (i >= 0) + { + while ( (j < (long) P_NeedleLen) && (P_Needle[i] != P_Needle[j]) ) + { + j = P_KMP_Table[j + 1]; + } + i--; + j--; + if (i >= 0) + { + if (P_Needle[i] == P_Needle[j]) + { + P_KMP_Table[i + 1] = P_KMP_Table[j + 1]; + } + else + { + P_KMP_Table[i + 1] = j; + } + } + else + { + P_KMP_Table[i + 1] = j; + } + } + + return; +} + + +/* Search data from left to right. ( Multiple search mode. ) */ +_UNUSED_ static long _utstring_find( + const char *P_Haystack, + size_t P_HaystackLen, + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + long V_FindPosition = -1; + + /* Search from left to right. */ + i = j = 0; + while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) ) + { + while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) ) + { + i = P_KMP_Table[i]; + } + i++; + j++; + if (i >= (int)P_NeedleLen) + { + /* Found. */ + V_FindPosition = j - i; + break; + } + } + + return V_FindPosition; +} + + +/* Search data from right to left. ( Multiple search mode. ) */ +_UNUSED_ static long _utstring_findR( + const char *P_Haystack, + size_t P_HaystackLen, + const char *P_Needle, + size_t P_NeedleLen, + long *P_KMP_Table) +{ + long i, j; + long V_FindPosition = -1; + + /* Search from right to left. */ + j = (P_HaystackLen - 1); + i = (P_NeedleLen - 1); + while ( (j >= 0) && (j >= i) ) + { + while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) ) + { + i = P_KMP_Table[i + 1]; + } + i--; + j--; + if (i < 0) + { + /* Found. */ + V_FindPosition = j + 1; + break; + } + } + + return V_FindPosition; +} + + +/* Search data from left to right. ( One time search mode. ) */ +_UNUSED_ static long utstring_find( + UT_string *s, + long P_StartPosition, /* Start from 0. -1 means last position. */ + const char *P_Needle, + size_t P_NeedleLen) +{ + long V_StartPosition; + long V_HaystackLen; + long *V_KMP_Table; + long V_FindPosition = -1; + + if (P_StartPosition < 0) + { + V_StartPosition = s->i + P_StartPosition; + } + else + { + V_StartPosition = P_StartPosition; + } + V_HaystackLen = s->i - V_StartPosition; + if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) ) + { + V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); + if (V_KMP_Table != NULL) + { + _utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table); + + V_FindPosition = _utstring_find(s->d + V_StartPosition, + V_HaystackLen, + P_Needle, + P_NeedleLen, + V_KMP_Table); + if (V_FindPosition >= 0) + { + V_FindPosition += V_StartPosition; + } + + free(V_KMP_Table); + } + } + + return V_FindPosition; +} + + +/* Search data from right to left. ( One time search mode. ) */ +_UNUSED_ static long utstring_findR( + UT_string *s, + long P_StartPosition, /* Start from 0. -1 means last position. */ + const char *P_Needle, + size_t P_NeedleLen) +{ + long V_StartPosition; + long V_HaystackLen; + long *V_KMP_Table; + long V_FindPosition = -1; + + if (P_StartPosition < 0) + { + V_StartPosition = s->i + P_StartPosition; + } + else + { + V_StartPosition = P_StartPosition; + } + V_HaystackLen = V_StartPosition + 1; + if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) ) + { + V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); + if (V_KMP_Table != NULL) + { + _utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table); + + V_FindPosition = _utstring_findR(s->d, + V_HaystackLen, + P_Needle, + P_NeedleLen, + V_KMP_Table); + + free(V_KMP_Table); + } + } + + return V_FindPosition; +} +/******************************************************************************* + * end substring search functions * + ******************************************************************************/ + +#endif /* UTSTRING_H */ diff --git a/libs/libaxolotl/src/vpool.c b/libs/libaxolotl/src/vpool.c new file mode 100644 index 0000000000..97b4ec8dd4 --- /dev/null +++ b/libs/libaxolotl/src/vpool.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2006, 2008 Alexey Vatchenko <av@bsdua.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> + +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include "vpool.h" + +static void vpool_shift(struct vpool *pool); +static int vpool_new_size(struct vpool *pool, size_t datsize, + size_t *size); +static int vpool_resize(struct vpool *pool, size_t datsize); + +static void +vpool_shift(struct vpool *pool) +{ + if (pool->v_buf != pool->v_basebuf) { + memmove(pool->v_basebuf, pool->v_buf, pool->v_off); + pool->v_buf = pool->v_basebuf; + } +} + +static int +vpool_new_size(struct vpool *pool, size_t datsize, size_t *size) +{ + size_t need; + size_t rem; + + if (datsize <= pool->v_size - pool->v_off) { + *size = pool->v_size; + return (0); + } + + /* Check limit of new requested size */ + if (pool->v_limit - pool->v_off < datsize) + return (EFBIG); + need = pool->v_off + datsize; + + /* Check limit of new size aligned to block size */ + rem = need % pool->v_blksize; + if (rem != 0) { + if (pool->v_limit - pool->v_off >= + datsize + (pool->v_blksize - rem)) + need += pool->v_blksize - rem; + else + need = pool->v_limit; + } + + *size = need; + return (0); +} + +static int +vpool_resize(struct vpool *pool, size_t datsize) +{ + char *ret; + size_t size; + int error; + + error = vpool_new_size(pool, datsize, &size); + if (error != 0) + return (error); + + if (size > pool->v_size) { + ret = (char *) malloc(size); + if (ret == NULL) + return (ENOMEM); + + memcpy(ret, pool->v_buf, pool->v_off); + free(pool->v_basebuf); + pool->v_basebuf = pool->v_buf = ret; + pool->v_size = size; + } else if ((pool->v_size - pool->v_off) - + (size_t)((char*)pool->v_buf - (char*)pool->v_basebuf) < datsize) + vpool_shift(pool); + + return (0); +} + +void +vpool_init(struct vpool *pool, size_t blksize, size_t limit) +{ + + pool->v_basebuf = pool->v_buf = NULL; + pool->v_off = pool->v_size = 0; + + pool->v_blksize = (blksize == 0) ? 4096 : blksize; /* XXX */ + pool->v_limit = (limit == 0) ? SIZE_MAX : limit; + + pool->v_lasterr = 0; +} + +void +vpool_final(struct vpool *pool) +{ + free(pool->v_basebuf); +} + +void +vpool_reset(struct vpool *pool) +{ + free(pool->v_basebuf); + pool->v_basebuf = pool->v_buf = NULL; + pool->v_off = pool->v_size = 0; + pool->v_lasterr = 0; +} + +void +vpool_wipe(struct vpool *pool) +{ + pool->v_off = 0; + pool->v_lasterr = 0; +} + +void * +vpool_insert(struct vpool *pool, size_t where, void *data, size_t datsize) +{ + char *ret; + int error; + + error = vpool_resize(pool, datsize); + if (error != 0) { + pool->v_lasterr = error; + return (NULL); + } + + /* + * If ``where'' is greater than or equal to offset then + * we are appending data to the end of the buffer. + */ + if (where > pool->v_off) + where = pool->v_off; + + ret = (char *)pool->v_buf + where; + if (pool->v_off - where > 0) + memmove(ret + datsize, ret, pool->v_off - where); + memcpy(ret, data, datsize); + pool->v_off += datsize; + pool->v_lasterr = 0; + + return (ret); +} + +void * +vpool_expand(struct vpool *pool, size_t where, size_t size) +{ + char *ret; + int error; + + error = vpool_resize(pool, size); + if (error != 0) { + pool->v_lasterr = error; + return (NULL); + } + + /* + * If ``where'' is greater than or equal to offset then + * we are appending data to the end of the buffer. + */ + if (where > pool->v_off) + where = pool->v_off; + + ret = (char *)pool->v_buf + where; + if (pool->v_off - where > 0) + memmove(ret + size, ret, pool->v_off - where); + pool->v_off += size; + pool->v_lasterr = 0; + + return (ret); +} + +int +vpool_truncate(struct vpool *pool, + size_t where, size_t size, enum vpool_trunc how) +{ + /* Check if caller wants to remove more data than we have */ + if (where >= pool->v_off || + size > pool->v_off || pool->v_off - size < where) { + pool->v_lasterr = ERANGE; + return (pool->v_lasterr); + } + + if (how == VPOOL_EXCLUDE) { + if (where == 0) { + /* + * Optimization. + * Don't move data, just adjust pointer. + */ + pool->v_buf = (char *)pool->v_buf + size; + } else { + memmove((char *)pool->v_buf + where, + (char *)pool->v_buf + where + size, + pool->v_off - size - where); + } + pool->v_off -= size; + } else { + pool->v_buf = (char*)(pool->v_buf) + where; + pool->v_off = size; + } + + pool->v_lasterr = 0; + return (0); +} + +void +vpool_export(struct vpool *pool, void **buf, size_t *size) +{ + vpool_shift(pool); + *buf = pool->v_buf; + *size = pool->v_off; + pool->v_basebuf = pool->v_buf = NULL; + pool->v_off = pool->v_size = 0; + pool->v_lasterr = 0; +} diff --git a/libs/libaxolotl/src/vpool.h b/libs/libaxolotl/src/vpool.h new file mode 100644 index 0000000000..0eb714cc10 --- /dev/null +++ b/libs/libaxolotl/src/vpool.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2006, 2008 Alexey Vatchenko <av@bsdua.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * VPool: implementation of pool of data with a variable size. + */ +#ifndef _VPOOL_H_ +#define _VPOOL_H_ + +#include <sys/types.h> +#ifndef _WINDOWS +#include <sys/cdefs.h> +#else +#ifdef __cplusplus +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS } +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif /* __cplusplus */ +#endif /* _WINDOWS */ + +#include <limits.h> + +struct vpool { + void *v_basebuf; /* pointer returned by (re|m)alloc() */ + void *v_buf; /* actual data starts here */ + size_t v_off; + size_t v_size; + + size_t v_blksize; + size_t v_limit; + int v_lasterr; +}; + +enum vpool_trunc {VPOOL_EXCLUDE, VPOOL_INCLUDE}; +#define VPOOL_TAIL UINT_MAX + +__BEGIN_DECLS + +void vpool_init(struct vpool *pool, size_t blksize, size_t limit); +void vpool_final(struct vpool *pool); + +void vpool_reset(struct vpool *pool); +void vpool_wipe(struct vpool *pool); + +void * vpool_insert(struct vpool *pool, + size_t where, void *data, size_t datsize); +void * vpool_expand(struct vpool *pool, size_t where, size_t size); + +int vpool_truncate(struct vpool *pool, + size_t where, size_t size, enum vpool_trunc how); + +#define vpool_is_empty(pool) ((pool)->v_off == 0) +#define vpool_get_buf(pool) ((pool)->v_buf) +#define vpool_get_length(pool) ((pool)->v_off) +#define vpool_get_error(pool) ((pool)->v_lasterr) + +void vpool_export(struct vpool *pool, void **buf, size_t *size); + +__END_DECLS + +#endif /* !_VPOOL_H_ */ |