From 094535c010320967639e8e86f974d878e80baa72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Fri, 1 May 2015 16:13:57 +0200 Subject: Imported Upstream version 1.7.0 --- ccast/Jamfile | 34 + ccast/License.txt | 662 ++++++++ ccast/Readme.txt | 12 + ccast/afiles | 54 + ccast/axTLS/Jamfile | 42 + ccast/axTLS/Readme.txt | 7 + ccast/axTLS/aes.c | 457 ++++++ ccast/axTLS/asn1.c | 566 +++++++ ccast/axTLS/bigint.c | 1512 ++++++++++++++++++ ccast/axTLS/bigint.h | 99 ++ ccast/axTLS/bigint_impl.h | 131 ++ ccast/axTLS/cert.h | 43 + ccast/axTLS/config.h | 41 + ccast/axTLS/crypto.h | 230 +++ ccast/axTLS/crypto_misc.c | 367 +++++ ccast/axTLS/crypto_misc.h | 172 +++ ccast/axTLS/gen_cert.c | 368 +++++ ccast/axTLS/hmac.c | 105 ++ ccast/axTLS/loader.c | 483 ++++++ ccast/axTLS/md2.c | 162 ++ ccast/axTLS/md5.c | 294 ++++ ccast/axTLS/openssl.c | 323 ++++ ccast/axTLS/os_int.h | 74 + ccast/axTLS/os_port.c | 160 ++ ccast/axTLS/os_port.h | 194 +++ ccast/axTLS/p12.c | 483 ++++++ ccast/axTLS/private_key.h | 54 + ccast/axTLS/rc4.c | 92 ++ ccast/axTLS/rsa.c | 270 ++++ ccast/axTLS/sha1.c | 249 +++ ccast/axTLS/ssl.h | 513 +++++++ ccast/axTLS/temp | 2 + ccast/axTLS/tls1.c | 2411 +++++++++++++++++++++++++++++ ccast/axTLS/tls1.h | 309 ++++ ccast/axTLS/tls1_clnt.c | 397 +++++ ccast/axTLS/tls1_svr.c | 478 ++++++ ccast/axTLS/version.h | 1 + ccast/axTLS/x509.c | 561 +++++++ ccast/cast_channel.proto | 79 + ccast/ccast.c | 1319 ++++++++++++++++ ccast/ccast.h | 108 ++ ccast/ccmdns.c | 1096 ++++++++++++++ ccast/ccmdns.h | 42 + ccast/ccmes.c | 334 ++++ ccast/ccmes.h | 103 ++ ccast/ccpacket.c | 501 ++++++ ccast/ccpacket.h | 89 ++ ccast/cctest.c | 329 ++++ ccast/chan/cast_channel.pb-c.c | 593 ++++++++ ccast/chan/cast_channel.pb-c.h | 233 +++ ccast/chan/protobuf-c.c | 3287 ++++++++++++++++++++++++++++++++++++++++ ccast/chan/protobuf-c.h | 1079 +++++++++++++ ccast/dpat.c | 952 ++++++++++++ ccast/filt.c | 531 +++++++ 54 files changed, 23087 insertions(+) create mode 100644 ccast/Jamfile create mode 100644 ccast/License.txt create mode 100644 ccast/Readme.txt create mode 100644 ccast/afiles create mode 100644 ccast/axTLS/Jamfile create mode 100644 ccast/axTLS/Readme.txt create mode 100644 ccast/axTLS/aes.c create mode 100644 ccast/axTLS/asn1.c create mode 100644 ccast/axTLS/bigint.c create mode 100644 ccast/axTLS/bigint.h create mode 100644 ccast/axTLS/bigint_impl.h create mode 100644 ccast/axTLS/cert.h create mode 100644 ccast/axTLS/config.h create mode 100644 ccast/axTLS/crypto.h create mode 100644 ccast/axTLS/crypto_misc.c create mode 100644 ccast/axTLS/crypto_misc.h create mode 100644 ccast/axTLS/gen_cert.c create mode 100644 ccast/axTLS/hmac.c create mode 100644 ccast/axTLS/loader.c create mode 100644 ccast/axTLS/md2.c create mode 100644 ccast/axTLS/md5.c create mode 100644 ccast/axTLS/openssl.c create mode 100644 ccast/axTLS/os_int.h create mode 100644 ccast/axTLS/os_port.c create mode 100644 ccast/axTLS/os_port.h create mode 100644 ccast/axTLS/p12.c create mode 100644 ccast/axTLS/private_key.h create mode 100644 ccast/axTLS/rc4.c create mode 100644 ccast/axTLS/rsa.c create mode 100644 ccast/axTLS/sha1.c create mode 100644 ccast/axTLS/ssl.h create mode 100644 ccast/axTLS/temp create mode 100644 ccast/axTLS/tls1.c create mode 100644 ccast/axTLS/tls1.h create mode 100644 ccast/axTLS/tls1_clnt.c create mode 100644 ccast/axTLS/tls1_svr.c create mode 100644 ccast/axTLS/version.h create mode 100644 ccast/axTLS/x509.c create mode 100644 ccast/cast_channel.proto create mode 100644 ccast/ccast.c create mode 100644 ccast/ccast.h create mode 100644 ccast/ccmdns.c create mode 100644 ccast/ccmdns.h create mode 100644 ccast/ccmes.c create mode 100644 ccast/ccmes.h create mode 100644 ccast/ccpacket.c create mode 100644 ccast/ccpacket.h create mode 100644 ccast/cctest.c create mode 100644 ccast/chan/cast_channel.pb-c.c create mode 100644 ccast/chan/cast_channel.pb-c.h create mode 100644 ccast/chan/protobuf-c.c create mode 100644 ccast/chan/protobuf-c.h create mode 100644 ccast/dpat.c create mode 100644 ccast/filt.c (limited to 'ccast') diff --git a/ccast/Jamfile b/ccast/Jamfile new file mode 100644 index 0000000..ff216ff --- /dev/null +++ b/ccast/Jamfile @@ -0,0 +1,34 @@ + +# Jamfile for ccast library + +#PREF_CCFLAGS = $(CCOPTFLAG) ; # Turn optimisation on +PREF_CCFLAGS = $(CCDEBUGFLAG) ; # Debugging flags +PREF_LINKFLAGS = $(LINKDEBUGFLAG) ; + +if $(NT) { + DEFINES += WIN32 ; +} + +SubInclude axTLS ; + +HDRS = ../h ../numlib ../spectro axTLS chan ../yajl ; + +# We create the channel protocol buffers implementation files thus: +# /src/protobuf_c/protobuf.exe --c_out=chan cast_channel.proto + +CHAN_SRC = + chan/cast_channel.pb-c.c + chan/protobuf-c.c + ; + +# ccast library +Library libccast : ccmdns.c ccpacket.c ccmes.c ccast.c $(CHAN_SRC) dpat.c ; + +LINKLIBS = ./libccast axTLS/libaxtls ../yajl/libyajl ../numlib/libnum ../spectro/libconv ; + +# Test harness +#Main cctest : cctest.c ; +Main filt : filt.c ; + +MainVariant dpat : dpat.c : : STANDALONE_TEST ; + diff --git a/ccast/License.txt b/ccast/License.txt new file mode 100644 index 0000000..a871fcf --- /dev/null +++ b/ccast/License.txt @@ -0,0 +1,662 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are 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. + + 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. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + 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 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 work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 Affero 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 Affero 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 Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. + diff --git a/ccast/Readme.txt b/ccast/Readme.txt new file mode 100644 index 0000000..3b95267 --- /dev/null +++ b/ccast/Readme.txt @@ -0,0 +1,12 @@ +This directory contains the library for sending +rasters to the Google ChromeCast. + +Hierarchy: + + cccast.c Top level actions & receive thread + ccmes.c Message handling, uses probuf + chan/ protobuf encoding + ccpacket.c socket write/read + + ccmdns.c MDNS sign on + axTLS diff --git a/ccast/afiles b/ccast/afiles new file mode 100644 index 0000000..0f8ffda --- /dev/null +++ b/ccast/afiles @@ -0,0 +1,54 @@ +afiles +Readme.txt +License.txt +Jamfile +ccast.c +ccast.h +ccmdns.c +ccmdns.h +ccmes.c +ccmes.h +ccpacket.c +ccpacket.h +cctest.c +filt.c +dpat.c +cast_channel.proto +axTLS/Jamfile +axTLS/Readme.txt +axTLS/aes.c +axTLS/asn1.c +axTLS/bigint.c +axTLS/bigint.h +axTLS/bigint_impl.h +axTLS/cert.h +axTLS/config.h +axTLS/crypto.h +axTLS/crypto_misc.c +axTLS/crypto_misc.h +axTLS/gen_cert.c +axTLS/hmac.c +axTLS/loader.c +axTLS/md2.c +axTLS/md5.c +axTLS/openssl.c +axTLS/os_int.h +axTLS/os_port.c +axTLS/os_port.h +axTLS/p12.c +axTLS/private_key.h +axTLS/rc4.c +axTLS/rsa.c +axTLS/sha1.c +axTLS/ssl.h +axTLS/temp +axTLS/tls1.c +axTLS/tls1.h +axTLS/tls1_clnt.c +axTLS/tls1_svr.c +axTLS/version.h +axTLS/x509.c +chan/cast_channel.pb-c.c +chan/cast_channel.pb-c.h +chan/protobuf-c.c +chan/protobuf-c.h diff --git a/ccast/axTLS/Jamfile b/ccast/axTLS/Jamfile new file mode 100644 index 0000000..2e8b027 --- /dev/null +++ b/ccast/axTLS/Jamfile @@ -0,0 +1,42 @@ + +PREF_CCFLAGS = $(CCOPTFLAG) ; # Turn optimisation on +#PREF_CCFLAGS = $(CCDEBUGFLAG) ; # Debugging flags +PREF_LINKFLAGS = $(LINKDEBUGFLAG) ; + +# Copy the configuration file +if $(NT) { + DEFINES += WIN32 ; +} else { +# File conf.h : tiffconf.vc.h ; + if $(OS) = MACOSX { + } else if $(OS) = FREEBSD { + } else { + DEFINES += CONFIG_PLATFORM_LINUX ; + } +} + +# tiff library +LIBSRCS = + aes.c + asn1.c + bigint.c + crypto_misc.c + gen_cert.c + hmac.c + loader.c + md2.c + md5.c + openssl.c + os_port.c + p12.c + rc4.c + rsa.c + sha1.c + tls1.c + tls1_clnt.c + tls1_svr.c + x509.c + ; + +Library libaxtls.lib : $(LIBSRCS) ; + diff --git a/ccast/axTLS/Readme.txt b/ccast/axTLS/Readme.txt new file mode 100644 index 0000000..8f42eb1 --- /dev/null +++ b/ccast/axTLS/Readme.txt @@ -0,0 +1,7 @@ +This is a cut down version of axTLS, just for client operation. +It has been modified to return timeout status rather than +erroring, and to be able to call ssl_read asynchronously +once the handshaking is complete. + +It is not used for any security sensitive purpose, but is used purely to +communicate with the ChromeCast. diff --git a/ccast/axTLS/aes.c b/ccast/axTLS/aes.c new file mode 100644 index 0000000..9b07e27 --- /dev/null +++ b/ccast/axTLS/aes.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * AES implementation - this is a small code version. There are much faster + * versions around but they are much larger in size (i.e. they use large + * submix tables). + */ + +#include +#include "os_port.h" +#include "crypto.h" + +/* all commented out in skeleton mode */ +#ifndef CONFIG_SSL_SKELETON_MODE + +#define rot1(x) (((x) << 24) | ((x) >> 8)) +#define rot2(x) (((x) << 16) | ((x) >> 16)) +#define rot3(x) (((x) << 8) | ((x) >> 24)) + +/* + * This cute trick does 4 'mul by two' at once. Stolen from + * Dr B. R. Gladman but I'm sure the u-(u>>7) is + * a standard graphics trick + * The key to this is that we need to xor with 0x1b if the top bit is set. + * a 1xxx xxxx 0xxx 0xxx First we mask the 7bit, + * b 1000 0000 0000 0000 then we shift right by 7 putting the 7bit in 0bit, + * c 0000 0001 0000 0000 we then subtract (c) from (b) + * d 0111 1111 0000 0000 and now we and with our mask + * e 0001 1011 0000 0000 + */ +#define mt 0x80808080 +#define ml 0x7f7f7f7f +#define mh 0xfefefefe +#define mm 0x1b1b1b1b +#define mul2(x,t) ((t)=((x)&mt), \ + ((((x)+(x))&mh)^(((t)-((t)>>7))&mm))) + +#define inv_mix_col(x,f2,f4,f8,f9) (\ + (f2)=mul2(x,f2), \ + (f4)=mul2(f2,f4), \ + (f8)=mul2(f4,f8), \ + (f9)=(x)^(f8), \ + (f8)=((f2)^(f4)^(f8)), \ + (f2)^=(f9), \ + (f4)^=(f9), \ + (f8)^=rot3(f2), \ + (f8)^=rot2(f4), \ + (f8)^rot1(f9)) + +/* + * AES S-box + */ +static const uint8_t aes_sbox[256] = +{ + 0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5, + 0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76, + 0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0, + 0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0, + 0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC, + 0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15, + 0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A, + 0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75, + 0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0, + 0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84, + 0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B, + 0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF, + 0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85, + 0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8, + 0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5, + 0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2, + 0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17, + 0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73, + 0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88, + 0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB, + 0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C, + 0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79, + 0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9, + 0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08, + 0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6, + 0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A, + 0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E, + 0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E, + 0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94, + 0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF, + 0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68, + 0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16, +}; + +/* + * AES is-box + */ +static const uint8_t aes_isbox[256] = +{ + 0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38, + 0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb, + 0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87, + 0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb, + 0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d, + 0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e, + 0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2, + 0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25, + 0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16, + 0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92, + 0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda, + 0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84, + 0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a, + 0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06, + 0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02, + 0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b, + 0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea, + 0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73, + 0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85, + 0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e, + 0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89, + 0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b, + 0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20, + 0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4, + 0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31, + 0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f, + 0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d, + 0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef, + 0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0, + 0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61, + 0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26, + 0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d +}; + +static const unsigned char Rcon[30]= +{ + 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80, + 0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f, + 0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4, + 0xb3,0x7d,0xfa,0xef,0xc5,0x91, +}; + +/* ----- static functions ----- */ +static void AES_encrypt(const AES_CTX *ctx, uint32_t *data); +static void AES_decrypt(const AES_CTX *ctx, uint32_t *data); + +/* Perform doubling in Galois Field GF(2^8) using the irreducible polynomial + x^8+x^4+x^3+x+1 */ +static unsigned char AES_xtime(uint32_t x) +{ + return (x&0x80) ? (x<<1)^0x1b : x<<1; +} + +/** + * Set up AES with the key/iv and cipher size. + */ +void AES_set_key(AES_CTX *ctx, const uint8_t *key, + const uint8_t *iv, AES_MODE mode) +{ + int i, ii; + uint32_t *W, tmp, tmp2; + const unsigned char *ip; + int words; + + switch (mode) + { + case AES_MODE_128: + i = 10; + words = 4; + break; + + case AES_MODE_256: + i = 14; + words = 8; + break; + + default: /* fail silently */ + return; + } + + ctx->rounds = i; + ctx->key_size = words; + W = ctx->ks; + for (i = 0; i < words; i+=2) + { + W[i+0]= ((uint32_t)key[ 0]<<24)| + ((uint32_t)key[ 1]<<16)| + ((uint32_t)key[ 2]<< 8)| + ((uint32_t)key[ 3] ); + W[i+1]= ((uint32_t)key[ 4]<<24)| + ((uint32_t)key[ 5]<<16)| + ((uint32_t)key[ 6]<< 8)| + ((uint32_t)key[ 7] ); + key += 8; + } + + ip = Rcon; + ii = 4 * (ctx->rounds+1); + for (i = words; i> 8)&0xff]<<16; + tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<24; + tmp2|=(uint32_t)aes_sbox[(tmp>>24) ]; + tmp=tmp2^(((unsigned int)*ip)<<24); + ip++; + } + + if ((words == 8) && ((i % words) == 4)) + { + tmp2 =(uint32_t)aes_sbox[(tmp )&0xff] ; + tmp2|=(uint32_t)aes_sbox[(tmp>> 8)&0xff]<< 8; + tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<16; + tmp2|=(uint32_t)aes_sbox[(tmp>>24) ]<<24; + tmp=tmp2; + } + + W[i]=W[i-words]^tmp; + } + + /* copy the iv across */ + memcpy(ctx->iv, iv, 16); +} + +/** + * Change a key for decryption. + */ +void AES_convert_key(AES_CTX *ctx) +{ + int i; + uint32_t *k,w,t1,t2,t3,t4; + + k = ctx->ks; + k += 4; + + for (i= ctx->rounds*4; i > 4; i--) + { + w= *k; + w = inv_mix_col(w,t1,t2,t3,t4); + *k++ =w; + } +} + +/** + * Encrypt a byte sequence (with a block size 16) using the AES cipher. + */ +void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) +{ + int i; + uint32_t tin[4], tout[4], iv[4]; + + memcpy(iv, ctx->iv, AES_IV_SIZE); + for (i = 0; i < 4; i++) + tout[i] = ntohl(iv[i]); + + for (length -= AES_BLOCKSIZE; length >= 0; length -= AES_BLOCKSIZE) + { + uint32_t msg_32[4]; + uint32_t out_32[4]; + memcpy(msg_32, msg, AES_BLOCKSIZE); + msg += AES_BLOCKSIZE; + + for (i = 0; i < 4; i++) + tin[i] = ntohl(msg_32[i])^tout[i]; + + AES_encrypt(ctx, tin); + + for (i = 0; i < 4; i++) + { + tout[i] = tin[i]; + out_32[i] = htonl(tout[i]); + } + + memcpy(out, out_32, AES_BLOCKSIZE); + out += AES_BLOCKSIZE; + } + + for (i = 0; i < 4; i++) + iv[i] = htonl(tout[i]); + memcpy(ctx->iv, iv, AES_IV_SIZE); +} + +/** + * Decrypt a byte sequence (with a block size 16) using the AES cipher. + */ +void AES_cbc_decrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) +{ + int i; + uint32_t tin[4], xor[4], tout[4], data[4], iv[4]; + + memcpy(iv, ctx->iv, AES_IV_SIZE); + for (i = 0; i < 4; i++) + xor[i] = ntohl(iv[i]); + + for (length -= 16; length >= 0; length -= 16) + { + uint32_t msg_32[4]; + uint32_t out_32[4]; + memcpy(msg_32, msg, AES_BLOCKSIZE); + msg += AES_BLOCKSIZE; + + for (i = 0; i < 4; i++) + { + tin[i] = ntohl(msg_32[i]); + data[i] = tin[i]; + } + + AES_decrypt(ctx, data); + + for (i = 0; i < 4; i++) + { + tout[i] = data[i]^xor[i]; + xor[i] = tin[i]; + out_32[i] = htonl(tout[i]); + } + + memcpy(out, out_32, AES_BLOCKSIZE); + out += AES_BLOCKSIZE; + } + + for (i = 0; i < 4; i++) + iv[i] = htonl(xor[i]); + memcpy(ctx->iv, iv, AES_IV_SIZE); +} + +/** + * Encrypt a single block (16 bytes) of data + */ +static void AES_encrypt(const AES_CTX *ctx, uint32_t *data) +{ + /* To make this code smaller, generate the sbox entries on the fly. + * This will have a really heavy effect upon performance. + */ + uint32_t tmp[4]; + uint32_t tmp1, old_a0, a0, a1, a2, a3, row; + int curr_rnd; + int rounds = ctx->rounds; + const uint32_t *k = ctx->ks; + + /* Pre-round key addition */ + for (row = 0; row < 4; row++) + data[row] ^= *(k++); + + /* Encrypt one block. */ + for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++) + { + /* Perform ByteSub and ShiftRow operations together */ + for (row = 0; row < 4; row++) + { + a0 = (uint32_t)aes_sbox[(data[row%4]>>24)&0xFF]; + a1 = (uint32_t)aes_sbox[(data[(row+1)%4]>>16)&0xFF]; + a2 = (uint32_t)aes_sbox[(data[(row+2)%4]>>8)&0xFF]; + a3 = (uint32_t)aes_sbox[(data[(row+3)%4])&0xFF]; + + /* Perform MixColumn iff not last round */ + if (curr_rnd < (rounds - 1)) + { + tmp1 = a0 ^ a1 ^ a2 ^ a3; + old_a0 = a0; + a0 ^= tmp1 ^ AES_xtime(a0 ^ a1); + a1 ^= tmp1 ^ AES_xtime(a1 ^ a2); + a2 ^= tmp1 ^ AES_xtime(a2 ^ a3); + a3 ^= tmp1 ^ AES_xtime(a3 ^ old_a0); + } + + tmp[row] = ((a0 << 24) | (a1 << 16) | (a2 << 8) | a3); + } + + /* KeyAddition - note that it is vital that this loop is separate from + the MixColumn operation, which must be atomic...*/ + for (row = 0; row < 4; row++) + data[row] = tmp[row] ^ *(k++); + } +} + +/** + * Decrypt a single block (16 bytes) of data + */ +static void AES_decrypt(const AES_CTX *ctx, uint32_t *data) +{ + uint32_t tmp[4]; + uint32_t xt0,xt1,xt2,xt3,xt4,xt5,xt6; + uint32_t a0, a1, a2, a3, row; + int curr_rnd; + int rounds = ctx->rounds; + const uint32_t *k = ctx->ks + ((rounds+1)*4); + + /* pre-round key addition */ + for (row=4; row > 0;row--) + data[row-1] ^= *(--k); + + /* Decrypt one block */ + for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++) + { + /* Perform ByteSub and ShiftRow operations together */ + for (row = 4; row > 0; row--) + { + a0 = aes_isbox[(data[(row+3)%4]>>24)&0xFF]; + a1 = aes_isbox[(data[(row+2)%4]>>16)&0xFF]; + a2 = aes_isbox[(data[(row+1)%4]>>8)&0xFF]; + a3 = aes_isbox[(data[row%4])&0xFF]; + + /* Perform MixColumn iff not last round */ + if (curr_rnd<(rounds-1)) + { + /* The MDS cofefficients (0x09, 0x0B, 0x0D, 0x0E) + are quite large compared to encryption; this + operation slows decryption down noticeably. */ + xt0 = AES_xtime(a0^a1); + xt1 = AES_xtime(a1^a2); + xt2 = AES_xtime(a2^a3); + xt3 = AES_xtime(a3^a0); + xt4 = AES_xtime(xt0^xt1); + xt5 = AES_xtime(xt1^xt2); + xt6 = AES_xtime(xt4^xt5); + + xt0 ^= a1^a2^a3^xt4^xt6; + xt1 ^= a0^a2^a3^xt5^xt6; + xt2 ^= a0^a1^a3^xt4^xt6; + xt3 ^= a0^a1^a2^xt5^xt6; + tmp[row-1] = ((xt0<<24)|(xt1<<16)|(xt2<<8)|xt3); + } + else + tmp[row-1] = ((a0<<24)|(a1<<16)|(a2<<8)|a3); + } + + for (row = 4; row > 0; row--) + data[row-1] = tmp[row-1] ^ *(--k); + } +} + +#endif diff --git a/ccast/axTLS/asn1.c b/ccast/axTLS/asn1.c new file mode 100644 index 0000000..b082275 --- /dev/null +++ b/ccast/axTLS/asn1.c @@ -0,0 +1,566 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * Some primitive asn methods for extraction ASN.1 data. + */ + +#include +#include +#include +#include +#include "os_port.h" +#include "crypto.h" +#include "crypto_misc.h" + +#define SIG_OID_PREFIX_SIZE 8 +#define SIG_IIS6_OID_SIZE 5 +#define SIG_SUBJECT_ALT_NAME_SIZE 3 + +/* Must be an RSA algorithm with either SHA1 or MD5 for verifying to work */ +static const uint8_t sig_oid_prefix[SIG_OID_PREFIX_SIZE] = +{ + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01 +}; + +static const uint8_t sig_sha1WithRSAEncrypt[SIG_IIS6_OID_SIZE] = +{ + 0x2b, 0x0e, 0x03, 0x02, 0x1d +}; + +static const uint8_t sig_subject_alt_name[SIG_SUBJECT_ALT_NAME_SIZE] = +{ + 0x55, 0x1d, 0x11 +}; + +/* CN, O, OU */ +static const uint8_t g_dn_types[] = { 3, 10, 11 }; + +int get_asn1_length(const uint8_t *buf, int *offset) +{ + int len, i; + + if (!(buf[*offset] & 0x80)) /* short form */ + { + len = buf[(*offset)++]; + } + else /* long form */ + { + int length_bytes = buf[(*offset)++]&0x7f; + len = 0; + for (i = 0; i < length_bytes; i++) + { + len <<= 8; + len += buf[(*offset)++]; + } + } + + return len; +} + +/** + * Skip the ASN1.1 object type and its length. Get ready to read the object's + * data. + */ +int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type) +{ + if (buf[*offset] != obj_type) + return X509_NOT_OK; + (*offset)++; + return get_asn1_length(buf, offset); +} + +/** + * Skip over an ASN.1 object type completely. Get ready to read the next + * object. + */ +int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type) +{ + int len; + + if (buf[*offset] != obj_type) + return X509_NOT_OK; + (*offset)++; + len = get_asn1_length(buf, offset); + *offset += len; + return 0; +} + +/** + * Read an integer value for ASN.1 data + * Note: This function allocates memory which must be freed by the user. + */ +int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object) +{ + int len; + + if ((len = asn1_next_obj(buf, offset, ASN1_INTEGER)) < 0) + goto end_int_array; + + if (len > 1 && buf[*offset] == 0x00) /* ignore the negative byte */ + { + len--; + (*offset)++; + } + + *object = (uint8_t *)malloc(len); + memcpy(*object, &buf[*offset], len); + *offset += len; + +end_int_array: + return len; +} + +/** + * Get all the RSA private key specifics from an ASN.1 encoded file + */ +int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx) +{ + int offset = 7; + uint8_t *modulus = NULL, *priv_exp = NULL, *pub_exp = NULL; + int mod_len, priv_len, pub_len; +#ifdef CONFIG_BIGINT_CRT + uint8_t *p = NULL, *q = NULL, *dP = NULL, *dQ = NULL, *qInv = NULL; + int p_len, q_len, dP_len, dQ_len, qInv_len; +#endif + + /* not in der format */ + if (buf[0] != ASN1_SEQUENCE) /* basic sanity check */ + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: This is not a valid ASN.1 file\n"); +#endif + return X509_INVALID_PRIV_KEY; + } + + /* Use the private key to mix up the RNG if possible. */ + RNG_custom_init(buf, len); + + mod_len = asn1_get_int(buf, &offset, &modulus); + pub_len = asn1_get_int(buf, &offset, &pub_exp); + priv_len = asn1_get_int(buf, &offset, &priv_exp); + + if (mod_len <= 0 || pub_len <= 0 || priv_len <= 0) + return X509_INVALID_PRIV_KEY; + +#ifdef CONFIG_BIGINT_CRT + p_len = asn1_get_int(buf, &offset, &p); + q_len = asn1_get_int(buf, &offset, &q); + dP_len = asn1_get_int(buf, &offset, &dP); + dQ_len = asn1_get_int(buf, &offset, &dQ); + qInv_len = asn1_get_int(buf, &offset, &qInv); + + if (p_len <= 0 || q_len <= 0 || dP_len <= 0 || dQ_len <= 0 || qInv_len <= 0) + return X509_INVALID_PRIV_KEY; + + RSA_priv_key_new(rsa_ctx, + modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len, + p, p_len, q, p_len, dP, dP_len, dQ, dQ_len, qInv, qInv_len); + + free(p); + free(q); + free(dP); + free(dQ); + free(qInv); +#else + RSA_priv_key_new(rsa_ctx, + modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len); +#endif + + free(modulus); + free(priv_exp); + free(pub_exp); + return X509_OK; +} + +/** + * Get the time of a certificate. Ignore hours/minutes/seconds. + */ +static int asn1_get_utc_time(const uint8_t *buf, int *offset, time_t *t) +{ + int ret = X509_NOT_OK, len, t_offset; + struct tm tm; + + if (buf[(*offset)++] != ASN1_UTC_TIME) + goto end_utc_time; + + len = get_asn1_length(buf, offset); + t_offset = *offset; + + memset(&tm, 0, sizeof(struct tm)); + tm.tm_year = (buf[t_offset] - '0')*10 + (buf[t_offset+1] - '0'); + + if (tm.tm_year <= 50) /* 1951-2050 thing */ + { + tm.tm_year += 100; + } + + tm.tm_mon = (buf[t_offset+2] - '0')*10 + (buf[t_offset+3] - '0') - 1; + tm.tm_mday = (buf[t_offset+4] - '0')*10 + (buf[t_offset+5] - '0'); + *t = mktime(&tm); + *offset += len; + ret = X509_OK; + +end_utc_time: + return ret; +} + +/** + * Get the version type of a certificate (which we don't actually care about) + */ +int asn1_version(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK; + + (*offset) += 2; /* get past explicit tag */ + if (asn1_skip_obj(cert, offset, ASN1_INTEGER)) + goto end_version; + + ret = X509_OK; +end_version: + return ret; +} + +/** + * Retrieve the notbefore and notafter certificate times. + */ +int asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + return (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || + asn1_get_utc_time(cert, offset, &x509_ctx->not_before) || + asn1_get_utc_time(cert, offset, &x509_ctx->not_after)); +} + +/** + * Get the components of a distinguished name + */ +static int asn1_get_oid_x520(const uint8_t *buf, int *offset) +{ + int dn_type = 0; + int len; + + if ((len = asn1_next_obj(buf, offset, ASN1_OID)) < 0) + goto end_oid; + + /* expect a sequence of 2.5.4.[x] where x is a one of distinguished name + components we are interested in. */ + if (len == 3 && buf[(*offset)++] == 0x55 && buf[(*offset)++] == 0x04) + dn_type = buf[(*offset)++]; + else + { + *offset += len; /* skip over it */ + } + +end_oid: + return dn_type; +} + +/** + * Obtain an ASN.1 printable string type. + */ +static int asn1_get_printable_str(const uint8_t *buf, int *offset, char **str) +{ + int len = X509_NOT_OK; + int asn1_type = buf[*offset]; + + /* some certs have this awful crud in them for some reason */ + if (asn1_type != ASN1_PRINTABLE_STR && + asn1_type != ASN1_PRINTABLE_STR2 && + asn1_type != ASN1_TELETEX_STR && + asn1_type != ASN1_IA5_STR && + asn1_type != ASN1_UNICODE_STR) + goto end_pnt_str; + + (*offset)++; + len = get_asn1_length(buf, offset); + + if (asn1_type == ASN1_UNICODE_STR) + { + int i; + *str = (char *)malloc(len/2+1); /* allow for null */ + + for (i = 0; i < len; i += 2) + (*str)[i/2] = buf[*offset + i + 1]; + + (*str)[len/2] = 0; /* null terminate */ + } + else + { + *str = (char *)malloc(len+1); /* allow for null */ + memcpy(*str, &buf[*offset], len); + (*str)[len] = 0; /* null terminate */ + } + + *offset += len; + +end_pnt_str: + return len; +} + +/** + * Get the subject name (or the issuer) of a certificate. + */ +int asn1_name(const uint8_t *cert, int *offset, char *dn[]) +{ + int ret = X509_NOT_OK; + int dn_type; + char *tmp; + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0) + goto end_name; + + while (asn1_next_obj(cert, offset, ASN1_SET) >= 0) + { + int i, found = 0; + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || + (dn_type = asn1_get_oid_x520(cert, offset)) < 0) + goto end_name; + + tmp = NULL; + + if (asn1_get_printable_str(cert, offset, &tmp) < 0) + { + free(tmp); + goto end_name; + } + + /* find the distinguished named type */ + for (i = 0; i < X509_NUM_DN_TYPES; i++) + { + if (dn_type == g_dn_types[i]) + { + if (dn[i] == NULL) + { + dn[i] = tmp; + found = 1; + break; + } + } + } + + if (found == 0) /* not found so get rid of it */ + { + free(tmp); + } + } + + ret = X509_OK; +end_name: + return ret; +} + +/** + * Read the modulus and public exponent of a certificate. + */ +int asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK, mod_len, pub_len; + uint8_t *modulus = NULL, *pub_exp = NULL; + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(cert, offset, ASN1_SEQUENCE) || + asn1_next_obj(cert, offset, ASN1_BIT_STRING) < 0) + goto end_pub_key; + + (*offset)++; /* ignore the padding bit field */ + + if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0) + goto end_pub_key; + + mod_len = asn1_get_int(cert, offset, &modulus); + pub_len = asn1_get_int(cert, offset, &pub_exp); + + RSA_pub_key_new(&x509_ctx->rsa_ctx, modulus, mod_len, pub_exp, pub_len); + + free(modulus); + free(pub_exp); + ret = X509_OK; + +end_pub_key: + return ret; +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Read the signature of the certificate. + */ +int asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK; + + if (cert[(*offset)++] != ASN1_BIT_STRING) + goto end_sig; + + x509_ctx->sig_len = get_asn1_length(cert, offset)-1; + (*offset)++; /* ignore bit string padding bits */ + x509_ctx->signature = (uint8_t *)malloc(x509_ctx->sig_len); + memcpy(x509_ctx->signature, &cert[*offset], x509_ctx->sig_len); + *offset += x509_ctx->sig_len; + ret = X509_OK; + +end_sig: + return ret; +} + +/* + * Compare 2 distinguished name components for equality + * @return 0 if a match + */ +static int asn1_compare_dn_comp(const char *dn1, const char *dn2) +{ + int ret; + + if (dn1 == NULL && dn2 == NULL) + ret = 0; + else + ret = (dn1 && dn2) ? strcmp(dn1, dn2) : 1; + + return ret; +} + +/** + * Clean up all of the CA certificates. + */ +void remove_ca_certs(CA_CERT_CTX *ca_cert_ctx) +{ + int i = 0; + + if (ca_cert_ctx == NULL) + return; + + while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) + { + x509_free(ca_cert_ctx->cert[i]); + ca_cert_ctx->cert[i++] = NULL; + } + + free(ca_cert_ctx); +} + +/* + * Compare 2 distinguished names for equality + * @return 0 if a match + */ +int asn1_compare_dn(char * const dn1[], char * const dn2[]) +{ + int i; + + for (i = 0; i < X509_NUM_DN_TYPES; i++) + { + if (asn1_compare_dn_comp(dn1[i], dn2[i])) + return 1; + } + + return 0; /* all good */ +} + +int asn1_find_oid(const uint8_t* cert, int* offset, + const uint8_t* oid, int oid_length) +{ + int seqlen; + if ((seqlen = asn1_next_obj(cert, offset, ASN1_SEQUENCE))> 0) + { + int end = *offset + seqlen; + + while (*offset < end) + { + int type = cert[(*offset)++]; + int length = get_asn1_length(cert, offset); + int noffset = *offset + length; + + if (type == ASN1_SEQUENCE) + { + type = cert[(*offset)++]; + length = get_asn1_length(cert, offset); + + if (type == ASN1_OID && length == oid_length && + memcmp(cert + *offset, oid, oid_length) == 0) + { + *offset += oid_length; + return 1; + } + } + + *offset = noffset; + } + } + + return 0; +} + +int asn1_find_subjectaltname(const uint8_t* cert, int offset) +{ + if (asn1_find_oid(cert, &offset, sig_subject_alt_name, + SIG_SUBJECT_ALT_NAME_SIZE)) + { + return offset; + } + + return 0; +} + +#endif /* CONFIG_SSL_CERT_VERIFICATION */ + +/** + * Read the signature type of the certificate. We only support RSA-MD5 and + * RSA-SHA1 signature types. + */ +int asn1_signature_type(const uint8_t *cert, + int *offset, X509_CTX *x509_ctx) +{ + int ret = X509_NOT_OK, len; + + if (cert[(*offset)++] != ASN1_OID) + goto end_check_sig; + + len = get_asn1_length(cert, offset); + + if (len == 5 && memcmp(sig_sha1WithRSAEncrypt, &cert[*offset], + SIG_IIS6_OID_SIZE) == 0) + { + x509_ctx->sig_type = SIG_TYPE_SHA1; + } + else + { + if (memcmp(sig_oid_prefix, &cert[*offset], SIG_OID_PREFIX_SIZE)) + goto end_check_sig; /* unrecognised cert type */ + + x509_ctx->sig_type = cert[*offset + SIG_OID_PREFIX_SIZE]; + } + + *offset += len; + asn1_skip_obj(cert, offset, ASN1_NULL); /* if it's there */ + ret = X509_OK; + +end_check_sig: + return ret; +} + diff --git a/ccast/axTLS/bigint.c b/ccast/axTLS/bigint.c new file mode 100644 index 0000000..e9ca04c --- /dev/null +++ b/ccast/axTLS/bigint.c @@ -0,0 +1,1512 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * @defgroup bigint_api Big Integer API + * @brief The bigint implementation as used by the axTLS project. + * + * The bigint library is for RSA encryption/decryption as well as signing. + * This code tries to minimise use of malloc/free by maintaining a small + * cache. A bigint context may maintain state by being made "permanent". + * It be be later released with a bi_depermanent() and bi_free() call. + * + * It supports the following reduction techniques: + * - Classical + * - Barrett + * - Montgomery + * + * It also implements the following: + * - Karatsuba multiplication + * - Squaring + * - Sliding window exponentiation + * - Chinese Remainder Theorem (implemented in rsa.c). + * + * All the algorithms used are pretty standard, and designed for different + * data bus sizes. Negative numbers are not dealt with at all, so a subtraction + * may need to be tested for negativity. + * + * This library steals some ideas from Jef Poskanzer + * + * and GMP . It gets most of its implementation + * detail from "The Handbook of Applied Cryptography" + * + * @{ + */ + +#include +#include +#include +#include +#include +#include "os_port.h" +#include "bigint.h" + +#define V1 v->comps[v->size-1] /**< v1 for division */ +#define V2 v->comps[v->size-2] /**< v2 for division */ +#define U(j) tmp_u->comps[tmp_u->size-j-1] /**< uj for division */ +#define Q(j) quotient->comps[quotient->size-j-1] /**< qj for division */ + +static bigint *bi_int_multiply(BI_CTX *ctx, bigint *bi, comp i); +static bigint *bi_int_divide(BI_CTX *ctx, bigint *biR, comp denom); +static bigint *alloc(BI_CTX *ctx, int size); +static bigint *trim(bigint *bi); +static void more_comps(bigint *bi, int n); +#if defined(CONFIG_BIGINT_KARATSUBA) || defined(CONFIG_BIGINT_BARRETT) || \ + defined(CONFIG_BIGINT_MONTGOMERY) +static bigint *comp_right_shift(bigint *biR, int num_shifts); +static bigint *comp_left_shift(bigint *biR, int num_shifts); +#endif + +#ifdef CONFIG_BIGINT_CHECK_ON +static void check(const bigint *bi); +#else +#define check(A) /**< disappears in normal production mode */ +#endif + + +/** + * @brief Start a new bigint context. + * @return A bigint context. + */ +BI_CTX *bi_initialize(void) +{ + /* calloc() sets everything to zero */ + BI_CTX *ctx = (BI_CTX *)calloc(1, sizeof(BI_CTX)); + + /* the radix */ + ctx->bi_radix = alloc(ctx, 2); + ctx->bi_radix->comps[0] = 0; + ctx->bi_radix->comps[1] = 1; + bi_permanent(ctx->bi_radix); + return ctx; +} + +/** + * @brief Close the bigint context and free any resources. + * + * Free up any used memory - a check is done if all objects were not + * properly freed. + * @param ctx [in] The bigint session context. + */ +void bi_terminate(BI_CTX *ctx) +{ + bi_depermanent(ctx->bi_radix); + bi_free(ctx, ctx->bi_radix); + + if (ctx->active_count != 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("bi_terminate: there were %d un-freed bigints\n", + ctx->active_count); +#endif + abort(); + } + + bi_clear_cache(ctx); + free(ctx); +} + +/** + *@brief Clear the memory cache. + */ +void bi_clear_cache(BI_CTX *ctx) +{ + bigint *p, *pn; + + if (ctx->free_list == NULL) + return; + + for (p = ctx->free_list; p != NULL; p = pn) + { + pn = p->next; + free(p->comps); + free(p); + } + + ctx->free_count = 0; + ctx->free_list = NULL; +} + +/** + * @brief Increment the number of references to this object. + * It does not do a full copy. + * @param bi [in] The bigint to copy. + * @return A reference to the same bigint. + */ +bigint *bi_copy(bigint *bi) +{ + check(bi); + if (bi->refs != PERMANENT) + bi->refs++; + return bi; +} + +/** + * @brief Simply make a bigint object "unfreeable" if bi_free() is called on it. + * + * For this object to be freed, bi_depermanent() must be called. + * @param bi [in] The bigint to be made permanent. + */ +void bi_permanent(bigint *bi) +{ + check(bi); + if (bi->refs != 1) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("bi_permanent: refs was not 1\n"); +#endif + abort(); + } + + bi->refs = PERMANENT; +} + +/** + * @brief Take a permanent object and make it eligible for freedom. + * @param bi [in] The bigint to be made back to temporary. + */ +void bi_depermanent(bigint *bi) +{ + check(bi); + if (bi->refs != PERMANENT) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("bi_depermanent: bigint was not permanent\n"); +#endif + abort(); + } + + bi->refs = 1; +} + +/** + * @brief Free a bigint object so it can be used again. + * + * The memory itself it not actually freed, just tagged as being available + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint to be freed. + */ +void bi_free(BI_CTX *ctx, bigint *bi) +{ + check(bi); + if (bi->refs == PERMANENT) + { + return; + } + + if (--bi->refs > 0) + { + return; + } + + bi->next = ctx->free_list; + ctx->free_list = bi; + ctx->free_count++; + + if (--ctx->active_count < 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("bi_free: active_count went negative " + "- double-freed bigint?\n"); +#endif + abort(); + } +} + +/** + * @brief Convert an (unsigned) integer into a bigint. + * @param ctx [in] The bigint session context. + * @param i [in] The (unsigned) integer to be converted. + * + */ +bigint *int_to_bi(BI_CTX *ctx, comp i) +{ + bigint *biR = alloc(ctx, 1); + biR->comps[0] = i; + return biR; +} + +/** + * @brief Do a full copy of the bigint object. + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint object to be copied. + */ +bigint *bi_clone(BI_CTX *ctx, const bigint *bi) +{ + bigint *biR = alloc(ctx, bi->size); + check(bi); + memcpy(biR->comps, bi->comps, bi->size*COMP_BYTE_SIZE); + return biR; +} + +/** + * @brief Perform an addition operation between two bigints. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @return The result of the addition. + */ +bigint *bi_add(BI_CTX *ctx, bigint *bia, bigint *bib) +{ + int n; + comp carry = 0; + comp *pa, *pb; + + check(bia); + check(bib); + + n = max(bia->size, bib->size); + more_comps(bia, n+1); + more_comps(bib, n); + pa = bia->comps; + pb = bib->comps; + + do + { + comp sl, rl, cy1; + sl = *pa + *pb++; + rl = sl + carry; + cy1 = sl < *pa; + carry = cy1 | (rl < sl); + *pa++ = rl; + } while (--n != 0); + + *pa = carry; /* do overflow */ + bi_free(ctx, bib); + return trim(bia); +} + +/** + * @brief Perform a subtraction operation between two bigints. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @param is_negative [out] If defined, indicates that the result was negative. + * is_negative may be null. + * @return The result of the subtraction. The result is always positive. + */ +bigint *bi_subtract(BI_CTX *ctx, + bigint *bia, bigint *bib, int *is_negative) +{ + int n = bia->size; + comp *pa, *pb, carry = 0; + + check(bia); + check(bib); + + more_comps(bib, n); + pa = bia->comps; + pb = bib->comps; + + do + { + comp sl, rl, cy1; + sl = *pa - *pb++; + rl = sl - carry; + cy1 = sl > *pa; + carry = cy1 | (rl > sl); + *pa++ = rl; + } while (--n != 0); + + if (is_negative) /* indicate a negative result */ + { + *is_negative = carry; + } + + bi_free(ctx, trim(bib)); /* put bib back to the way it was */ + return trim(bia); +} + +/** + * Perform a multiply between a bigint an an (unsigned) integer + */ +static bigint *bi_int_multiply(BI_CTX *ctx, bigint *bia, comp b) +{ + int j = 0, n = bia->size; + bigint *biR = alloc(ctx, n + 1); + comp carry = 0; + comp *r = biR->comps; + comp *a = bia->comps; + + check(bia); + + /* clear things to start with */ + memset(r, 0, ((n+1)*COMP_BYTE_SIZE)); + + do + { + long_comp tmp = *r + (long_comp)a[j]*b + carry; + *r++ = (comp)tmp; /* downsize */ + carry = (comp)(tmp >> COMP_BIT_SIZE); + } while (++j < n); + + *r = carry; + bi_free(ctx, bia); + return trim(biR); +} + +/** + * @brief Does both division and modulo calculations. + * + * Used extensively when doing classical reduction. + * @param ctx [in] The bigint session context. + * @param u [in] A bigint which is the numerator. + * @param v [in] Either the denominator or the modulus depending on the mode. + * @param is_mod [n] Determines if this is a normal division (0) or a reduction + * (1). + * @return The result of the division/reduction. + */ +bigint *bi_divide(BI_CTX *ctx, bigint *u, bigint *v, int is_mod) +{ + int n = v->size, m = u->size-n; + int j = 0, orig_u_size = u->size; + uint8_t mod_offset = ctx->mod_offset; + comp d; + bigint *quotient, *tmp_u; + comp q_dash; + + check(u); + check(v); + + /* if doing reduction and we are < mod, then return mod */ + if (is_mod && bi_compare(v, u) > 0) + { + bi_free(ctx, v); + return u; + } + + quotient = alloc(ctx, m+1); + tmp_u = alloc(ctx, n+1); + v = trim(v); /* make sure we have no leading 0's */ + d = (comp)((long_comp)COMP_RADIX/(V1+1)); + + /* clear things to start with */ + memset(quotient->comps, 0, ((quotient->size)*COMP_BYTE_SIZE)); + + /* normalise */ + if (d > 1) + { + u = bi_int_multiply(ctx, u, d); + + if (is_mod) + { + v = ctx->bi_normalised_mod[mod_offset]; + } + else + { + v = bi_int_multiply(ctx, v, d); + } + } + + if (orig_u_size == u->size) /* new digit position u0 */ + { + more_comps(u, orig_u_size + 1); + } + + do + { + /* get a temporary short version of u */ + memcpy(tmp_u->comps, &u->comps[u->size-n-1-j], (n+1)*COMP_BYTE_SIZE); + + /* calculate q' */ + if (U(0) == V1) + { + q_dash = COMP_RADIX-1; + } + else + { + q_dash = (comp)(((long_comp)U(0)*COMP_RADIX + U(1))/V1); + + if (v->size > 1 && V2) + { + /* we are implementing the following: + if (V2*q_dash > (((U(0)*COMP_RADIX + U(1) - + q_dash*V1)*COMP_RADIX) + U(2))) ... */ + comp inner = (comp)((long_comp)COMP_RADIX*U(0) + U(1) - + (long_comp)q_dash*V1); + if ((long_comp)V2*q_dash > (long_comp)inner*COMP_RADIX + U(2)) + { + q_dash--; + } + } + } + + /* multiply and subtract */ + if (q_dash) + { + int is_negative; + tmp_u = bi_subtract(ctx, tmp_u, + bi_int_multiply(ctx, bi_copy(v), q_dash), &is_negative); + more_comps(tmp_u, n+1); + + Q(j) = q_dash; + + /* add back */ + if (is_negative) + { + Q(j)--; + tmp_u = bi_add(ctx, tmp_u, bi_copy(v)); + + /* lop off the carry */ + tmp_u->size--; + v->size--; + } + } + else + { + Q(j) = 0; + } + + /* copy back to u */ + memcpy(&u->comps[u->size-n-1-j], tmp_u->comps, (n+1)*COMP_BYTE_SIZE); + } while (++j <= m); + + bi_free(ctx, tmp_u); + bi_free(ctx, v); + + if (is_mod) /* get the remainder */ + { + bi_free(ctx, quotient); + return bi_int_divide(ctx, trim(u), d); + } + else /* get the quotient */ + { + bi_free(ctx, u); + return trim(quotient); + } +} + +/* + * Perform an integer divide on a bigint. + */ +static bigint *bi_int_divide(BI_CTX *ctx, bigint *biR, comp denom) +{ + int i = biR->size - 1; + long_comp r = 0; + + check(biR); + + do + { + r = (r<comps[i]; + biR->comps[i] = (comp)(r / denom); + r %= denom; + } while (--i >= 0); + + return trim(biR); +} + +#ifdef CONFIG_BIGINT_MONTGOMERY +/** + * There is a need for the value of integer N' such that B^-1(B-1)-N^-1N'=1, + * where B^-1(B-1) mod N=1. Actually, only the least significant part of + * N' is needed, hence the definition N0'=N' mod b. We reproduce below the + * simple algorithm from an article by Dusse and Kaliski to efficiently + * find N0' from N0 and b */ +static comp modular_inverse(bigint *bim) +{ + int i; + comp t = 1; + comp two_2_i_minus_1 = 2; /* 2^(i-1) */ + long_comp two_2_i = 4; /* 2^i */ + comp N = bim->comps[0]; + + for (i = 2; i <= COMP_BIT_SIZE; i++) + { + if ((long_comp)N*t%two_2_i >= two_2_i_minus_1) + { + t += two_2_i_minus_1; + } + + two_2_i_minus_1 <<= 1; + two_2_i <<= 1; + } + + return (comp)(COMP_RADIX-t); +} +#endif + +#if defined(CONFIG_BIGINT_KARATSUBA) || defined(CONFIG_BIGINT_BARRETT) || \ + defined(CONFIG_BIGINT_MONTGOMERY) +/** + * Take each component and shift down (in terms of components) + */ +static bigint *comp_right_shift(bigint *biR, int num_shifts) +{ + int i = biR->size-num_shifts; + comp *x = biR->comps; + comp *y = &biR->comps[num_shifts]; + + check(biR); + + if (i <= 0) /* have we completely right shifted? */ + { + biR->comps[0] = 0; /* return 0 */ + biR->size = 1; + return biR; + } + + do + { + *x++ = *y++; + } while (--i > 0); + + biR->size -= num_shifts; + return biR; +} + +/** + * Take each component and shift it up (in terms of components) + */ +static bigint *comp_left_shift(bigint *biR, int num_shifts) +{ + int i = biR->size-1; + comp *x, *y; + + check(biR); + + if (num_shifts <= 0) + { + return biR; + } + + more_comps(biR, biR->size + num_shifts); + + x = &biR->comps[i+num_shifts]; + y = &biR->comps[i]; + + do + { + *x-- = *y--; + } while (i--); + + memset(biR->comps, 0, num_shifts*COMP_BYTE_SIZE); /* zero LS comps */ + return biR; +} +#endif + +/** + * @brief Allow a binary sequence to be imported as a bigint. + * @param ctx [in] The bigint session context. + * @param data [in] The data to be converted. + * @param size [in] The number of bytes of data. + * @return A bigint representing this data. + */ +bigint *bi_import(BI_CTX *ctx, const uint8_t *data, int size) +{ + bigint *biR = alloc(ctx, (size+COMP_BYTE_SIZE-1)/COMP_BYTE_SIZE); + int i, j = 0, offset = 0; + + memset(biR->comps, 0, biR->size*COMP_BYTE_SIZE); + + for (i = size-1; i >= 0; i--) + { + biR->comps[offset] += data[i] << (j*8); + + if (++j == COMP_BYTE_SIZE) + { + j = 0; + offset ++; + } + } + + return trim(biR); +} + +#ifdef CONFIG_SSL_FULL_MODE +/** + * @brief The testharness uses this code to import text hex-streams and + * convert them into bigints. + * @param ctx [in] The bigint session context. + * @param data [in] A string consisting of hex characters. The characters must + * be in upper case. + * @return A bigint representing this data. + */ +bigint *bi_str_import(BI_CTX *ctx, const char *data) +{ + int size = strlen(data); + bigint *biR = alloc(ctx, (size+COMP_NUM_NIBBLES-1)/COMP_NUM_NIBBLES); + int i, j = 0, offset = 0; + memset(biR->comps, 0, biR->size*COMP_BYTE_SIZE); + + for (i = size-1; i >= 0; i--) + { + int num = (data[i] <= '9') ? (data[i] - '0') : (data[i] - 'A' + 10); + biR->comps[offset] += num << (j*4); + + if (++j == COMP_NUM_NIBBLES) + { + j = 0; + offset ++; + } + } + + return biR; +} + +void bi_print(const char *label, bigint *x) +{ + int i, j; + + if (x == NULL) + { + printf("%s: (null)\n", label); + return; + } + + printf("%s: (size %d)\n", label, x->size); + for (i = x->size-1; i >= 0; i--) + { + for (j = COMP_NUM_NIBBLES-1; j >= 0; j--) + { + comp mask = 0x0f << (j*4); + comp num = (x->comps[i] & mask) >> (j*4); + putc((num <= 9) ? (num + '0') : (num + 'A' - 10), stdout); + } + } + + printf("\n"); +} +#endif + +/** + * @brief Take a bigint and convert it into a byte sequence. + * + * This is useful after a decrypt operation. + * @param ctx [in] The bigint session context. + * @param x [in] The bigint to be converted. + * @param data [out] The converted data as a byte stream. + * @param size [in] The maximum size of the byte stream. Unused bytes will be + * zeroed. + */ +void bi_export(BI_CTX *ctx, bigint *x, uint8_t *data, int size) +{ + int i, j, k = size-1; + + check(x); + memset(data, 0, size); /* ensure all leading 0's are cleared */ + + for (i = 0; i < x->size; i++) + { + for (j = 0; j < COMP_BYTE_SIZE; j++) + { + comp mask = 0xff << (j*8); + int num = (x->comps[i] & mask) >> (j*8); + data[k--] = num; + + if (k < 0) + { + goto buf_done; + } + } + } +buf_done: + + bi_free(ctx, x); +} + +/** + * @brief Pre-calculate some of the expensive steps in reduction. + * + * This function should only be called once (normally when a session starts). + * When the session is over, bi_free_mod() should be called. bi_mod_power() + * relies on this function being called. + * @param ctx [in] The bigint session context. + * @param bim [in] The bigint modulus that will be used. + * @param mod_offset [in] There are three moduluii that can be stored - the + * standard modulus, and its two primes p and q. This offset refers to which + * modulus we are referring to. + * @see bi_free_mod(), bi_mod_power(). + */ +void bi_set_mod(BI_CTX *ctx, bigint *bim, int mod_offset) +{ + int k = bim->size; + comp d = (comp)((long_comp)COMP_RADIX/(bim->comps[k-1]+1)); +#ifdef CONFIG_BIGINT_MONTGOMERY + bigint *R, *R2; +#endif + + ctx->bi_mod[mod_offset] = bim; + bi_permanent(ctx->bi_mod[mod_offset]); + ctx->bi_normalised_mod[mod_offset] = bi_int_multiply(ctx, bim, d); + bi_permanent(ctx->bi_normalised_mod[mod_offset]); + +#if defined(CONFIG_BIGINT_MONTGOMERY) + /* set montgomery variables */ + R = comp_left_shift(bi_clone(ctx, ctx->bi_radix), k-1); /* R */ + R2 = comp_left_shift(bi_clone(ctx, ctx->bi_radix), k*2-1); /* R^2 */ + ctx->bi_RR_mod_m[mod_offset] = bi_mod(ctx, R2); /* R^2 mod m */ + ctx->bi_R_mod_m[mod_offset] = bi_mod(ctx, R); /* R mod m */ + + bi_permanent(ctx->bi_RR_mod_m[mod_offset]); + bi_permanent(ctx->bi_R_mod_m[mod_offset]); + + ctx->N0_dash[mod_offset] = modular_inverse(ctx->bi_mod[mod_offset]); + +#elif defined (CONFIG_BIGINT_BARRETT) + ctx->bi_mu[mod_offset] = + bi_divide(ctx, comp_left_shift( + bi_clone(ctx, ctx->bi_radix), k*2-1), ctx->bi_mod[mod_offset], 0); + bi_permanent(ctx->bi_mu[mod_offset]); +#endif +} + +/** + * @brief Used when cleaning various bigints at the end of a session. + * @param ctx [in] The bigint session context. + * @param mod_offset [in] The offset to use. + * @see bi_set_mod(). + */ +void bi_free_mod(BI_CTX *ctx, int mod_offset) +{ + bi_depermanent(ctx->bi_mod[mod_offset]); + bi_free(ctx, ctx->bi_mod[mod_offset]); +#if defined (CONFIG_BIGINT_MONTGOMERY) + bi_depermanent(ctx->bi_RR_mod_m[mod_offset]); + bi_depermanent(ctx->bi_R_mod_m[mod_offset]); + bi_free(ctx, ctx->bi_RR_mod_m[mod_offset]); + bi_free(ctx, ctx->bi_R_mod_m[mod_offset]); +#elif defined(CONFIG_BIGINT_BARRETT) + bi_depermanent(ctx->bi_mu[mod_offset]); + bi_free(ctx, ctx->bi_mu[mod_offset]); +#endif + bi_depermanent(ctx->bi_normalised_mod[mod_offset]); + bi_free(ctx, ctx->bi_normalised_mod[mod_offset]); +} + +/** + * Perform a standard multiplication between two bigints. + * + * Barrett reduction has no need for some parts of the product, so ignore bits + * of the multiply. This routine gives Barrett its big performance + * improvements over Classical/Montgomery reduction methods. + */ +static bigint *regular_multiply(BI_CTX *ctx, bigint *bia, bigint *bib, + int inner_partial, int outer_partial) +{ + int i = 0, j; + int n = bia->size; + int t = bib->size; + bigint *biR = alloc(ctx, n + t); + comp *sr = biR->comps; + comp *sa = bia->comps; + comp *sb = bib->comps; + + check(bia); + check(bib); + + /* clear things to start with */ + memset(biR->comps, 0, ((n+t)*COMP_BYTE_SIZE)); + + do + { + long_comp tmp; + comp carry = 0; + int r_index = i; + j = 0; + + if (outer_partial && outer_partial-i > 0 && outer_partial < n) + { + r_index = outer_partial-1; + j = outer_partial-i-1; + } + + do + { + if (inner_partial && r_index >= inner_partial) + { + break; + } + + tmp = sr[r_index] + ((long_comp)sa[j])*sb[i] + carry; + sr[r_index++] = (comp)tmp; /* downsize */ + carry = tmp >> COMP_BIT_SIZE; + } while (++j < n); + + sr[r_index] = carry; + } while (++i < t); + + bi_free(ctx, bia); + bi_free(ctx, bib); + return trim(biR); +} + +#ifdef CONFIG_BIGINT_KARATSUBA +/* + * Karatsuba improves on regular multiplication due to only 3 multiplications + * being done instead of 4. The additional additions/subtractions are O(N) + * rather than O(N^2) and so for big numbers it saves on a few operations + */ +static bigint *karatsuba(BI_CTX *ctx, bigint *bia, bigint *bib, int is_square) +{ + bigint *x0, *x1; + bigint *p0, *p1, *p2; + int m; + + if (is_square) + { + m = (bia->size + 1)/2; + } + else + { + m = (max(bia->size, bib->size) + 1)/2; + } + + x0 = bi_clone(ctx, bia); + x0->size = m; + x1 = bi_clone(ctx, bia); + comp_right_shift(x1, m); + bi_free(ctx, bia); + + /* work out the 3 partial products */ + if (is_square) + { + p0 = bi_square(ctx, bi_copy(x0)); + p2 = bi_square(ctx, bi_copy(x1)); + p1 = bi_square(ctx, bi_add(ctx, x0, x1)); + } + else /* normal multiply */ + { + bigint *y0, *y1; + y0 = bi_clone(ctx, bib); + y0->size = m; + y1 = bi_clone(ctx, bib); + comp_right_shift(y1, m); + bi_free(ctx, bib); + + p0 = bi_multiply(ctx, bi_copy(x0), bi_copy(y0)); + p2 = bi_multiply(ctx, bi_copy(x1), bi_copy(y1)); + p1 = bi_multiply(ctx, bi_add(ctx, x0, x1), bi_add(ctx, y0, y1)); + } + + p1 = bi_subtract(ctx, + bi_subtract(ctx, p1, bi_copy(p2), NULL), bi_copy(p0), NULL); + + comp_left_shift(p1, m); + comp_left_shift(p2, 2*m); + return bi_add(ctx, p1, bi_add(ctx, p0, p2)); +} +#endif + +/** + * @brief Perform a multiplication operation between two bigints. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @return The result of the multiplication. + */ +bigint *bi_multiply(BI_CTX *ctx, bigint *bia, bigint *bib) +{ + check(bia); + check(bib); + +#ifdef CONFIG_BIGINT_KARATSUBA + if (min(bia->size, bib->size) < MUL_KARATSUBA_THRESH) + { + return regular_multiply(ctx, bia, bib, 0, 0); + } + + return karatsuba(ctx, bia, bib, 0); +#else + return regular_multiply(ctx, bia, bib, 0, 0); +#endif +} + +#ifdef CONFIG_BIGINT_SQUARE +/* + * Perform the actual square operion. It takes into account overflow. + */ +static bigint *regular_square(BI_CTX *ctx, bigint *bi) +{ + int t = bi->size; + int i = 0, j; + bigint *biR = alloc(ctx, t*2+1); + comp *w = biR->comps; + comp *x = bi->comps; + long_comp carry; + memset(w, 0, biR->size*COMP_BYTE_SIZE); + + do + { + long_comp tmp = w[2*i] + (long_comp)x[i]*x[i]; + w[2*i] = (comp)tmp; + carry = tmp >> COMP_BIT_SIZE; + + for (j = i+1; j < t; j++) + { + uint8_t c = 0; + long_comp xx = (long_comp)x[i]*x[j]; + if ((COMP_MAX-xx) < xx) + c = 1; + + tmp = (xx<<1); + + if ((COMP_MAX-tmp) < w[i+j]) + c = 1; + + tmp += w[i+j]; + + if ((COMP_MAX-tmp) < carry) + c = 1; + + tmp += carry; + w[i+j] = (comp)tmp; + carry = tmp >> COMP_BIT_SIZE; + + if (c) + carry += COMP_RADIX; + } + + tmp = w[i+t] + carry; + w[i+t] = (comp)tmp; + w[i+t+1] = tmp >> COMP_BIT_SIZE; + } while (++i < t); + + bi_free(ctx, bi); + return trim(biR); +} + +/** + * @brief Perform a square operation on a bigint. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @return The result of the multiplication. + */ +bigint *bi_square(BI_CTX *ctx, bigint *bia) +{ + check(bia); + +#ifdef CONFIG_BIGINT_KARATSUBA + if (bia->size < SQU_KARATSUBA_THRESH) + { + return regular_square(ctx, bia); + } + + return karatsuba(ctx, bia, NULL, 1); +#else + return regular_square(ctx, bia); +#endif +} +#endif + +/** + * @brief Compare two bigints. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @return -1 if smaller, 1 if larger and 0 if equal. + */ +int bi_compare(bigint *bia, bigint *bib) +{ + int r, i; + + check(bia); + check(bib); + + if (bia->size > bib->size) + r = 1; + else if (bia->size < bib->size) + r = -1; + else + { + comp *a = bia->comps; + comp *b = bib->comps; + + /* Same number of components. Compare starting from the high end + * and working down. */ + r = 0; + i = bia->size - 1; + + do + { + if (a[i] > b[i]) + { + r = 1; + break; + } + else if (a[i] < b[i]) + { + r = -1; + break; + } + } while (--i >= 0); + } + + return r; +} + +/* + * Allocate and zero more components. Does not consume bi. + */ +static void more_comps(bigint *bi, int n) +{ + if (n > bi->max_comps) + { + bi->max_comps = max(bi->max_comps * 2, n); + bi->comps = (comp*)realloc(bi->comps, bi->max_comps * COMP_BYTE_SIZE); + } + + if (n > bi->size) + { + memset(&bi->comps[bi->size], 0, (n-bi->size)*COMP_BYTE_SIZE); + } + + bi->size = n; +} + +/* + * Make a new empty bigint. It may just use an old one if one is available. + * Otherwise get one off the heap. + */ +static bigint *alloc(BI_CTX *ctx, int size) +{ + bigint *biR; + + /* Can we recycle an old bigint? */ + if (ctx->free_list != NULL) + { + biR = ctx->free_list; + ctx->free_list = biR->next; + ctx->free_count--; + + if (biR->refs != 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("alloc: refs was not 0\n"); +#endif + abort(); /* create a stack trace from a core dump */ + } + + more_comps(biR, size); + } + else + { + /* No free bigints available - create a new one. */ + biR = (bigint *)malloc(sizeof(bigint)); + biR->comps = (comp*)malloc(size * COMP_BYTE_SIZE); + biR->max_comps = size; /* give some space to spare */ + } + + biR->size = size; + biR->refs = 1; + biR->next = NULL; + ctx->active_count++; + return biR; +} + +/* + * Work out the highest '1' bit in an exponent. Used when doing sliding-window + * exponentiation. + */ +static int find_max_exp_index(bigint *biexp) +{ + int i = COMP_BIT_SIZE-1; + comp shift = COMP_RADIX/2; + comp test = biexp->comps[biexp->size-1]; /* assume no leading zeroes */ + + check(biexp); + + do + { + if (test & shift) + { + return i+(biexp->size-1)*COMP_BIT_SIZE; + } + + shift >>= 1; + } while (i-- != 0); + + return -1; /* error - must have been a leading 0 */ +} + +/* + * Is a particular bit is an exponent 1 or 0? Used when doing sliding-window + * exponentiation. + */ +static int exp_bit_is_one(bigint *biexp, int offset) +{ + comp test = biexp->comps[offset / COMP_BIT_SIZE]; + int num_shifts = offset % COMP_BIT_SIZE; + comp shift = 1; + int i; + + check(biexp); + + for (i = 0; i < num_shifts; i++) + { + shift <<= 1; + } + + return (test & shift) != 0; +} + +#ifdef CONFIG_BIGINT_CHECK_ON +/* + * Perform a sanity check on bi. + */ +static void check(const bigint *bi) +{ + if (bi->refs <= 0) + { + printf("check: zero or negative refs in bigint\n"); + abort(); + } + + if (bi->next != NULL) + { + printf("check: attempt to use a bigint from " + "the free list\n"); + abort(); + } +} +#endif + +/* + * Delete any leading 0's (and allow for 0). + */ +static bigint *trim(bigint *bi) +{ + check(bi); + + while (bi->comps[bi->size-1] == 0 && bi->size > 1) + { + bi->size--; + } + + return bi; +} + +#if defined(CONFIG_BIGINT_MONTGOMERY) +/** + * @brief Perform a single montgomery reduction. + * @param ctx [in] The bigint session context. + * @param bixy [in] A bigint. + * @return The result of the montgomery reduction. + */ +bigint *bi_mont(BI_CTX *ctx, bigint *bixy) +{ + int i = 0, n; + uint8_t mod_offset = ctx->mod_offset; + bigint *bim = ctx->bi_mod[mod_offset]; + comp mod_inv = ctx->N0_dash[mod_offset]; + + check(bixy); + + if (ctx->use_classical) /* just use classical instead */ + { + return bi_mod(ctx, bixy); + } + + n = bim->size; + + do + { + bixy = bi_add(ctx, bixy, comp_left_shift( + bi_int_multiply(ctx, bim, bixy->comps[i]*mod_inv), i)); + } while (++i < n); + + comp_right_shift(bixy, n); + + if (bi_compare(bixy, bim) >= 0) + { + bixy = bi_subtract(ctx, bixy, bim, NULL); + } + + return bixy; +} + +#elif defined(CONFIG_BIGINT_BARRETT) +/* + * Stomp on the most significant components to give the illusion of a "mod base + * radix" operation + */ +static bigint *comp_mod(bigint *bi, int mod) +{ + check(bi); + + if (bi->size > mod) + { + bi->size = mod; + } + + return bi; +} + +/** + * @brief Perform a single Barrett reduction. + * @param ctx [in] The bigint session context. + * @param bi [in] A bigint. + * @return The result of the Barrett reduction. + */ +bigint *bi_barrett(BI_CTX *ctx, bigint *bi) +{ + bigint *q1, *q2, *q3, *r1, *r2, *r; + uint8_t mod_offset = ctx->mod_offset; + bigint *bim = ctx->bi_mod[mod_offset]; + int k = bim->size; + + check(bi); + check(bim); + + /* use Classical method instead - Barrett cannot help here */ + if (bi->size > k*2) + { + return bi_mod(ctx, bi); + } + + q1 = comp_right_shift(bi_clone(ctx, bi), k-1); + + /* do outer partial multiply */ + q2 = regular_multiply(ctx, q1, ctx->bi_mu[mod_offset], 0, k-1); + q3 = comp_right_shift(q2, k+1); + r1 = comp_mod(bi, k+1); + + /* do inner partial multiply */ + r2 = comp_mod(regular_multiply(ctx, q3, bim, k+1, 0), k+1); + r = bi_subtract(ctx, r1, r2, NULL); + + /* if (r >= m) r = r - m; */ + if (bi_compare(r, bim) >= 0) + { + r = bi_subtract(ctx, r, bim, NULL); + } + + return r; +} +#endif /* CONFIG_BIGINT_BARRETT */ + +#ifdef CONFIG_BIGINT_SLIDING_WINDOW +/* + * Work out g1, g3, g5, g7... etc for the sliding-window algorithm + */ +static void precompute_slide_window(BI_CTX *ctx, int window, bigint *g1) +{ + int k = 1, i; + bigint *g2; + + for (i = 0; i < window-1; i++) /* compute 2^(window-1) */ + { + k <<= 1; + } + + ctx->g = (bigint **)malloc(k*sizeof(bigint *)); + ctx->g[0] = bi_clone(ctx, g1); + bi_permanent(ctx->g[0]); + g2 = bi_residue(ctx, bi_square(ctx, ctx->g[0])); /* g^2 */ + + for (i = 1; i < k; i++) + { + ctx->g[i] = bi_residue(ctx, bi_multiply(ctx, ctx->g[i-1], bi_copy(g2))); + bi_permanent(ctx->g[i]); + } + + bi_free(ctx, g2); + ctx->window = k; +} +#endif + +/** + * @brief Perform a modular exponentiation. + * + * This function requires bi_set_mod() to have been called previously. This is + * one of the optimisations used for performance. + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint on which to perform the mod power operation. + * @param biexp [in] The bigint exponent. + * @return The result of the mod exponentiation operation + * @see bi_set_mod(). + */ +bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp) +{ + int i = find_max_exp_index(biexp), j, window_size = 1; + bigint *biR = int_to_bi(ctx, 1); + +#if defined(CONFIG_BIGINT_MONTGOMERY) + uint8_t mod_offset = ctx->mod_offset; + if (!ctx->use_classical) + { + /* preconvert */ + bi = bi_mont(ctx, + bi_multiply(ctx, bi, ctx->bi_RR_mod_m[mod_offset])); /* x' */ + bi_free(ctx, biR); + biR = ctx->bi_R_mod_m[mod_offset]; /* A */ + } +#endif + + check(bi); + check(biexp); + +#ifdef CONFIG_BIGINT_SLIDING_WINDOW + for (j = i; j > 32; j /= 5) /* work out an optimum size */ + window_size++; + + /* work out the slide constants */ + precompute_slide_window(ctx, window_size, bi); +#else /* just one constant */ + ctx->g = (bigint **)malloc(sizeof(bigint *)); + ctx->g[0] = bi_clone(ctx, bi); + ctx->window = 1; + bi_permanent(ctx->g[0]); +#endif + + /* if sliding-window is off, then only one bit will be done at a time and + * will reduce to standard left-to-right exponentiation */ + do + { + if (exp_bit_is_one(biexp, i)) + { + int l = i-window_size+1; + int part_exp = 0; + + if (l < 0) /* LSB of exponent will always be 1 */ + l = 0; + else + { + while (exp_bit_is_one(biexp, l) == 0) + l++; /* go back up */ + } + + /* build up the section of the exponent */ + for (j = i; j >= l; j--) + { + biR = bi_residue(ctx, bi_square(ctx, biR)); + if (exp_bit_is_one(biexp, j)) + part_exp++; + + if (j != l) + part_exp <<= 1; + } + + part_exp = (part_exp-1)/2; /* adjust for array */ + biR = bi_residue(ctx, bi_multiply(ctx, biR, ctx->g[part_exp])); + i = l-1; + } + else /* square it */ + { + biR = bi_residue(ctx, bi_square(ctx, biR)); + i--; + } + } while (i >= 0); + + /* cleanup */ + for (i = 0; i < ctx->window; i++) + { + bi_depermanent(ctx->g[i]); + bi_free(ctx, ctx->g[i]); + } + + free(ctx->g); + bi_free(ctx, bi); + bi_free(ctx, biexp); +#if defined CONFIG_BIGINT_MONTGOMERY + return ctx->use_classical ? biR : bi_mont(ctx, biR); /* convert back */ +#else /* CONFIG_BIGINT_CLASSICAL or CONFIG_BIGINT_BARRETT */ + return biR; +#endif +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * @brief Perform a modular exponentiation using a temporary modulus. + * + * We need this function to check the signatures of certificates. The modulus + * of this function is temporary as it's just used for authentication. + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint to perform the exp/mod. + * @param bim [in] The temporary modulus. + * @param biexp [in] The bigint exponent. + * @return The result of the mod exponentiation operation + * @see bi_set_mod(). + */ +bigint *bi_mod_power2(BI_CTX *ctx, bigint *bi, bigint *bim, bigint *biexp) +{ + bigint *biR, *tmp_biR; + + /* Set up a temporary bigint context and transfer what we need between + * them. We need to do this since we want to keep the original modulus + * which is already in this context. This operation is only called when + * doing peer verification, and so is not expensive :-) */ + BI_CTX *tmp_ctx = bi_initialize(); + bi_set_mod(tmp_ctx, bi_clone(tmp_ctx, bim), BIGINT_M_OFFSET); + tmp_biR = bi_mod_power(tmp_ctx, + bi_clone(tmp_ctx, bi), + bi_clone(tmp_ctx, biexp)); + biR = bi_clone(ctx, tmp_biR); + bi_free(tmp_ctx, tmp_biR); + bi_free_mod(tmp_ctx, BIGINT_M_OFFSET); + bi_terminate(tmp_ctx); + + bi_free(ctx, bi); + bi_free(ctx, bim); + bi_free(ctx, biexp); + return biR; +} +#endif + +#ifdef CONFIG_BIGINT_CRT +/** + * @brief Use the Chinese Remainder Theorem to quickly perform RSA decrypts. + * + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint to perform the exp/mod. + * @param dP [in] CRT's dP bigint + * @param dQ [in] CRT's dQ bigint + * @param p [in] CRT's p bigint + * @param q [in] CRT's q bigint + * @param qInv [in] CRT's qInv bigint + * @return The result of the CRT operation + */ +bigint *bi_crt(BI_CTX *ctx, bigint *bi, + bigint *dP, bigint *dQ, + bigint *p, bigint *q, bigint *qInv) +{ + bigint *m1, *m2, *h; + + /* Montgomery has a condition the 0 < x, y < m and these products violate + * that condition. So disable Montgomery when using CRT */ +#if defined(CONFIG_BIGINT_MONTGOMERY) + ctx->use_classical = 1; +#endif + ctx->mod_offset = BIGINT_P_OFFSET; + m1 = bi_mod_power(ctx, bi_copy(bi), dP); + + ctx->mod_offset = BIGINT_Q_OFFSET; + m2 = bi_mod_power(ctx, bi, dQ); + + h = bi_subtract(ctx, bi_add(ctx, m1, p), bi_copy(m2), NULL); + h = bi_multiply(ctx, h, qInv); + ctx->mod_offset = BIGINT_P_OFFSET; + h = bi_residue(ctx, h); +#if defined(CONFIG_BIGINT_MONTGOMERY) + ctx->use_classical = 0; /* reset for any further operation */ +#endif + return bi_add(ctx, m2, bi_multiply(ctx, q, h)); +} +#endif +/** @} */ diff --git a/ccast/axTLS/bigint.h b/ccast/axTLS/bigint.h new file mode 100644 index 0000000..2966a3e --- /dev/null +++ b/ccast/axTLS/bigint.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +#ifndef BIGINT_HEADER +#define BIGINT_HEADER + +#include "crypto.h" + +BI_CTX *bi_initialize(void); +void bi_terminate(BI_CTX *ctx); +void bi_permanent(bigint *bi); +void bi_depermanent(bigint *bi); +void bi_clear_cache(BI_CTX *ctx); +void bi_free(BI_CTX *ctx, bigint *bi); +bigint *bi_copy(bigint *bi); +bigint *bi_clone(BI_CTX *ctx, const bigint *bi); +void bi_export(BI_CTX *ctx, bigint *bi, uint8_t *data, int size); +bigint *bi_import(BI_CTX *ctx, const uint8_t *data, int len); +bigint *int_to_bi(BI_CTX *ctx, comp i); + +/* the functions that actually do something interesting */ +bigint *bi_add(BI_CTX *ctx, bigint *bia, bigint *bib); +bigint *bi_subtract(BI_CTX *ctx, bigint *bia, + bigint *bib, int *is_negative); +bigint *bi_divide(BI_CTX *ctx, bigint *bia, bigint *bim, int is_mod); +bigint *bi_multiply(BI_CTX *ctx, bigint *bia, bigint *bib); +bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp); +bigint *bi_mod_power2(BI_CTX *ctx, bigint *bi, bigint *bim, bigint *biexp); +int bi_compare(bigint *bia, bigint *bib); +void bi_set_mod(BI_CTX *ctx, bigint *bim, int mod_offset); +void bi_free_mod(BI_CTX *ctx, int mod_offset); + +#ifdef CONFIG_SSL_FULL_MODE +void bi_print(const char *label, bigint *bi); +bigint *bi_str_import(BI_CTX *ctx, const char *data); +#endif + +/** + * @def bi_mod + * Find the residue of B. bi_set_mod() must be called before hand. + */ +#define bi_mod(A, B) bi_divide(A, B, ctx->bi_mod[ctx->mod_offset], 1) + +/** + * bi_residue() is technically the same as bi_mod(), but it uses the + * appropriate reduction technique (which is bi_mod() when doing classical + * reduction). + */ +#if defined(CONFIG_BIGINT_MONTGOMERY) +#define bi_residue(A, B) bi_mont(A, B) +bigint *bi_mont(BI_CTX *ctx, bigint *bixy); +#elif defined(CONFIG_BIGINT_BARRETT) +#define bi_residue(A, B) bi_barrett(A, B) +bigint *bi_barrett(BI_CTX *ctx, bigint *bi); +#else /* if defined(CONFIG_BIGINT_CLASSICAL) */ +#define bi_residue(A, B) bi_mod(A, B) +#endif + +#ifdef CONFIG_BIGINT_SQUARE +bigint *bi_square(BI_CTX *ctx, bigint *bi); +#else +#define bi_square(A, B) bi_multiply(A, bi_copy(B), B) +#endif + +#ifdef CONFIG_BIGINT_CRT +bigint *bi_crt(BI_CTX *ctx, bigint *bi, + bigint *dP, bigint *dQ, + bigint *p, bigint *q, + bigint *qInv); +#endif + +#endif diff --git a/ccast/axTLS/bigint_impl.h b/ccast/axTLS/bigint_impl.h new file mode 100644 index 0000000..820620d --- /dev/null +++ b/ccast/axTLS/bigint_impl.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +#ifndef BIGINT_IMPL_HEADER +#define BIGINT_IMPL_HEADER + +/* Maintain a number of precomputed variables when doing reduction */ +#define BIGINT_M_OFFSET 0 /**< Normal modulo offset. */ +#ifdef CONFIG_BIGINT_CRT +#define BIGINT_P_OFFSET 1 /**< p modulo offset. */ +#define BIGINT_Q_OFFSET 2 /**< q module offset. */ +#define BIGINT_NUM_MODS 3 /**< The number of modulus constants used. */ +#else +#define BIGINT_NUM_MODS 1 +#endif + +/* Architecture specific functions for big ints */ +#if defined(CONFIG_INTEGER_8BIT) +#define COMP_RADIX 256U /**< Max component + 1 */ +#define COMP_MAX 0xFFFFU/**< (Max dbl comp -1) */ +#define COMP_BIT_SIZE 8 /**< Number of bits in a component. */ +#define COMP_BYTE_SIZE 1 /**< Number of bytes in a component. */ +#define COMP_NUM_NIBBLES 2 /**< Used For diagnostics only. */ +typedef uint8_t comp; /**< A single precision component. */ +typedef uint16_t long_comp; /**< A double precision component. */ +typedef int16_t slong_comp; /**< A signed double precision component. */ +#elif defined(CONFIG_INTEGER_16BIT) +#define COMP_RADIX 65536U /**< Max component + 1 */ +#define COMP_MAX 0xFFFFFFFFU/**< (Max dbl comp -1) */ +#define COMP_BIT_SIZE 16 /**< Number of bits in a component. */ +#define COMP_BYTE_SIZE 2 /**< Number of bytes in a component. */ +#define COMP_NUM_NIBBLES 4 /**< Used For diagnostics only. */ +typedef uint16_t comp; /**< A single precision component. */ +typedef uint32_t long_comp; /**< A double precision component. */ +typedef int32_t slong_comp; /**< A signed double precision component. */ +#else /* regular 32 bit */ +#if defined(WIN32) && !defined(__GNUC__) +#define COMP_RADIX 4294967296i64 +#define COMP_MAX 0xFFFFFFFFFFFFFFFFui64 +#else +#define COMP_RADIX 4294967296ULL /**< Max component + 1 */ +#define COMP_MAX 0xFFFFFFFFFFFFFFFFULL/**< (Max dbl comp -1) */ +#endif +#define COMP_BIT_SIZE 32 /**< Number of bits in a component. */ +#define COMP_BYTE_SIZE 4 /**< Number of bytes in a component. */ +#define COMP_NUM_NIBBLES 8 /**< Used For diagnostics only. */ +typedef uint32_t comp; /**< A single precision component. */ +typedef uint64_t long_comp; /**< A double precision component. */ +typedef int64_t slong_comp; /**< A signed double precision component. */ +#endif + +/** + * @struct _bigint + * @brief A big integer basic object + */ +struct _bigint +{ + struct _bigint* next; /**< The next bigint in the cache. */ + short size; /**< The number of components in this bigint. */ + short max_comps; /**< The heapsize allocated for this bigint */ + int refs; /**< An internal reference count. */ + comp* comps; /**< A ptr to the actual component data */ +}; + +typedef struct _bigint bigint; /**< An alias for _bigint */ + +/** + * Maintains the state of the cache, and a number of variables used in + * reduction. + */ +typedef struct /**< A big integer "session" context. */ +{ + bigint *active_list; /**< Bigints currently used. */ + bigint *free_list; /**< Bigints not used. */ + bigint *bi_radix; /**< The radix used. */ + bigint *bi_mod[BIGINT_NUM_MODS]; /**< modulus */ + +#if defined(CONFIG_BIGINT_MONTGOMERY) + bigint *bi_RR_mod_m[BIGINT_NUM_MODS]; /**< R^2 mod m */ + bigint *bi_R_mod_m[BIGINT_NUM_MODS]; /**< R mod m */ + comp N0_dash[BIGINT_NUM_MODS]; +#elif defined(CONFIG_BIGINT_BARRETT) + bigint *bi_mu[BIGINT_NUM_MODS]; /**< Storage for mu */ +#endif + bigint *bi_normalised_mod[BIGINT_NUM_MODS]; /**< Normalised mod storage. */ + bigint **g; /**< Used by sliding-window. */ + int window; /**< The size of the sliding window */ + int active_count; /**< Number of active bigints. */ + int free_count; /**< Number of free bigints. */ + +#ifdef CONFIG_BIGINT_MONTGOMERY + uint8_t use_classical; /**< Use classical reduction. */ +#endif + uint8_t mod_offset; /**< The mod offset we are using */ +} BI_CTX; + +#ifndef WIN32 +#define max(a,b) ((a)>(b)?(a):(b)) /**< Find the maximum of 2 numbers. */ +#define min(a,b) ((a)<(b)?(a):(b)) /**< Find the minimum of 2 numbers. */ +#endif + +#define PERMANENT 0x7FFF55AA /**< A magic number for permanents. */ + +#endif diff --git a/ccast/axTLS/cert.h b/ccast/axTLS/cert.h new file mode 100644 index 0000000..30c7b65 --- /dev/null +++ b/ccast/axTLS/cert.h @@ -0,0 +1,43 @@ +unsigned char default_certificate[] = { + 0x30, 0x82, 0x01, 0xd7, 0x30, 0x82, 0x01, 0x40, 0x02, 0x09, 0x00, 0xab, + 0x08, 0x18, 0xa7, 0x03, 0x07, 0x27, 0xfd, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x34, + 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x29, 0x61, + 0x78, 0x54, 0x4c, 0x53, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x20, 0x44, 0x6f, 0x64, 0x67, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x31, 0x32, + 0x32, 0x36, 0x32, 0x32, 0x33, 0x33, 0x33, 0x39, 0x5a, 0x17, 0x0d, 0x32, + 0x34, 0x30, 0x39, 0x30, 0x33, 0x32, 0x32, 0x33, 0x33, 0x33, 0x39, 0x5a, + 0x30, 0x2c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0d, 0x61, 0x78, 0x54, 0x4c, 0x53, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x81, + 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, + 0x81, 0x81, 0x00, 0xcd, 0xfd, 0x89, 0x48, 0xbe, 0x36, 0xb9, 0x95, 0x76, + 0xd4, 0x13, 0x30, 0x0e, 0xbf, 0xb2, 0xed, 0x67, 0x0a, 0xc0, 0x16, 0x3f, + 0x51, 0x09, 0x9d, 0x29, 0x2f, 0xb2, 0x6d, 0x3f, 0x3e, 0x6c, 0x2f, 0x90, + 0x80, 0xa1, 0x71, 0xdf, 0xbe, 0x38, 0xc5, 0xcb, 0xa9, 0x9a, 0x40, 0x14, + 0x90, 0x0a, 0xf9, 0xb7, 0x07, 0x0b, 0xe1, 0xda, 0xe7, 0x09, 0xbf, 0x0d, + 0x57, 0x41, 0x86, 0x60, 0xa1, 0xc1, 0x27, 0x91, 0x5b, 0x0a, 0x98, 0x46, + 0x1b, 0xf6, 0xa2, 0x84, 0xf8, 0x65, 0xc7, 0xce, 0x2d, 0x96, 0x17, 0xaa, + 0x91, 0xf8, 0x61, 0x04, 0x50, 0x70, 0xeb, 0xb4, 0x43, 0xb7, 0xdc, 0x9a, + 0xcc, 0x31, 0x01, 0x14, 0xd4, 0xcd, 0xcc, 0xc2, 0x37, 0x6d, 0x69, 0x82, + 0xd6, 0xc6, 0xc4, 0xbe, 0xf2, 0x34, 0xa5, 0xc9, 0xa6, 0x19, 0x53, 0x32, + 0x7a, 0x86, 0x0e, 0x91, 0x82, 0x0f, 0xa1, 0x42, 0x54, 0xaa, 0x01, 0x02, + 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x40, + 0xb4, 0x94, 0x9a, 0xa8, 0x89, 0x72, 0x1d, 0x07, 0xe5, 0xb3, 0x6b, 0x88, + 0x21, 0xc2, 0x38, 0x36, 0x9e, 0x7a, 0x8c, 0x49, 0x48, 0x68, 0x0c, 0x06, + 0xe8, 0xdb, 0x1f, 0x4e, 0x05, 0xe6, 0x31, 0xe3, 0xfd, 0xe6, 0x0d, 0x6b, + 0xd8, 0x13, 0x17, 0xe0, 0x2d, 0x0d, 0xb8, 0x7e, 0xcb, 0x20, 0x6c, 0xa8, + 0x73, 0xa7, 0xfd, 0xe3, 0xa7, 0xfa, 0xf3, 0x02, 0x60, 0x78, 0x1f, 0x13, + 0x40, 0x45, 0xee, 0x75, 0xf5, 0x10, 0xfd, 0x8f, 0x68, 0x74, 0xd4, 0xac, + 0xae, 0x04, 0x09, 0x55, 0x2c, 0xdb, 0xd8, 0x07, 0x07, 0x65, 0x69, 0x27, + 0x6e, 0xbf, 0x5e, 0x61, 0x40, 0x56, 0x8b, 0xd7, 0x33, 0x3b, 0xff, 0x6e, + 0x53, 0x7e, 0x9d, 0x3f, 0xc0, 0x40, 0x3a, 0xab, 0xa0, 0x50, 0x4e, 0x80, + 0x47, 0x46, 0x0d, 0x1e, 0xdb, 0x4c, 0xf1, 0x1b, 0x5d, 0x3c, 0x2a, 0x54, + 0xa7, 0x4d, 0xfa, 0x7b, 0x72, 0x66, 0xc5 +}; +unsigned int default_certificate_len = 475; diff --git a/ccast/axTLS/config.h b/ccast/axTLS/config.h new file mode 100644 index 0000000..ffda4e3 --- /dev/null +++ b/ccast/axTLS/config.h @@ -0,0 +1,41 @@ + +#ifdef NT +//# define CONFIG_WIN32_USE_CRYPTO_LIB +#endif + +//CONFIG_SSL_SERVER_ONLY +//CONFIG_SSL_CERT_VERIFICATION +#define CONFIG_SSL_ENABLE_CLIENT +//CONFIG_SSL_FULL_MODE +#define CONFIG_SSL_PROT_MEDIUM +#define CONFIG_SSL_USE_DEFAULT_KEY +#define CONFIG_SSL_PRIVATE_KEY_LOCATION "" +#define CONFIG_SSL_PRIVATE_KEY_PASSWORD "" +#define CONFIG_SSL_X509_CERT_LOCATION "" +//CONFIG_SSL_GENERATE_X509_CERT +//CONFIG_SSL_X509_COMMON_NAME="" +//CONFIG_SSL_X509_ORGANIZATION_NAME="" +//CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME="" +//CONFIG_SSL_ENABLE_V23_HANDSHAKE +#define CONFIG_SSL_HAS_PEM +#define CONFIG_SSL_USE_PKCS12 +#define CONFIG_SSL_EXPIRY_TIME 24 +#define CONFIG_X509_MAX_CA_CERTS 4 +#define CONFIG_SSL_MAX_CERTS 2 +// CONFIG_SSL_CTX_MUTEXING +// CONFIG_USE_DEV_URANDOM +// CONFIG_OPENSSL_COMPATIBLE +// CONFIG_PERFORMANCE_TESTING +// CONFIG_SSL_TEST is not set + +// CONFIG_BIGINT_CLASSICAL +// CONFIG_BIGINT_MONTGOMERY +#define CONFIG_BIGINT_BARRETT +#define CONFIG_BIGINT_CRT +// CONFIG_BIGINT_KARATSUBA is not set +#define MUL_KARATSUBA_THRESH 0 +#define SQU_KARATSUBA_THRESH 0 +#define CONFIG_BIGINT_SLIDING_WINDOW +#define CONFIG_BIGINT_SQUARE +// CONFIG_BIGINT_CHECK_ON + diff --git a/ccast/axTLS/crypto.h b/ccast/axTLS/crypto.h new file mode 100644 index 0000000..8a314a3 --- /dev/null +++ b/ccast/axTLS/crypto.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * @file crypto.h + */ + +#ifndef HEADER_CRYPTO_H +#define HEADER_CRYPTO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "config.h" +#include "bigint_impl.h" +#include "bigint.h" + +#ifndef STDCALL +#define STDCALL +#endif +#ifndef EXP_FUNC +#define EXP_FUNC +#endif + + +/* enable features based on a 'super-set' capbaility. */ +#if defined(CONFIG_SSL_FULL_MODE) +#define CONFIG_SSL_ENABLE_CLIENT +#define CONFIG_SSL_CERT_VERIFICATION +#elif defined(CONFIG_SSL_ENABLE_CLIENT) +#define CONFIG_SSL_CERT_VERIFICATION +#endif + +/************************************************************************** + * AES declarations + **************************************************************************/ + +#define AES_MAXROUNDS 14 +#define AES_BLOCKSIZE 16 +#define AES_IV_SIZE 16 + +typedef struct aes_key_st +{ + uint16_t rounds; + uint16_t key_size; + uint32_t ks[(AES_MAXROUNDS+1)*8]; + uint8_t iv[AES_IV_SIZE]; +} AES_CTX; + +typedef enum +{ + AES_MODE_128, + AES_MODE_256 +} AES_MODE; + +void AES_set_key(AES_CTX *ctx, const uint8_t *key, + const uint8_t *iv, AES_MODE mode); +void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, + uint8_t *out, int length); +void AES_cbc_decrypt(AES_CTX *ks, const uint8_t *in, uint8_t *out, int length); +void AES_convert_key(AES_CTX *ctx); + +/************************************************************************** + * RC4 declarations + **************************************************************************/ + +typedef struct +{ + uint8_t x, y, m[256]; +} RC4_CTX; + +void RC4_setup(RC4_CTX *s, const uint8_t *key, int length); +void RC4_crypt(RC4_CTX *s, const uint8_t *msg, uint8_t *data, int length); + +/************************************************************************** + * SHA1 declarations + **************************************************************************/ + +#define SHA1_SIZE 20 + +/* + * This structure will hold context information for the SHA-1 + * hashing operation + */ +typedef struct +{ + uint32_t Intermediate_Hash[SHA1_SIZE/4]; /* Message Digest */ + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + uint16_t Message_Block_Index; /* Index into message block array */ + uint8_t Message_Block[64]; /* 512-bit message blocks */ +} SHA1_CTX; + +void SHA1_Init(SHA1_CTX *); +void SHA1_Update(SHA1_CTX *, const uint8_t * msg, int len); +void SHA1_Final(uint8_t *digest, SHA1_CTX *); + +/************************************************************************** + * MD2 declarations + **************************************************************************/ + +#define MD2_SIZE 16 + +typedef struct +{ + unsigned char cksum[16]; /* checksum of the data block */ + unsigned char state[48]; /* intermediate digest state */ + unsigned char buffer[16]; /* data block being processed */ + int left; /* amount of data in buffer */ +} MD2_CTX; + +EXP_FUNC void STDCALL MD2_Init(MD2_CTX *ctx); +EXP_FUNC void STDCALL MD2_Update(MD2_CTX *ctx, const uint8_t *input, int ilen); +EXP_FUNC void STDCALL MD2_Final(uint8_t *digest, MD2_CTX *ctx); + +/************************************************************************** + * MD5 declarations + **************************************************************************/ + +#define MD5_SIZE 16 + +typedef struct +{ + uint32_t state[4]; /* state (ABCD) */ + uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ + uint8_t buffer[64]; /* input buffer */ +} MD5_CTX; + +EXP_FUNC void STDCALL MD5_Init(MD5_CTX *); +EXP_FUNC void STDCALL MD5_Update(MD5_CTX *, const uint8_t *msg, int len); +EXP_FUNC void STDCALL MD5_Final(uint8_t *digest, MD5_CTX *); + +/************************************************************************** + * HMAC declarations + **************************************************************************/ +void hmac_md5(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest); +void hmac_sha1(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest); + +/************************************************************************** + * RSA declarations + **************************************************************************/ + +typedef struct +{ + bigint *m; /* modulus */ + bigint *e; /* public exponent */ + bigint *d; /* private exponent */ +#ifdef CONFIG_BIGINT_CRT + bigint *p; /* p as in m = pq */ + bigint *q; /* q as in m = pq */ + bigint *dP; /* d mod (p-1) */ + bigint *dQ; /* d mod (q-1) */ + bigint *qInv; /* q^-1 mod p */ +#endif + int num_octets; + BI_CTX *bi_ctx; +} RSA_CTX; + +void RSA_priv_key_new(RSA_CTX **rsa_ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len, + const uint8_t *priv_exp, int priv_len +#ifdef CONFIG_BIGINT_CRT + , const uint8_t *p, int p_len, + const uint8_t *q, int q_len, + const uint8_t *dP, int dP_len, + const uint8_t *dQ, int dQ_len, + const uint8_t *qInv, int qInv_len +#endif + ); +void RSA_pub_key_new(RSA_CTX **rsa_ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len); +void RSA_free(RSA_CTX *ctx); +int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data, + int is_decryption); +bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg); +#if defined(CONFIG_SSL_CERT_VERIFICATION) || defined(CONFIG_SSL_GENERATE_X509_CERT) +bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, + bigint *modulus, bigint *pub_exp); +bigint *RSA_public(const RSA_CTX * c, bigint *bi_msg); +int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, + uint8_t *out_data, int is_signing); +void RSA_print(const RSA_CTX *ctx); +#endif + +/************************************************************************** + * RNG declarations + **************************************************************************/ +EXP_FUNC void STDCALL RNG_initialize(void); +EXP_FUNC void STDCALL RNG_custom_init(const uint8_t *seed_buf, int size); +EXP_FUNC void STDCALL RNG_terminate(void); +EXP_FUNC void STDCALL get_random(int num_rand_bytes, uint8_t *rand_data); +void get_random_NZ(int num_rand_bytes, uint8_t *rand_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ccast/axTLS/crypto_misc.c b/ccast/axTLS/crypto_misc.c new file mode 100644 index 0000000..f43fd04 --- /dev/null +++ b/ccast/axTLS/crypto_misc.c @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * Some misc. routines to help things out + */ + +#include +#include +#include +#include +#include "os_port.h" +#include "crypto_misc.h" +#ifdef CONFIG_WIN32_USE_CRYPTO_LIB +#include "wincrypt.h" +#endif + +#ifndef WIN32 +static int rng_fd = -1; +#elif defined(CONFIG_WIN32_USE_CRYPTO_LIB) +static HCRYPTPROV gCryptProv; +#endif + +#if (!defined(CONFIG_USE_DEV_URANDOM) && !defined(CONFIG_WIN32_USE_CRYPTO_LIB)) +/* change to processor registers as appropriate */ +#define ENTROPY_POOL_SIZE 32 +#define ENTROPY_COUNTER1 ((((uint64_t)tv.tv_sec)<<32) | tv.tv_usec) +#define ENTROPY_COUNTER2 rand() +static uint8_t entropy_pool[ENTROPY_POOL_SIZE]; +#endif + +const char * const unsupported_str = "Error: Feature not supported"; + +#ifndef CONFIG_SSL_SKELETON_MODE +/** + * Retrieve a file and put it into memory + * @return The size of the file, or -1 on failure. + */ +int get_file(const char *filename, uint8_t **buf) +{ + int total_bytes = 0; + int bytes_read = 0; + int filesize; + FILE *stream = fopen(filename, "rb"); + + if (stream == NULL) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("file '%s' does not exist\n", filename); TTY_FLUSH(); +#endif + return -1; + } + + /* Win CE doesn't support stat() */ + fseek(stream, 0, SEEK_END); + filesize = ftell(stream); + *buf = (uint8_t *)malloc(filesize); + fseek(stream, 0, SEEK_SET); + + do + { + bytes_read = fread(*buf+total_bytes, 1, filesize-total_bytes, stream); + total_bytes += bytes_read; + } while (total_bytes < filesize && bytes_read > 0); + + fclose(stream); + return filesize; +} +#endif + +/** + * Initialise the Random Number Generator engine. + * - On Win32 use the platform SDK's crypto engine. + * - On Linux use /dev/urandom + * - If none of these work then use a custom RNG. + */ +EXP_FUNC void STDCALL RNG_initialize() +{ +#if !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM) + rng_fd = ax_open("/dev/urandom", O_RDONLY); +#elif defined(WIN32) && defined(CONFIG_WIN32_USE_CRYPTO_LIB) + if (!CryptAcquireContext(&gCryptProv, + NULL, NULL, PROV_RSA_FULL, 0)) + { + if (GetLastError() == NTE_BAD_KEYSET && + !CryptAcquireContext(&gCryptProv, + NULL, + NULL, + PROV_RSA_FULL, + CRYPT_NEWKEYSET)) + { + printf("CryptoLib: %x\n", GetLastError()); + exit(1); + } + } +#else + /* start of with a stack to copy across */ + int i = 0; + memcpy(entropy_pool, &i, ENTROPY_POOL_SIZE); + srand((unsigned int)(size_t)&i); +#endif +} + +/** + * If no /dev/urandom, then initialise the RNG with something interesting. + */ +EXP_FUNC void STDCALL RNG_custom_init(const uint8_t *seed_buf, int size) +{ +#if defined(WIN32) || defined(CONFIG_WIN32_USE_CRYPTO_LIB) + int i; + + for (i = 0; i < ENTROPY_POOL_SIZE && i < size; i++) + entropy_pool[i] ^= seed_buf[i]; +#endif +} + +/** + * Terminate the RNG engine. + */ +EXP_FUNC void STDCALL RNG_terminate(void) +{ +#ifndef WIN32 + close(rng_fd); +#elif defined(CONFIG_WIN32_USE_CRYPTO_LIB) + CryptReleaseContext(gCryptProv, 0); +#endif +} + +/** + * Set a series of bytes with a random number. Individual bytes can be 0 + */ +EXP_FUNC void STDCALL get_random(int num_rand_bytes, uint8_t *rand_data) +{ +#if !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM) + /* use the Linux default */ + read(rng_fd, rand_data, num_rand_bytes); /* read from /dev/urandom */ +#elif defined(WIN32) && defined(CONFIG_WIN32_USE_CRYPTO_LIB) + /* use Microsoft Crypto Libraries */ + CryptGenRandom(gCryptProv, num_rand_bytes, rand_data); +#else /* nothing else to use, so use a custom RNG */ + /* The method we use when we've got nothing better. Use RC4, time + and a couple of random seeds to generate a random sequence */ + RC4_CTX rng_ctx; + struct timeval tv; + MD5_CTX rng_digest_ctx; + uint8_t digest[MD5_SIZE]; + uint64_t *ep; + int i; + + /* A proper implementation would use counters etc for entropy */ + gettimeofday(&tv, NULL); + ep = (uint64_t *)entropy_pool; + ep[0] ^= ENTROPY_COUNTER1; + ep[1] ^= ENTROPY_COUNTER2; + + /* use a digested version of the entropy pool as a key */ + MD5_Init(&rng_digest_ctx); + MD5_Update(&rng_digest_ctx, entropy_pool, ENTROPY_POOL_SIZE); + MD5_Final(digest, &rng_digest_ctx); + + /* come up with the random sequence */ + RC4_setup(&rng_ctx, digest, MD5_SIZE); /* use as a key */ + memcpy(rand_data, entropy_pool, num_rand_bytes < ENTROPY_POOL_SIZE ? + num_rand_bytes : ENTROPY_POOL_SIZE); + RC4_crypt(&rng_ctx, rand_data, rand_data, num_rand_bytes); + + /* move things along */ + for (i = ENTROPY_POOL_SIZE-1; i >= MD5_SIZE ; i--) + entropy_pool[i] = entropy_pool[i-MD5_SIZE]; + + /* insert the digest at the start of the entropy pool */ + memcpy(entropy_pool, digest, MD5_SIZE); +#endif +} + +/** + * Set a series of bytes with a random number. Individual bytes are not zero. + */ +void get_random_NZ(int num_rand_bytes, uint8_t *rand_data) +{ + int i; + get_random(num_rand_bytes, rand_data); + + for (i = 0; i < num_rand_bytes; i++) + { + while (rand_data[i] == 0) /* can't be 0 */ + rand_data[i] = (uint8_t)(rand()); + } +} + +/** + * Some useful diagnostic routines + */ +#if defined(CONFIG_SSL_FULL_MODE) || defined(CONFIG_DEBUG) +int hex_finish; +int hex_index; + +static void print_hex_init(int finish) +{ + hex_finish = finish; + hex_index = 0; +} + +static void print_hex(uint8_t hex) +{ + static int column; + + if (hex_index == 0) + { + column = 0; + } + + printf("%02x ", hex); + if (++column == 8) + { + printf(": "); + } + else if (column >= 16) + { + printf("\n"); + column = 0; + } + + if (++hex_index >= hex_finish && column > 0) + { + printf("\n"); + } +} + +/** + * Spit out a blob of data for diagnostics. The data is is a nice column format + * for easy reading. + * + * @param format [in] The string (with possible embedded format characters) + * @param size [in] The number of numbers to print + * @param data [in] The start of data to use + * @param ... [in] Any additional arguments + */ +EXP_FUNC void STDCALL print_blob(const char *format, + const uint8_t *data, int size, ...) +{ + int i; + char tmp[80]; + va_list(ap); + + va_start(ap, size); + sprintf(tmp, "%s\n", format); + vprintf(tmp, ap); + print_hex_init(size); + for (i = 0; i < size; i++) + { + print_hex(data[i]); + } + + va_end(ap); + TTY_FLUSH(); +} +#elif defined(WIN32) +/* VC6.0 doesn't handle variadic macros */ +EXP_FUNC void STDCALL print_blob(const char *format, const uint8_t *data, + int size, ...) {} +#endif + +#if defined(CONFIG_SSL_HAS_PEM) || defined(CONFIG_HTTP_HAS_AUTHORIZATION) +/* base64 to binary lookup table */ +static const uint8_t map[128] = +{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, + 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, + 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255 +}; + +EXP_FUNC int STDCALL base64_decode(const char *in, int len, + uint8_t *out, int *outlen) +{ + int g, t, x, y, z; + uint8_t c; + int ret = -1; + + g = 3; + for (x = y = z = t = 0; x < len; x++) + { + if ((c = map[in[x]&0x7F]) == 0xff) + continue; + + if (c == 254) /* this is the end... */ + { + c = 0; + + if (--g < 0) + goto error; + } + else if (g != 3) /* only allow = at end */ + goto error; + + t = (t<<6) | c; + + if (++y == 4) + { + out[z++] = (uint8_t)((t>>16)&255); + + if (g > 1) + out[z++] = (uint8_t)((t>>8)&255); + + if (g > 2) + out[z++] = (uint8_t)(t&255); + + y = t = 0; + } + + /* check that we don't go past the output buffer */ + if (z > *outlen) + goto error; + } + + if (y != 0) + goto error; + + *outlen = z; + ret = 0; + +error: +#ifdef CONFIG_SSL_FULL_MODE + if (ret < 0) + printf("Error: Invalid base64\n"); TTY_FLUSH(); +#endif + TTY_FLUSH(); + return ret; + +} +#endif + diff --git a/ccast/axTLS/crypto_misc.h b/ccast/axTLS/crypto_misc.h new file mode 100644 index 0000000..acb5c73 --- /dev/null +++ b/ccast/axTLS/crypto_misc.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. +*/ + +/** + * @file crypto_misc.h + */ + +#ifndef HEADER_CRYPTO_MISC_H +#define HEADER_CRYPTO_MISC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "crypto.h" +#include "bigint.h" + +/************************************************************************** + * X509 declarations + **************************************************************************/ +#define X509_OK 0 +#define X509_NOT_OK -1 +#define X509_VFY_ERROR_NO_TRUSTED_CERT -2 +#define X509_VFY_ERROR_BAD_SIGNATURE -3 +#define X509_VFY_ERROR_NOT_YET_VALID -4 +#define X509_VFY_ERROR_EXPIRED -5 +#define X509_VFY_ERROR_SELF_SIGNED -6 +#define X509_VFY_ERROR_INVALID_CHAIN -7 +#define X509_VFY_ERROR_UNSUPPORTED_DIGEST -8 +#define X509_INVALID_PRIV_KEY -9 + +/* + * The Distinguished Name + */ +#define X509_NUM_DN_TYPES 3 +#define X509_COMMON_NAME 0 +#define X509_ORGANIZATION 1 +#define X509_ORGANIZATIONAL_UNIT 2 + +struct _x509_ctx +{ + char *ca_cert_dn[X509_NUM_DN_TYPES]; + char *cert_dn[X509_NUM_DN_TYPES]; + char **subject_alt_dnsnames; + time_t not_before; + time_t not_after; + uint8_t *signature; + uint16_t sig_len; + uint8_t sig_type; + RSA_CTX *rsa_ctx; + bigint *digest; + struct _x509_ctx *next; +}; + +typedef struct _x509_ctx X509_CTX; + +#ifdef CONFIG_SSL_CERT_VERIFICATION +typedef struct +{ + X509_CTX *cert[CONFIG_X509_MAX_CA_CERTS]; +} CA_CERT_CTX; +#endif + +int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx); +void x509_free(X509_CTX *x509_ctx); +#ifdef CONFIG_SSL_CERT_VERIFICATION +int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert); +#endif +#ifdef CONFIG_SSL_FULL_MODE +void x509_print(const X509_CTX *cert, CA_CERT_CTX *ca_cert_ctx); +#endif +const char * x509_display_error(int error); + +/************************************************************************** + * ASN1 declarations + **************************************************************************/ +#define ASN1_INTEGER 0x02 +#define ASN1_BIT_STRING 0x03 +#define ASN1_OCTET_STRING 0x04 +#define ASN1_NULL 0x05 +#define ASN1_PRINTABLE_STR2 0x0C +#define ASN1_OID 0x06 +#define ASN1_PRINTABLE_STR2 0x0C +#define ASN1_PRINTABLE_STR 0x13 +#define ASN1_TELETEX_STR 0x14 +#define ASN1_IA5_STR 0x16 +#define ASN1_UTC_TIME 0x17 +#define ASN1_UNICODE_STR 0x1e +#define ASN1_SEQUENCE 0x30 +#define ASN1_CONTEXT_DNSNAME 0x82 +#define ASN1_SET 0x31 +#define ASN1_V3_DATA 0xa3 +#define ASN1_IMPLICIT_TAG 0x80 +#define ASN1_CONTEXT_DNSNAME 0x82 +#define ASN1_EXPLICIT_TAG 0xa0 +#define ASN1_V3_DATA 0xa3 + +#define SIG_TYPE_MD2 0x02 +#define SIG_TYPE_MD5 0x04 +#define SIG_TYPE_SHA1 0x05 + +int get_asn1_length(const uint8_t *buf, int *offset); +int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx); +int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type); +int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type); +int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object); +int asn1_version(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); +int asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); +int asn1_name(const uint8_t *cert, int *offset, char *dn[]); +int asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); +#ifdef CONFIG_SSL_CERT_VERIFICATION +int asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); +int asn1_find_subjectaltname(const uint8_t* cert, int offset); +int asn1_compare_dn(char * const dn1[], char * const dn2[]); +#endif /* CONFIG_SSL_CERT_VERIFICATION */ +int asn1_signature_type(const uint8_t *cert, + int *offset, X509_CTX *x509_ctx); + +/************************************************************************** + * MISC declarations + **************************************************************************/ +#define SALT_SIZE 8 + +extern const char * const unsupported_str; + +typedef void (*crypt_func)(void *, const uint8_t *, uint8_t *, int); +typedef void (*hmac_func)(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest); + +int get_file(const char *filename, uint8_t **buf); + +#if defined(CONFIG_SSL_FULL_MODE) || defined(WIN32) || defined(CONFIG_DEBUG) +EXP_FUNC void STDCALL print_blob(const char *format, const uint8_t *data, int size, ...); +#else + #define print_blob(...) +#endif + +EXP_FUNC int STDCALL base64_decode(const char *in, int len, + uint8_t *out, int *outlen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ccast/axTLS/gen_cert.c b/ccast/axTLS/gen_cert.c new file mode 100644 index 0000000..d5cb4d5 --- /dev/null +++ b/ccast/axTLS/gen_cert.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +#include "config.h" + +#ifdef CONFIG_SSL_GENERATE_X509_CERT +#include +#include +#include "os_port.h" +#include "ssl.h" + +/** + * Generate a basic X.509 certificate + */ + +static uint8_t set_gen_length(int len, uint8_t *buf, int *offset) +{ + if (len < 0x80) /* short form */ + { + buf[(*offset)++] = len; + return 1; + } + else /* long form */ + { + int i, length_bytes = 0; + + if (len & 0x00FF0000) + length_bytes = 3; + else if (len & 0x0000FF00) + length_bytes = 2; + else if (len & 0x000000FF) + length_bytes = 1; + + buf[(*offset)++] = 0x80 + length_bytes; + + for (i = length_bytes-1; i >= 0; i--) + { + buf[*offset+i] = len & 0xFF; + len >>= 8; + } + + *offset += length_bytes; + return length_bytes+1; + } +} + +static int pre_adjust_with_size(uint8_t type, + int *seq_offset, uint8_t *buf, int *offset) +{ + buf[(*offset)++] = type; + *seq_offset = *offset; + *offset += 4; /* fill in later */ + return *offset; +} + +static void adjust_with_size(int seq_size, int seq_start, + uint8_t *buf, int *offset) +{ + uint8_t seq_byte_size; + int orig_seq_size = seq_size; + int orig_seq_start = seq_start; + + seq_size = *offset-seq_size; + seq_byte_size = set_gen_length(seq_size, buf, &seq_start); + + if (seq_byte_size != 4) + { + memmove(&buf[orig_seq_start+seq_byte_size], + &buf[orig_seq_size], seq_size); + *offset -= 4-seq_byte_size; + } +} + +static void gen_serial_number(uint8_t *buf, int *offset) +{ + static const uint8_t ser_oid[] = { ASN1_INTEGER, 1, 0x7F }; + memcpy(&buf[*offset], ser_oid , sizeof(ser_oid)); + *offset += sizeof(ser_oid); +} + +static void gen_signature_alg(uint8_t *buf, int *offset) +{ + /* OBJECT IDENTIFIER sha1withRSAEncryption (1 2 840 113549 1 1 5) */ + static const uint8_t sig_oid[] = + { + ASN1_SEQUENCE, 0x0d, ASN1_OID, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + ASN1_NULL, 0x00 + }; + + memcpy(&buf[*offset], sig_oid, sizeof(sig_oid)); + *offset += sizeof(sig_oid); +} + +static int gen_dn(const char *name, uint8_t dn_type, + uint8_t *buf, int *offset) +{ + int ret = X509_OK; + int name_size = strlen(name); + + if (name_size > 0x70) /* just too big */ + { + ret = X509_NOT_OK; + goto error; + } + + buf[(*offset)++] = ASN1_SET; + set_gen_length(9+name_size, buf, offset); + buf[(*offset)++] = ASN1_SEQUENCE; + set_gen_length(7+name_size, buf, offset); + buf[(*offset)++] = ASN1_OID; + buf[(*offset)++] = 3; + buf[(*offset)++] = 0x55; + buf[(*offset)++] = 0x04; + buf[(*offset)++] = dn_type; + buf[(*offset)++] = ASN1_PRINTABLE_STR; + buf[(*offset)++] = name_size; + strcpy(&buf[*offset], name); + *offset += name_size; + +error: + return ret; +} + +static int gen_issuer(const char * dn[], uint8_t *buf, int *offset) +{ + int ret = X509_OK; + int seq_offset; + int seq_size = pre_adjust_with_size( + ASN1_SEQUENCE, &seq_offset, buf, offset); + char fqdn[128]; + + /* we need the common name, so if not configured, work out the fully + * qualified domain name */ + if (dn[X509_COMMON_NAME] == NULL || strlen(dn[X509_COMMON_NAME]) == 0) + { + int fqdn_len; + gethostname(fqdn, sizeof(fqdn)); + fqdn_len = strlen(fqdn); + fqdn[fqdn_len++] = '.'; + getdomainname(&fqdn[fqdn_len], sizeof(fqdn)-fqdn_len); + fqdn_len = strlen(fqdn); + + if (fqdn[fqdn_len-1] == '.') /* ensure '.' is not last char */ + fqdn[fqdn_len-1] = 0; + + dn[X509_COMMON_NAME] = fqdn; + } + + if ((ret = gen_dn(dn[X509_COMMON_NAME], 3, buf, offset))) + goto error; + + if (dn[X509_ORGANIZATION] != NULL && strlen(dn[X509_ORGANIZATION]) > 0) + { + if ((ret = gen_dn(dn[X509_ORGANIZATION], 10, buf, offset))) + goto error; + } + + if (dn[X509_ORGANIZATIONAL_UNIT] != NULL && + strlen(dn[X509_ORGANIZATIONAL_UNIT]) > 0) + { + if ((ret = gen_dn(dn[X509_ORGANIZATIONAL_UNIT], 11, buf, offset))) + goto error; + } + + adjust_with_size(seq_size, seq_offset, buf, offset); + +error: + return ret; +} + +static void gen_utc_time(uint8_t *buf, int *offset) +{ + static const uint8_t time_seq[] = + { + ASN1_SEQUENCE, 30, + ASN1_UTC_TIME, 13, + '0', '7', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z', + ASN1_UTC_TIME, 13, /* make it good for 30 or so years */ + '3', '8', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z' + }; + + /* fixed time */ + memcpy(&buf[*offset], time_seq, sizeof(time_seq)); + *offset += sizeof(time_seq); +} + +static void gen_pub_key2(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset) +{ + static const uint8_t pub_key_seq[] = + { + ASN1_INTEGER, 0x03, 0x01, 0x00, 0x01 /* INTEGER 65537 */ + }; + + int seq_offset; + int pub_key_size = rsa_ctx->num_octets; + uint8_t *block = (uint8_t *)malloc(pub_key_size); + int seq_size = pre_adjust_with_size( + ASN1_SEQUENCE, &seq_offset, buf, offset); + buf[(*offset)++] = ASN1_INTEGER; + bi_export(rsa_ctx->bi_ctx, rsa_ctx->m, block, pub_key_size); + + if (*block & 0x80) /* make integer positive */ + { + set_gen_length(pub_key_size+1, buf, offset); + buf[(*offset)++] = 0; + } + else + set_gen_length(pub_key_size, buf, offset); + + memcpy(&buf[*offset], block, pub_key_size); + *offset += pub_key_size; + memcpy(&buf[*offset], pub_key_seq, sizeof(pub_key_seq)); + *offset += sizeof(pub_key_seq); + adjust_with_size(seq_size, seq_offset, buf, offset); + free(block); +} + +static void gen_pub_key1(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset) +{ + int seq_offset; + int seq_size = pre_adjust_with_size( + ASN1_BIT_STRING, &seq_offset, buf, offset); + buf[(*offset)++] = 0; /* bit string is multiple of 8 */ + gen_pub_key2(rsa_ctx, buf, offset); + adjust_with_size(seq_size, seq_offset, buf, offset); +} + +static void gen_pub_key(const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset) +{ + /* OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1) */ + static const uint8_t rsa_enc_oid[] = + { + ASN1_SEQUENCE, 0x0d, ASN1_OID, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + ASN1_NULL, 0x00 + }; + + int seq_offset; + int seq_size = pre_adjust_with_size( + ASN1_SEQUENCE, &seq_offset, buf, offset); + + memcpy(&buf[*offset], rsa_enc_oid, sizeof(rsa_enc_oid)); + *offset += sizeof(rsa_enc_oid); + gen_pub_key1(rsa_ctx, buf, offset); + adjust_with_size(seq_size, seq_offset, buf, offset); +} + +static void gen_signature(const RSA_CTX *rsa_ctx, const uint8_t *sha_dgst, + uint8_t *buf, int *offset) +{ + static const uint8_t asn1_sig[] = + { + ASN1_SEQUENCE, 0x21, ASN1_SEQUENCE, 0x09, ASN1_OID, 0x05, + 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* sha1 (1 3 14 3 2 26) */ + ASN1_NULL, 0x00, ASN1_OCTET_STRING, 0x14 + }; + + uint8_t *enc_block = (uint8_t *)malloc(rsa_ctx->num_octets); + uint8_t *block = (uint8_t *)malloc(sizeof(asn1_sig) + SHA1_SIZE); + int sig_size; + + /* add the digest as an embedded asn.1 sequence */ + memcpy(block, asn1_sig, sizeof(asn1_sig)); + memcpy(&block[sizeof(asn1_sig)], sha_dgst, SHA1_SIZE); + + sig_size = RSA_encrypt(rsa_ctx, block, + sizeof(asn1_sig) + SHA1_SIZE, enc_block, 1); + + buf[(*offset)++] = ASN1_BIT_STRING; + set_gen_length(sig_size+1, buf, offset); + buf[(*offset)++] = 0; /* bit string is multiple of 8 */ + memcpy(&buf[*offset], enc_block, sig_size); + *offset += sig_size; + free(block); + free(enc_block); +} + +static int gen_tbs_cert(const char * dn[], + const RSA_CTX *rsa_ctx, uint8_t *buf, int *offset, + uint8_t *sha_dgst) +{ + int ret = X509_OK; + SHA1_CTX sha_ctx; + int seq_offset; + int begin_tbs = *offset; + int seq_size = pre_adjust_with_size( + ASN1_SEQUENCE, &seq_offset, buf, offset); + + gen_serial_number(buf, offset); + gen_signature_alg(buf, offset); + + /* CA certicate issuer */ + if ((ret = gen_issuer(dn, buf, offset))) + goto error; + + gen_utc_time(buf, offset); + + /* certificate issuer */ + if ((ret = gen_issuer(dn, buf, offset))) + goto error; + + gen_pub_key(rsa_ctx, buf, offset); + adjust_with_size(seq_size, seq_offset, buf, offset); + + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, &buf[begin_tbs], *offset-begin_tbs); + SHA1_Final(sha_dgst, &sha_ctx); + +error: + return ret; +} + +/** + * Create a new certificate. + */ +EXP_FUNC int STDCALL ssl_x509_create(SSL_CTX *ssl_ctx, uint32_t options, const char * dn[], uint8_t **cert_data) +{ + int ret = X509_OK, offset = 0, seq_offset; + /* allocate enough space to load a new certificate */ + uint8_t *buf = (uint8_t *)malloc(ssl_ctx->rsa_ctx->num_octets*2 + 512); + uint8_t sha_dgst[SHA1_SIZE]; + int seq_size = pre_adjust_with_size(ASN1_SEQUENCE, + &seq_offset, buf, &offset); + + if ((ret = gen_tbs_cert(dn, ssl_ctx->rsa_ctx, buf, &offset, sha_dgst)) < 0) + goto error; + + gen_signature_alg(buf, &offset); + gen_signature(ssl_ctx->rsa_ctx, sha_dgst, buf, &offset); + adjust_with_size(seq_size, seq_offset, buf, &offset); + *cert_data = (uint8_t *)malloc(offset); /* create the exact memory for it */ + memcpy(*cert_data, buf, offset); + +error: + free(buf); + return ret < 0 ? ret : offset; +} + +#endif + diff --git a/ccast/axTLS/hmac.c b/ccast/axTLS/hmac.c new file mode 100644 index 0000000..24a04d7 --- /dev/null +++ b/ccast/axTLS/hmac.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * HMAC implementation - This code was originally taken from RFC2104 + * See http://www.ietf.org/rfc/rfc2104.txt and + * http://www.faqs.org/rfcs/rfc2202.html + */ + +#include +#include "os_port.h" +#include "crypto.h" + +/** + * Perform HMAC-MD5 + * NOTE: does not handle keys larger than the block size. + */ +void hmac_md5(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest) +{ + MD5_CTX context; + uint8_t k_ipad[64]; + uint8_t k_opad[64]; + int i; + + memset(k_ipad, 0, sizeof k_ipad); + memset(k_opad, 0, sizeof k_opad); + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + + for (i = 0; i < 64; i++) + { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + MD5_Init(&context); + MD5_Update(&context, k_ipad, 64); + MD5_Update(&context, msg, length); + MD5_Final(digest, &context); + MD5_Init(&context); + MD5_Update(&context, k_opad, 64); + MD5_Update(&context, digest, MD5_SIZE); + MD5_Final(digest, &context); +} + +/** + * Perform HMAC-SHA1 + * NOTE: does not handle keys larger than the block size. + */ +void hmac_sha1(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest) +{ + SHA1_CTX context; + uint8_t k_ipad[64]; + uint8_t k_opad[64]; + int i; + + memset(k_ipad, 0, sizeof k_ipad); + memset(k_opad, 0, sizeof k_opad); + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + + for (i = 0; i < 64; i++) + { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + SHA1_Init(&context); + SHA1_Update(&context, k_ipad, 64); + SHA1_Update(&context, msg, length); + SHA1_Final(digest, &context); + SHA1_Init(&context); + SHA1_Update(&context, k_opad, 64); + SHA1_Update(&context, digest, SHA1_SIZE); + SHA1_Final(digest, &context); +} diff --git a/ccast/axTLS/loader.c b/ccast/axTLS/loader.c new file mode 100644 index 0000000..92167be --- /dev/null +++ b/ccast/axTLS/loader.c @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * Load certificates/keys into memory. These can be in many different formats. + * PEM support and other formats can be processed here. + * + * The PEM private keys may be optionally encrypted with AES128 or AES256. + * The encrypted PEM keys were generated with something like: + * + * openssl genrsa -aes128 -passout pass:abcd -out axTLS.key_aes128.pem 512 + */ + +#include +#include +#include +#include "os_port.h" +#include "ssl.h" + +static int do_obj(SSL_CTX *ssl_ctx, int obj_type, + SSLObjLoader *ssl_obj, const char *password); +#ifdef CONFIG_SSL_HAS_PEM +static int ssl_obj_PEM_load(SSL_CTX *ssl_ctx, int obj_type, + SSLObjLoader *ssl_obj, const char *password); +#endif + +/* + * Load a file into memory that is in binary DER (or ascii PEM) format. + */ +EXP_FUNC int STDCALL ssl_obj_load(SSL_CTX *ssl_ctx, int obj_type, + const char *filename, const char *password) +{ +#ifndef CONFIG_SSL_SKELETON_MODE + static const char * const begin = "-----BEGIN"; + int ret = SSL_OK; + SSLObjLoader *ssl_obj = NULL; + + if (filename == NULL) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + + ssl_obj = (SSLObjLoader *)calloc(1, sizeof(SSLObjLoader)); + ssl_obj->len = get_file(filename, &ssl_obj->buf); + if (ssl_obj->len <= 0) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + + /* is the file a PEM file? */ + if (strstr((char *)ssl_obj->buf, begin) != NULL) + { +#ifdef CONFIG_SSL_HAS_PEM + ret = ssl_obj_PEM_load(ssl_ctx, obj_type, ssl_obj, password); +#else + puts(unsupported_str); + ret = SSL_ERROR_NOT_SUPPORTED; +#endif + } + else + ret = do_obj(ssl_ctx, obj_type, ssl_obj, password); + +error: + ssl_obj_free(ssl_obj); + return ret; +#else + puts(unsupported_str); + return SSL_ERROR_NOT_SUPPORTED; +#endif /* CONFIG_SSL_SKELETON_MODE */ +} + +/* + * Transfer binary data into the object loader. + */ +EXP_FUNC int STDCALL ssl_obj_memory_load(SSL_CTX *ssl_ctx, int mem_type, + const uint8_t *data, int len, const char *password) +{ + int ret; + SSLObjLoader *ssl_obj; + + ssl_obj = (SSLObjLoader *)calloc(1, sizeof(SSLObjLoader)); + ssl_obj->buf = (uint8_t *)malloc(len); + memcpy(ssl_obj->buf, data, len); + ssl_obj->len = len; + ret = do_obj(ssl_ctx, mem_type, ssl_obj, password); + ssl_obj_free(ssl_obj); + return ret; +} + +/* + * Actually work out what we are doing + */ +static int do_obj(SSL_CTX *ssl_ctx, int obj_type, + SSLObjLoader *ssl_obj, const char *password) +{ + int ret = SSL_OK; + + switch (obj_type) + { + case SSL_OBJ_RSA_KEY: + ret = add_private_key(ssl_ctx, ssl_obj); + break; + + case SSL_OBJ_X509_CERT: + ret = add_cert(ssl_ctx, ssl_obj->buf, ssl_obj->len); + break; + +#ifdef CONFIG_SSL_CERT_VERIFICATION + case SSL_OBJ_X509_CACERT: + add_cert_auth(ssl_ctx, ssl_obj->buf, ssl_obj->len); + break; +#endif + +#ifdef CONFIG_SSL_USE_PKCS12 + case SSL_OBJ_PKCS8: + ret = pkcs8_decode(ssl_ctx, ssl_obj, password); + break; + + case SSL_OBJ_PKCS12: + ret = pkcs12_decode(ssl_ctx, ssl_obj, password); + break; +#endif + default: + puts(unsupported_str); + ret = SSL_ERROR_NOT_SUPPORTED; + break; + } + + return ret; +} + +/* + * Clean up our mess. + */ +void ssl_obj_free(SSLObjLoader *ssl_obj) +{ + if (ssl_obj) + { + free(ssl_obj->buf); + free(ssl_obj); + } +} + +/* + * Support for PEM encoded keys/certificates. + */ +#ifdef CONFIG_SSL_HAS_PEM + +#define NUM_PEM_TYPES 4 +#define IV_SIZE 16 +#define IS_RSA_PRIVATE_KEY 0 +#define IS_ENCRYPTED_PRIVATE_KEY 1 +#define IS_PRIVATE_KEY 2 +#define IS_CERTIFICATE 3 + +static const char * const begins[NUM_PEM_TYPES] = +{ + "-----BEGIN RSA PRIVATE KEY-----", + "-----BEGIN ENCRYPTED PRIVATE KEY-----", + "-----BEGIN PRIVATE KEY-----", + "-----BEGIN CERTIFICATE-----", +}; + +static const char * const ends[NUM_PEM_TYPES] = +{ + "-----END RSA PRIVATE KEY-----", + "-----END ENCRYPTED PRIVATE KEY-----", + "-----END PRIVATE KEY-----", + "-----END CERTIFICATE-----", +}; + +static const char * const aes_str[2] = +{ + "DEK-Info: AES-128-CBC,", + "DEK-Info: AES-256-CBC," +}; + +/** + * Take a base64 blob of data and decrypt it (using AES) into its + * proper ASN.1 form. + */ +static int pem_decrypt(const char *where, const char *end, + const char *password, SSLObjLoader *ssl_obj) +{ + int ret = -1; + int is_aes_256 = 0; + char *start = NULL; + uint8_t iv[IV_SIZE]; + int i, pem_size; + MD5_CTX md5_ctx; + AES_CTX aes_ctx; + uint8_t key[32]; /* AES256 size */ + + if (password == NULL || strlen(password) == 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: Need a password for this PEM file\n"); TTY_FLUSH(); +#endif + goto error; + } + + if ((start = strstr((const char *)where, aes_str[0]))) /* AES128? */ + { + start += strlen(aes_str[0]); + } + else if ((start = strstr((const char *)where, aes_str[1]))) /* AES256? */ + { + is_aes_256 = 1; + start += strlen(aes_str[1]); + } + else + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: Unsupported password cipher\n"); TTY_FLUSH(); +#endif + goto error; + } + + /* convert from hex to binary - assumes uppercase hex */ + for (i = 0; i < IV_SIZE; i++) + { + char c = *start++ - '0'; + iv[i] = (c > 9 ? c + '0' - 'A' + 10 : c) << 4; + c = *start++ - '0'; + iv[i] += (c > 9 ? c + '0' - 'A' + 10 : c); + } + + while (*start == '\r' || *start == '\n') + start++; + + /* turn base64 into binary */ + pem_size = (int)(end-start); + if (base64_decode(start, pem_size, ssl_obj->buf, &ssl_obj->len) != 0) + goto error; + + /* work out the key */ + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, (const uint8_t *)password, strlen(password)); + MD5_Update(&md5_ctx, iv, SALT_SIZE); + MD5_Final(key, &md5_ctx); + + if (is_aes_256) + { + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, key, MD5_SIZE); + MD5_Update(&md5_ctx, (const uint8_t *)password, strlen(password)); + MD5_Update(&md5_ctx, iv, SALT_SIZE); + MD5_Final(&key[MD5_SIZE], &md5_ctx); + } + + /* decrypt using the key/iv */ + AES_set_key(&aes_ctx, key, iv, is_aes_256 ? AES_MODE_256 : AES_MODE_128); + AES_convert_key(&aes_ctx); + AES_cbc_decrypt(&aes_ctx, ssl_obj->buf, ssl_obj->buf, ssl_obj->len); + ret = 0; + +error: + return ret; +} + +/** + * Take a base64 blob of data and turn it into its proper ASN.1 form. + */ +static int new_pem_obj(SSL_CTX *ssl_ctx, int is_cacert, char *where, + int remain, const char *password) +{ + int ret = SSL_ERROR_BAD_CERTIFICATE; + SSLObjLoader *ssl_obj = NULL; + + while (remain > 0) + { + int i, pem_size, obj_type; + char *start = NULL, *end = NULL; + + for (i = 0; i < NUM_PEM_TYPES; i++) + { + if ((start = strstr(where, begins[i])) && + (end = strstr(where, ends[i]))) + { + remain -= (int)(end-where); + start += strlen(begins[i]); + pem_size = (int)(end-start); + + ssl_obj = (SSLObjLoader *)calloc(1, sizeof(SSLObjLoader)); + + /* 4/3 bigger than what we need but so what */ + ssl_obj->buf = (uint8_t *)calloc(1, pem_size); + ssl_obj->len = pem_size; + + if (i == IS_RSA_PRIVATE_KEY && + strstr(start, "Proc-Type:") && + strstr(start, "4,ENCRYPTED")) + { + /* check for encrypted PEM file */ + if (pem_decrypt(start, end, password, ssl_obj) < 0) + { + ret = SSL_ERROR_BAD_CERTIFICATE; + goto error; + } + } + else + { + ssl_obj->len = pem_size; + if (base64_decode(start, pem_size, + ssl_obj->buf, &ssl_obj->len) != 0) + { + ret = SSL_ERROR_BAD_CERTIFICATE; + goto error; + } + } + + switch (i) + { + case IS_RSA_PRIVATE_KEY: + obj_type = SSL_OBJ_RSA_KEY; + break; + + case IS_ENCRYPTED_PRIVATE_KEY: + case IS_PRIVATE_KEY: + obj_type = SSL_OBJ_PKCS8; + break; + + case IS_CERTIFICATE: + obj_type = is_cacert ? + SSL_OBJ_X509_CACERT : SSL_OBJ_X509_CERT; + break; + + default: + ret = SSL_ERROR_BAD_CERTIFICATE; + goto error; + } + + /* In a format we can now understand - so process it */ + if ((ret = do_obj(ssl_ctx, obj_type, ssl_obj, password))) + goto error; + + end += strlen(ends[i]); + remain -= strlen(ends[i]); + while (remain > 0 && (*end == '\r' || *end == '\n')) + { + end++; + remain--; + } + + where = end; + break; + } + } + + ssl_obj_free(ssl_obj); + ssl_obj = NULL; + if (start == NULL) + break; + } +error: + ssl_obj_free(ssl_obj); + return ret; +} + +/* + * Load a file into memory that is in ASCII PEM format. + */ +static int ssl_obj_PEM_load(SSL_CTX *ssl_ctx, int obj_type, + SSLObjLoader *ssl_obj, const char *password) +{ + char *start; + + /* add a null terminator */ + ssl_obj->len++; + ssl_obj->buf = (uint8_t *)realloc(ssl_obj->buf, ssl_obj->len); + ssl_obj->buf[ssl_obj->len-1] = 0; + start = (char *)ssl_obj->buf; + return new_pem_obj(ssl_ctx, obj_type == SSL_OBJ_X509_CACERT, + start, ssl_obj->len, password); +} +#endif /* CONFIG_SSL_HAS_PEM */ + +/** + * Load the key/certificates in memory depending on compile-time and user + * options. + */ +int load_key_certs(SSL_CTX *ssl_ctx) +{ + int ret = SSL_OK; + uint32_t options = ssl_ctx->options; +#ifdef CONFIG_SSL_GENERATE_X509_CERT + uint8_t *cert_data = NULL; + int cert_size; + static const char *dn[] = + { + CONFIG_SSL_X509_COMMON_NAME, + CONFIG_SSL_X509_ORGANIZATION_NAME, + CONFIG_SSL_X509_ORGANIZATION_UNIT_NAME + }; +#endif + + /* do the private key first */ + if (strlen(CONFIG_SSL_PRIVATE_KEY_LOCATION) > 0) + { + if ((ret = ssl_obj_load(ssl_ctx, SSL_OBJ_RSA_KEY, + CONFIG_SSL_PRIVATE_KEY_LOCATION, + CONFIG_SSL_PRIVATE_KEY_PASSWORD)) < 0) + goto error; + } + else if (!(options & SSL_NO_DEFAULT_KEY)) + { +#if defined(CONFIG_SSL_USE_DEFAULT_KEY) || defined(CONFIG_SSL_SKELETON_MODE) + static const /* saves a few more bytes */ +#include "private_key.h" + + ssl_obj_memory_load(ssl_ctx, SSL_OBJ_RSA_KEY, default_private_key, + default_private_key_len, NULL); +#endif + } + + /* now load the certificate */ +#ifdef CONFIG_SSL_GENERATE_X509_CERT + if ((cert_size = ssl_x509_create(ssl_ctx, 0, dn, &cert_data)) < 0) + { + ret = cert_size; + goto error; + } + + ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CERT, cert_data, cert_size, NULL); + free(cert_data); +#else + if (strlen(CONFIG_SSL_X509_CERT_LOCATION)) + { + if ((ret = ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, + CONFIG_SSL_X509_CERT_LOCATION, NULL)) < 0) + goto error; + } + else if (!(options & SSL_NO_DEFAULT_KEY)) + { +#if defined(CONFIG_SSL_USE_DEFAULT_KEY) || defined(CONFIG_SSL_SKELETON_MODE) + static const /* saves a few bytes and RAM */ +#include "cert.h" + ssl_obj_memory_load(ssl_ctx, SSL_OBJ_X509_CERT, + default_certificate, default_certificate_len, NULL); +#endif + } +#endif + +error: +#ifdef CONFIG_SSL_FULL_MODE + if (ret) + { + printf("Error: Certificate or key not loaded\n"); TTY_FLUSH(); + } +#endif + + return ret; + +} diff --git a/ccast/axTLS/md2.c b/ccast/axTLS/md2.c new file mode 100644 index 0000000..dee909a --- /dev/null +++ b/ccast/axTLS/md2.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/* + * RFC 1115/1319 compliant MD2 implementation + * The MD2 algorithm was designed by Ron Rivest in 1989. + * + * http://www.ietf.org/rfc/rfc1115.txt + * http://www.ietf.org/rfc/rfc1319.txt + */ + +#include +#include +#include "os_port.h" +#include "crypto.h" + +/** + * This code is only here to enable the verification of Verisign root + * certificates. So only enable it for verification mode. + */ +#ifdef CONFIG_SSL_CERT_VERIFICATION + +static const uint8_t PI_SUBST[256] = +{ + 0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01, 0x3D, 0x36, + 0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13, 0x62, 0xA7, 0x05, 0xF3, + 0xC0, 0xC7, 0x73, 0x8C, 0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C, + 0x82, 0xCA, 0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16, + 0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12, 0xBE, 0x4E, + 0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49, 0xA0, 0xFB, 0xF5, 0x8E, + 0xBB, 0x2F, 0xEE, 0x7A, 0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2, + 0x07, 0x3F, 0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21, + 0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27, 0x35, 0x3E, + 0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03, 0xFF, 0x19, 0x30, 0xB3, + 0x48, 0xA5, 0xB5, 0xD1, 0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56, + 0xAA, 0xC6, 0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6, + 0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1, 0x45, 0x9D, + 0x70, 0x59, 0x64, 0x71, 0x87, 0x20, 0x86, 0x5B, 0xCF, 0x65, + 0xE6, 0x2D, 0xA8, 0x02, 0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0, + 0xB9, 0xF6, 0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F, + 0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A, 0xC3, 0x5C, + 0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26, 0x2C, 0x53, 0x0D, 0x6E, + 0x85, 0x28, 0x84, 0x09, 0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81, + 0x4D, 0x52, 0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA, + 0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A, 0x78, 0x88, + 0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D, 0xE9, 0xCB, 0xD5, 0xFE, + 0x3B, 0x00, 0x1D, 0x39, 0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58, + 0xD0, 0xE4, 0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A, + 0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A, 0xDB, 0x99, + 0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14 +}; + +/* + * MD2 context setup + */ +EXP_FUNC void STDCALL MD2_Init(MD2_CTX *ctx) +{ + memset(ctx, 0, sizeof *ctx); +} + +static void md2_process(MD2_CTX *ctx) +{ + int i, j; + uint8_t t = 0; + + for (i = 0; i < 16; i++) + { + ctx->state[i + 16] = ctx->buffer[i]; + ctx->state[i + 32] = ctx->buffer[i] ^ ctx->state[i]; + } + + for (i = 0; i < 18; i++) + { + for (j = 0; j < 48; j++) + t = (ctx->state[j] ^= PI_SUBST[t]); + + t = (t + i) & 0xFF; + } + + t = ctx->cksum[15]; + + for (i = 0; i < 16; i++) + t = (ctx->cksum[i] ^= PI_SUBST[ctx->buffer[i] ^ t]); +} + +/* + * MD2 process buffer + */ +EXP_FUNC void STDCALL MD2_Update(MD2_CTX *ctx, const uint8_t *input, int ilen) +{ + int fill; + + while (ilen > 0) + { + if (ctx->left + ilen > 16) + fill = 16 - ctx->left; + else + fill = ilen; + + memcpy(ctx->buffer + ctx->left, input, fill); + + ctx->left += fill; + input += fill; + ilen -= fill; + + if (ctx->left == 16) + { + ctx->left = 0; + md2_process(ctx); + } + } +} + +/* + * MD2 final digest + */ +EXP_FUNC void STDCALL MD2_Final(uint8_t *output, MD2_CTX *ctx) +{ + int i; + uint8_t x; + + x = (uint8_t)(16 - ctx->left); + + for (i = ctx->left; i < 16; i++) + ctx->buffer[i] = x; + + md2_process(ctx); + + memcpy(ctx->buffer, ctx->cksum, 16); + md2_process(ctx); + + memcpy(output, ctx->state, 16); +} + +#endif diff --git a/ccast/axTLS/md5.c b/ccast/axTLS/md5.c new file mode 100644 index 0000000..7f50713 --- /dev/null +++ b/ccast/axTLS/md5.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * This file implements the MD5 algorithm as defined in RFC1321 + */ + +#include +#include "os_port.h" +#include "crypto.h" + +/* Constants for MD5Transform routine. + */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +/* ----- static functions ----- */ +static void MD5Transform(uint32_t state[4], const uint8_t block[64]); +static void Encode(uint8_t *output, uint32_t *input, uint32_t len); +static void Decode(uint32_t *output, const uint8_t *input, uint32_t len); + +static const uint8_t PADDING[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + Rotation is separate from addition to prevent recomputation. */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/** + * MD5 initialization - begins an MD5 operation, writing a new ctx. + */ +EXP_FUNC void STDCALL MD5_Init(MD5_CTX *ctx) +{ + ctx->count[0] = ctx->count[1] = 0; + + /* Load magic initialization constants. + */ + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xefcdab89; + ctx->state[2] = 0x98badcfe; + ctx->state[3] = 0x10325476; +} + +/** + * Accepts an array of octets as the next portion of the message. + */ +EXP_FUNC void STDCALL MD5_Update(MD5_CTX *ctx, const uint8_t * msg, int len) +{ + uint32_t x; + int i, partLen; + + /* Compute number of bytes mod 64 */ + x = (uint32_t)((ctx->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((ctx->count[0] += ((uint32_t)len << 3)) < ((uint32_t)len << 3)) + ctx->count[1]++; + ctx->count[1] += ((uint32_t)len >> 29); + + partLen = 64 - x; + + /* Transform as many times as possible. */ + if (len >= partLen) + { + memcpy(&ctx->buffer[x], msg, partLen); + MD5Transform(ctx->state, ctx->buffer); + + for (i = partLen; i + 63 < len; i += 64) + MD5Transform(ctx->state, &msg[i]); + + x = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy(&ctx->buffer[x], &msg[i], len-i); +} + +/** + * Return the 128-bit message digest into the user's array + */ +EXP_FUNC void STDCALL MD5_Final(uint8_t *digest, MD5_CTX *ctx) +{ + uint8_t bits[8]; + uint32_t x, padLen; + + /* Save number of bits */ + Encode(bits, ctx->count, 8); + + /* Pad out to 56 mod 64. + */ + x = (uint32_t)((ctx->count[0] >> 3) & 0x3f); + padLen = (x < 56) ? (56 - x) : (120 - x); + MD5_Update(ctx, PADDING, padLen); + + /* Append length (before padding) */ + MD5_Update(ctx, bits, 8); + + /* Store state in digest */ + Encode(digest, ctx->state, MD5_SIZE); +} + +/** + * MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform(uint32_t state[4], const uint8_t block[64]) +{ + uint32_t a = state[0], b = state[1], c = state[2], + d = state[3], x[MD5_SIZE]; + + Decode(x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +/** + * Encodes input (uint32_t) into output (uint8_t). Assumes len is + * a multiple of 4. + */ +static void Encode(uint8_t *output, uint32_t *input, uint32_t len) +{ + uint32_t i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + output[j] = (uint8_t)(input[i] & 0xff); + output[j+1] = (uint8_t)((input[i] >> 8) & 0xff); + output[j+2] = (uint8_t)((input[i] >> 16) & 0xff); + output[j+3] = (uint8_t)((input[i] >> 24) & 0xff); + } +} + +/** + * Decodes input (uint8_t) into output (uint32_t). Assumes len is + * a multiple of 4. + */ +static void Decode(uint32_t *output, const uint8_t *input, uint32_t len) +{ + uint32_t i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j+1]) << 8) | + (((uint32_t)input[j+2]) << 16) | (((uint32_t)input[j+3]) << 24); +} diff --git a/ccast/axTLS/openssl.c b/ccast/axTLS/openssl.c new file mode 100644 index 0000000..e700436 --- /dev/null +++ b/ccast/axTLS/openssl.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/* + * Enable a subset of openssl compatible functions. We don't aim to be 100% + * compatible - just to be able to do basic ports etc. + * + * Only really tested on mini_httpd, so I'm not too sure how extensive this + * port is. + */ + +#include "config.h" + +#ifdef CONFIG_OPENSSL_COMPATIBLE +#include +#include +#include +#include "os_port.h" +#include "ssl.h" + +#define OPENSSL_CTX_ATTR ((OPENSSL_CTX *)ssl_ctx->bonus_attr) + +static char *key_password = NULL; + +void *SSLv23_server_method(void) { return NULL; } +void *SSLv3_server_method(void) { return NULL; } +void *TLSv1_server_method(void) { return NULL; } +void *SSLv23_client_method(void) { return NULL; } +void *SSLv3_client_method(void) { return NULL; } +void *TLSv1_client_method(void) { return NULL; } + +typedef void * (*ssl_func_type_t)(void); +typedef void * (*bio_func_type_t)(void); + +typedef struct +{ + ssl_func_type_t ssl_func_type; +} OPENSSL_CTX; + +SSL_CTX * SSL_CTX_new(ssl_func_type_t meth) +{ + SSL_CTX *ssl_ctx = ssl_ctx_new(0, 5); + ssl_ctx->bonus_attr = malloc(sizeof(OPENSSL_CTX)); + OPENSSL_CTX_ATTR->ssl_func_type = meth; + return ssl_ctx; +} + +void SSL_CTX_free(SSL_CTX * ssl_ctx) +{ + free(ssl_ctx->bonus_attr); + ssl_ctx_free(ssl_ctx); +} + +SSL * SSL_new(SSL_CTX *ssl_ctx) +{ + SSL *ssl; + ssl_func_type_t ssl_func_type; + + ssl = ssl_new(ssl_ctx, -1); /* fd is set later */ + ssl_func_type = OPENSSL_CTX_ATTR->ssl_func_type; + +#ifdef CONFIG_SSL_ENABLE_CLIENT + if (ssl_func_type == SSLv23_client_method || + ssl_func_type == SSLv3_client_method || + ssl_func_type == TLSv1_client_method) + { + SET_SSL_FLAG(SSL_IS_CLIENT); + } + else +#endif + { + ssl->next_state = HS_CLIENT_HELLO; + } + + return ssl; +} + +int SSL_set_fd(SSL *s, int fd) +{ + s->client_fd = fd; + return 1; /* always succeeds */ +} + +int SSL_accept(SSL *ssl) +{ + while (ssl_readi(ssl, NULL) == SSL_OK) + { + if (ssl->next_state == HS_CLIENT_HELLO) + return 1; /* we're done */ + } + + return -1; +} + +#ifdef CONFIG_SSL_ENABLE_CLIENT +int SSL_connect(SSL *ssl) +{ + return do_client_connect(ssl) == SSL_OK ? 1 : -1; +} +#endif + +void SSL_free(SSL *ssl) +{ + ssl_free(ssl); +} + +int SSL_read(SSL *ssl, void *buf, int num) +{ + uint8_t *read_buf; + int ret; + + while ((ret = ssl_read(ssl, &read_buf)) == SSL_OK); + + if (ret > SSL_OK) + { + memcpy(buf, read_buf, ret > num ? num : ret); + } + + return ret; +} + +int SSL_write(SSL *ssl, const void *buf, int num) +{ + return ssl_write(ssl, buf, num); +} + +int SSL_CTX_use_certificate_file(SSL_CTX *ssl_ctx, const char *file, int type) +{ + return (ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, file, NULL) == SSL_OK); +} + +int SSL_CTX_use_PrivateKey_file(SSL_CTX *ssl_ctx, const char *file, int type) +{ + return (ssl_obj_load(ssl_ctx, SSL_OBJ_RSA_KEY, file, key_password) == SSL_OK); +} + +int SSL_CTX_use_certificate_ASN1(SSL_CTX *ssl_ctx, int len, const uint8_t *d) +{ + return (ssl_obj_memory_load(ssl_ctx, + SSL_OBJ_X509_CERT, d, len, NULL) == SSL_OK); +} + +int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const unsigned char *sid_ctx, + unsigned int sid_ctx_len) +{ + return 1; +} + +int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) +{ + return 1; +} + +int SSL_CTX_use_certificate_chain_file(SSL_CTX *ssl_ctx, const char *file) +{ + return (ssl_obj_load(ssl_ctx, + SSL_OBJ_X509_CERT, file, NULL) == SSL_OK); +} + +int SSL_shutdown(SSL *ssl) +{ + return 1; +} + +/*** get/set session ***/ +SSL_SESSION *SSL_get1_session(SSL *ssl) +{ + return (SSL_SESSION *)ssl_get_session_id(ssl); /* note: wrong cast */ +} + +int SSL_set_session(SSL *ssl, SSL_SESSION *session) +{ + memcpy(ssl->session_id, (uint8_t *)session, SSL_SESSION_ID_SIZE); + return 1; +} + +void SSL_SESSION_free(SSL_SESSION *session) { } +/*** end get/set session ***/ + +long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) +{ + return 0; +} + +void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, + int (*verify_callback)(int, void *)) { } + +void SSL_CTX_set_verify_depth(SSL_CTX *ctx,int depth) { } + +int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, + const char *CApath) +{ + return 1; +} + +void *SSL_load_client_CA_file(const char *file) +{ + return (void *)file; +} + +void SSL_CTX_set_client_CA_list(SSL_CTX *ssl_ctx, void *file) +{ + + ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CERT, (const char *)file, NULL); +} + +void SSLv23_method(void) { } + +void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, void *cb) { } + +void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *u) +{ + key_password = (char *)u; +} + +int SSL_peek(SSL *ssl, void *buf, int num) +{ + memcpy(buf, ssl->bm_data, num); + return num; +} + +void SSL_set_bio(SSL *ssl, void *rbio, void *wbio) { } + +long SSL_get_verify_result(const SSL *ssl) +{ + return ssl_handshake_status(ssl); +} + +int SSL_state(SSL *ssl) +{ + return 0x03; // ok state +} + +/** end of could do better list */ + +void *SSL_get_peer_certificate(const SSL *ssl) +{ + return &ssl->ssl_ctx->certs[0]; +} + +int SSL_clear(SSL *ssl) +{ + return 1; +} + + +int SSL_CTX_check_private_key(const SSL_CTX *ctx) +{ + return 1; +} + +int SSL_CTX_set_cipher_list(SSL *s, const char *str) +{ + return 1; +} + +int SSL_get_error(const SSL *ssl, int ret) +{ + ssl_display_error(ret); + return 0; /* TODO: return proper return code */ +} + +void SSL_CTX_set_options(SSL_CTX *ssl_ctx, int option) {} +int SSL_library_init(void ) { return 1; } +void SSL_load_error_strings(void ) {} +void ERR_print_errors_fp(FILE *fp) {} + +#ifndef CONFIG_SSL_SKELETON_MODE +long SSL_CTX_get_timeout(const SSL_CTX *ssl_ctx) { + return CONFIG_SSL_EXPIRY_TIME*3600; } +long SSL_CTX_set_timeout(SSL_CTX *ssl_ctx, long t) { + return SSL_CTX_get_timeout(ssl_ctx); } +#endif +void BIO_printf(FILE *f, const char *format, ...) +{ + va_list(ap); + va_start(ap, format); + vfprintf(f, format, ap); + va_end(ap); +} + +void* BIO_s_null(void) { return NULL; } +FILE *BIO_new(bio_func_type_t func) +{ + if (func == BIO_s_null) + return fopen("/dev/null", "r"); + else + return NULL; +} + +FILE *BIO_new_fp(FILE *stream, int close_flag) { return stream; } +int BIO_free(FILE *a) { if (a != stdout && a != stderr) fclose(a); return 1; } + + + +#endif diff --git a/ccast/axTLS/os_int.h b/ccast/axTLS/os_int.h new file mode 100644 index 0000000..4918111 --- /dev/null +++ b/ccast/axTLS/os_int.h @@ -0,0 +1,74 @@ + + /* Ensure a consistent bit size */ + +/************************************************************************* + Copyright 2014 Graeme W. Gill + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + *************************************************************************/ + +#ifndef HEADER_OS_INT_H +#define HEADER_OS_INT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if (__STDC_VERSION__ >= 199901L) /* C99 */ \ + || defined(_STDINT_H_) || defined(_STDINT_H) \ + || defined(_SYS_TYPES_H) + +#include + +#else /* !__STDC_VERSION__ */ +#ifdef _MSC_VER + +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +#else /* !_MSC_VER */ + +/* The following works on a lot of modern systems, including */ +/* LLP64 and LP64 models, but won't work with ILP64 which needs int32 */ + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef long long int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +#endif /* !_MSC_VER */ +#endif /* !__STDC_VERSION__ */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ccast/axTLS/os_port.c b/ccast/axTLS/os_port.c new file mode 100644 index 0000000..0bb74f8 --- /dev/null +++ b/ccast/axTLS/os_port.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * @file os_port.c + * + * OS specific functions. + */ +#include +#include +#include +#include +#include "os_port.h" + +#ifdef WIN32 +/** + * gettimeofday() not in Win32 + */ +EXP_FUNC void STDCALL gettimeofday(struct timeval* t, void* timezone) +{ +#if defined(_WIN32_WCE) + t->tv_sec = time(NULL); + t->tv_usec = 0; /* 1sec precision only */ +#else + struct _timeb timebuffer; + _ftime(&timebuffer); + t->tv_sec = (long)timebuffer.time; + t->tv_usec = 1000 * timebuffer.millitm; /* 1ms precision */ +#endif +} + +#ifndef __GNUC__ /* But is in MingW */ +/** + * strcasecmp() not in Win32 + */ +EXP_FUNC int STDCALL strcasecmp(const char *s1, const char *s2) +{ + while (tolower(*s1) == tolower(*s2++)) + { + if (*s1++ == '\0') + { + return 0; + } + } + + return *(unsigned char *)s1 - *(unsigned char *)(s2 - 1); +} +#endif + + +EXP_FUNC int STDCALL getdomainname(char *buf, int buf_size) +{ + HKEY hKey; + unsigned long datatype; + unsigned long bufferlength = buf_size; + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, + TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"), + 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) + return -1; + + RegQueryValueEx(hKey, "Domain", NULL, &datatype, buf, &bufferlength); + RegCloseKey(hKey); + return 0; +} +#endif + +#undef malloc +#undef realloc +#undef calloc + +static const char * out_of_mem_str = "out of memory"; +static const char * file_open_str = "Could not open file \"%s\""; + +/* + * Some functions that call display some error trace and then call abort(). + * This just makes life much easier on embedded systems, since we're + * suffering major trauma... + */ +EXP_FUNC void * STDCALL ax_malloc(size_t s) +{ + void *x; + + if ((x = malloc(s)) == NULL) + exit_now(out_of_mem_str); + + return x; +} + +EXP_FUNC void * STDCALL ax_realloc(void *y, size_t s) +{ + void *x; + + if ((x = realloc(y, s)) == NULL) + exit_now(out_of_mem_str); + + return x; +} + +EXP_FUNC void * STDCALL ax_calloc(size_t n, size_t s) +{ + void *x; + + if ((x = calloc(n, s)) == NULL) + exit_now(out_of_mem_str); + + return x; +} + +EXP_FUNC int STDCALL ax_open(const char *pathname, int flags) +{ + int x; + + if ((x = open(pathname, flags)) < 0) + exit_now(file_open_str, pathname); + + return x; +} + +/** + * This is a call which will deliberately exit an application, but will + * display some information before dying. + */ +void exit_now(const char *format, ...) +{ + va_list argp; + + va_start(argp, format); + vfprintf(stderr, format, argp); + va_end(argp); + abort(); +} + diff --git a/ccast/axTLS/os_port.h b/ccast/axTLS/os_port.h new file mode 100644 index 0000000..8ede75a --- /dev/null +++ b/ccast/axTLS/os_port.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * @file os_port.h + * + * Some stuff to minimise the differences between windows and linux/unix + */ + +#ifndef HEADER_OS_PORT_H +#define HEADER_OS_PORT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if defined(NEVER) && defined(WIN32) +#define STDCALL __stdcall +#define EXP_FUNC __declspec(dllexport) +#else +#define STDCALL +#define EXP_FUNC +#endif + +#if defined(_WIN32_WCE) +#undef WIN32 +#define WIN32 +#endif + +#ifdef WIN32 + +/* Windows CE stuff */ +#if defined(_WIN32_WCE) +#include +#define abort() exit(1) +#else +#include +#include +#include +#include +#endif /* _WIN32_WCE */ + +#include +#include +#undef getpid +#undef open +#undef close +#undef sleep +#undef gettimeofday +#undef dup2 +#undef unlink + +#define SOCKET_READ(A,B,C) recv(A,B,C,0) +#define SOCKET_WRITE(A,B,C) send(A,B,C,0) +#define SOCKET_CLOSE(A) closesocket(A) +#define srandom(A) srand(A) +#define random() rand() +#define getpid() _getpid() +#define snprintf _snprintf +#define open(A,B) _open(A,B) +#define dup2(A,B) _dup2(A,B) +#define unlink(A) _unlink(A) +#define close(A) _close(A) +#define read(A,B,C) _read(A,B,C) +#define write(A,B,C) _write(A,B,C) +#define sleep(A) Sleep(A*1000) +#define usleep(A) Sleep(A/1000) +#define strdup(A) _strdup(A) +#define chroot(A) _chdir(A) +#define chdir(A) _chdir(A) +//#define alloca(A) _alloca(A) +#ifndef lseek +#define lseek(A,B,C) _lseek(A,B,C) +#endif + +/* This fix gets around a problem where a win32 application on a cygwin xterm + doesn't display regular output (until a certain buffer limit) - but it works + fine under a normal DOS window. This is a hack to get around the issue - + see http://www.khngai.com/emacs/tty.php */ +#define TTY_FLUSH() if (!_isatty(_fileno(stdout))) fflush(stdout); + +/* + * automatically build some library dependencies. + */ +#pragma comment(lib, "WS2_32.lib") +#pragma comment(lib, "AdvAPI32.lib") + +typedef int socklen_t; + +EXP_FUNC void STDCALL gettimeofday(struct timeval* t,void* timezone); +EXP_FUNC int STDCALL getdomainname(char *buf, int buf_size); +#ifndef __GNUC__ +EXP_FUNC int STDCALL strcasecmp(const char *s1, const char *s2); +#endif + +#else /* Not Win32 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOCKET_READ(A,B,C) read(A,B,C) +#define SOCKET_WRITE(A,B,C) write(A,B,C) +#define SOCKET_CLOSE(A) if (A >= 0) close(A) +#define TTY_FLUSH() + +#endif /* Not Win32 */ + +#include "os_int.h" + +/* some functions to mutate the way these work */ +#define malloc(A) ax_malloc(A) +#ifndef realloc +#define realloc(A,B) ax_realloc(A,B) +#endif +#define calloc(A,B) ax_calloc(A,B) + +EXP_FUNC void * STDCALL ax_malloc(size_t s); +EXP_FUNC void * STDCALL ax_realloc(void *y, size_t s); +EXP_FUNC void * STDCALL ax_calloc(size_t n, size_t s); +EXP_FUNC int STDCALL ax_open(const char *pathname, int flags); + +#ifdef CONFIG_PLATFORM_LINUX +void exit_now(const char *format, ...) __attribute((noreturn)); +#else +void exit_now(const char *format, ...); +#endif + +/* Mutexing definitions */ +#if defined(CONFIG_SSL_CTX_MUTEXING) +#if defined(WIN32) +#define SSL_CTX_MUTEX_TYPE HANDLE +#define SSL_CTX_MUTEX_INIT(A) A=CreateMutex(0, FALSE, 0) +#define SSL_CTX_MUTEX_DESTROY(A) CloseHandle(A) +#define SSL_CTX_LOCK(A) WaitForSingleObject(A, INFINITE) +#define SSL_CTX_UNLOCK(A) ReleaseMutex(A) +#else +#include +#define SSL_CTX_MUTEX_TYPE pthread_mutex_t +#define SSL_CTX_MUTEX_INIT(A) pthread_mutex_init(&A, NULL) +#define SSL_CTX_MUTEX_DESTROY(A) pthread_mutex_destroy(&A) +#define SSL_CTX_LOCK(A) pthread_mutex_lock(&A) +#define SSL_CTX_UNLOCK(A) pthread_mutex_unlock(&A) +#endif +#else /* no mutexing */ +#define SSL_CTX_MUTEX_INIT(A) +#define SSL_CTX_MUTEX_DESTROY(A) +#define SSL_CTX_LOCK(A) +#define SSL_CTX_UNLOCK(A) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ccast/axTLS/p12.c b/ccast/axTLS/p12.c new file mode 100644 index 0000000..2bafaf7 --- /dev/null +++ b/ccast/axTLS/p12.c @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * Process PKCS#8/PKCS#12 keys. + * + * The decoding of a PKCS#12 key is fairly specific - this code was tested on a + * key generated with: + * + * openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem + * -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 + * -name "p12_withoutCA" -out axTLS.withoutCA.p12 -password pass:abcd + * + * or with a certificate chain: + * + * openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem + * -certfile axTLS.ca_x509.pem -keypbe PBE-SHA1-RC4-128 -certpbe + * PBE-SHA1-RC4-128 -name "p12_withCA" -out axTLS.withCA.p12 -password pass:abcd + * + * Note that the PBE has to be specified with PBE-SHA1-RC4-128. The + * private/public keys/certs have to use RSA encryption. Both the integrity + * and privacy passwords are the same. + * + * The PKCS#8 files were generated with something like: + * + * PEM format: + * openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -v1 + * PBE-SHA1-RC4-128 -out axTLS.encrypted_pem.p8 + * + * DER format: + * openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -outform DER + * -v1 PBE-SHA1-RC4-128 -out axTLS.encrypted.p8 + */ + +#include +#include +#include +#include "os_port.h" +#include "ssl.h" + +/* all commented out if not used */ +#ifdef CONFIG_SSL_USE_PKCS12 + +#define BLOCK_SIZE 64 +#define PKCS12_KEY_ID 1 +#define PKCS12_IV_ID 2 +#define PKCS12_MAC_ID 3 + +static char *make_uni_pass(const char *password, int *uni_pass_len); +static int p8_decrypt(const char *uni_pass, int uni_pass_len, + const uint8_t *salt, int iter, + uint8_t *priv_key, int priv_key_len, int id); +static int p8_add_key(SSL_CTX *ssl_ctx, uint8_t *priv_key); +static int get_pbe_params(uint8_t *buf, int *offset, + const uint8_t **salt, int *iterations); + +/* + * Take a raw pkcs8 block and then decrypt it and turn it into a normal key. + */ +int pkcs8_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password) +{ + uint8_t *buf = ssl_obj->buf; + int len, offset = 0; + int iterations; + int ret = SSL_NOT_OK; + uint8_t *version = NULL; + const uint8_t *salt; + uint8_t *priv_key; + int uni_pass_len; + char *uni_pass = make_uni_pass(password, &uni_pass_len); + + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: Invalid p8 ASN.1 file\n"); +#endif + goto error; + } + + /* unencrypted key? */ + if (asn1_get_int(buf, &offset, &version) > 0 && *version == 0) + { + ret = p8_add_key(ssl_ctx, buf); + goto error; + } + + if (get_pbe_params(buf, &offset, &salt, &iterations) < 0) + goto error; + + if ((len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0) + goto error; + + priv_key = &buf[offset]; + + p8_decrypt(uni_pass, uni_pass_len, salt, + iterations, priv_key, len, PKCS12_KEY_ID); + ret = p8_add_key(ssl_ctx, priv_key); + +error: + free(version); + free(uni_pass); + return ret; +} + +/* + * Take the unencrypted pkcs8 and turn it into a private key + */ +static int p8_add_key(SSL_CTX *ssl_ctx, uint8_t *priv_key) +{ + uint8_t *buf = priv_key; + int len, offset = 0; + int ret = SSL_NOT_OK; + + /* Skip the preamble and go straight to the private key. + We only support rsaEncryption (1.2.840.113549.1.1.1) */ + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(buf, &offset, ASN1_INTEGER) < 0 || + asn1_skip_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0) + goto error; + + ret = asn1_get_private_key(&buf[offset], len, &ssl_ctx->rsa_ctx); + +error: + return ret; +} + +/* + * Create the unicode password + */ +static char *make_uni_pass(const char *password, int *uni_pass_len) +{ + int pass_len = 0, i; + char *uni_pass; + + if (password == NULL) + { + password = ""; + } + + uni_pass = (char *)malloc((strlen(password)+1)*2); + + /* modify the password into a unicode version */ + for (i = 0; i < (int)strlen(password); i++) + { + uni_pass[pass_len++] = 0; + uni_pass[pass_len++] = password[i]; + } + + uni_pass[pass_len++] = 0; /* null terminate */ + uni_pass[pass_len++] = 0; + *uni_pass_len = pass_len; + return uni_pass; +} + +/* + * Decrypt a pkcs8 block. + */ +static int p8_decrypt(const char *uni_pass, int uni_pass_len, + const uint8_t *salt, int iter, + uint8_t *priv_key, int priv_key_len, int id) +{ + uint8_t p[BLOCK_SIZE*2]; + uint8_t d[BLOCK_SIZE]; + uint8_t Ai[SHA1_SIZE]; + SHA1_CTX sha_ctx; + RC4_CTX rc4_ctx; + int i; + + for (i = 0; i < BLOCK_SIZE; i++) + { + p[i] = salt[i % SALT_SIZE]; + p[BLOCK_SIZE+i] = uni_pass[i % uni_pass_len]; + d[i] = id; + } + + /* get the key - no IV since we are using RC4 */ + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, d, sizeof(d)); + SHA1_Update(&sha_ctx, p, sizeof(p)); + SHA1_Final(Ai, &sha_ctx); + + for (i = 1; i < iter; i++) + { + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, Ai, SHA1_SIZE); + SHA1_Final(Ai, &sha_ctx); + } + + /* do the decryption */ + if (id == PKCS12_KEY_ID) + { + RC4_setup(&rc4_ctx, Ai, 16); + RC4_crypt(&rc4_ctx, priv_key, priv_key, priv_key_len); + } + else /* MAC */ + memcpy(priv_key, Ai, SHA1_SIZE); + + return 0; +} + +/* + * Take a raw pkcs12 block and the decrypt it and turn it into a certificate(s) + * and keys. + */ +int pkcs12_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password) +{ + uint8_t *buf = ssl_obj->buf; + int len, iterations, auth_safes_start, + auth_safes_end, auth_safes_len, key_offset, offset = 0; + int all_certs = 0; + uint8_t *version = NULL, *auth_safes = NULL, *cert, *orig_mac; + uint8_t key[SHA1_SIZE]; + uint8_t mac[SHA1_SIZE]; + const uint8_t *salt; + int uni_pass_len, ret = SSL_OK; + char *uni_pass = make_uni_pass(password, &uni_pass_len); + static const uint8_t pkcs_data[] = /* pkc7 data */ + { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01 }; + static const uint8_t pkcs_encrypted[] = /* pkc7 encrypted */ + { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x06 }; + static const uint8_t pkcs8_key_bag[] = /* 1.2.840.113549.1.12.10.1.2 */ + { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02 }; + + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: Invalid p12 ASN.1 file\n"); +#endif + goto error; + } + + if (asn1_get_int(buf, &offset, &version) < 0 || *version != 3) + { + ret = SSL_ERROR_INVALID_VERSION; + goto error; + } + + /* remove all the boring pcks7 bits */ + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + len != sizeof(pkcs_data) || + memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data))) + goto error; + + offset += len; + + if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(buf, &offset, ASN1_OCTET_STRING) < 0) + goto error; + + /* work out the MAC start/end points (done on AuthSafes) */ + auth_safes_start = offset; + auth_safes_end = offset; + if (asn1_skip_obj(buf, &auth_safes_end, ASN1_SEQUENCE) < 0) + goto error; + + auth_safes_len = auth_safes_end - auth_safes_start; + auth_safes = malloc(auth_safes_len); + + memcpy(auth_safes, &buf[auth_safes_start], auth_safes_len); + + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + (len != sizeof(pkcs_encrypted) || + memcmp(&buf[offset], pkcs_encrypted, sizeof(pkcs_encrypted)))) + goto error; + + offset += len; + + if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(buf, &offset, ASN1_INTEGER) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + len != sizeof(pkcs_data) || + memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data))) + goto error; + + offset += len; + + /* work out the salt for the certificate */ + if (get_pbe_params(buf, &offset, &salt, &iterations) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_IMPLICIT_TAG)) < 0) + goto error; + + /* decrypt the certificate */ + cert = &buf[offset]; + if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, cert, + len, PKCS12_KEY_ID)) < 0) + goto error; + + offset += len; + + /* load the certificate */ + key_offset = 0; + all_certs = asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE); + + /* keep going until all certs are loaded */ + while (key_offset < all_certs) + { + int cert_offset = key_offset; + + if (asn1_skip_obj(cert, &cert_offset, ASN1_SEQUENCE) < 0 || + asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(cert, &key_offset, ASN1_OID) < 0 || + asn1_next_obj(cert, &key_offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(cert, &key_offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(cert, &key_offset, ASN1_OID) < 0 || + asn1_next_obj(cert, &key_offset, ASN1_EXPLICIT_TAG) < 0 || + (len = asn1_next_obj(cert, &key_offset, ASN1_OCTET_STRING)) < 0) + goto error; + + if ((ret = add_cert(ssl_ctx, &cert[key_offset], len)) < 0) + goto error; + + key_offset = cert_offset; + } + + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + len != sizeof(pkcs_data) || + memcmp(&buf[offset], pkcs_data, sizeof(pkcs_data))) + goto error; + + offset += len; + + if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(buf, &offset, ASN1_OCTET_STRING) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OID)) < 0 || + (len != sizeof(pkcs8_key_bag)) || + memcmp(&buf[offset], pkcs8_key_bag, sizeof(pkcs8_key_bag))) + goto error; + + offset += len; + + /* work out the salt for the private key */ + if (asn1_next_obj(buf, &offset, ASN1_EXPLICIT_TAG) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + get_pbe_params(buf, &offset, &salt, &iterations) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0) + goto error; + + /* decrypt the private key */ + cert = &buf[offset]; + if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, cert, + len, PKCS12_KEY_ID)) < 0) + goto error; + + offset += len; + + /* load the private key */ + if ((ret = p8_add_key(ssl_ctx, cert)) < 0) + goto error; + + /* miss out on friendly name, local key id etc */ + if (asn1_skip_obj(buf, &offset, ASN1_SET) < 0) + goto error; + + /* work out the MAC */ + if (asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_next_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(buf, &offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0 || + len != SHA1_SIZE) + goto error; + + orig_mac = &buf[offset]; + offset += len; + + /* get the salt */ + if ((len = asn1_next_obj(buf, &offset, ASN1_OCTET_STRING)) < 0 || len != 8) + goto error; + + salt = &buf[offset]; + + /* work out what the mac should be */ + if ((ret = p8_decrypt(uni_pass, uni_pass_len, salt, iterations, + key, SHA1_SIZE, PKCS12_MAC_ID)) < 0) + goto error; + + hmac_sha1(auth_safes, auth_safes_len, key, SHA1_SIZE, mac); + + if (memcmp(mac, orig_mac, SHA1_SIZE)) + { + ret = SSL_ERROR_INVALID_HMAC; + goto error; + } + +error: + free(version); + free(uni_pass); + free(auth_safes); + return ret; +} + +/* + * Retrieve the salt/iteration details from a PBE block. + */ +static int get_pbe_params(uint8_t *buf, int *offset, + const uint8_t **salt, int *iterations) +{ + static const uint8_t pbeSH1RC4[] = /* pbeWithSHAAnd128BitRC4 */ + { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x01 }; + + int i, len; + uint8_t *iter = NULL; + int error_code = SSL_ERROR_NOT_SUPPORTED; + + /* Get the PBE type */ + if (asn1_next_obj(buf, offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, offset, ASN1_OID)) < 0) + goto error; + + /* we expect pbeWithSHAAnd128BitRC4 (1.2.840.113549.1.12.1.1) + which is the only algorithm we support */ + if (len != sizeof(pbeSH1RC4) || + memcmp(&buf[*offset], pbeSH1RC4, sizeof(pbeSH1RC4))) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: pkcs8/pkcs12 must use \"PBE-SHA1-RC4-128\"\n"); +#endif + goto error; + } + + *offset += len; + + if (asn1_next_obj(buf, offset, ASN1_SEQUENCE) < 0 || + (len = asn1_next_obj(buf, offset, ASN1_OCTET_STRING)) < 0 || + len != 8) + goto error; + + *salt = &buf[*offset]; + *offset += len; + + if ((len = asn1_get_int(buf, offset, &iter)) < 0) + goto error; + + *iterations = 0; + for (i = 0; i < len; i++) + { + (*iterations) <<= 8; + (*iterations) += iter[i]; + } + + free(iter); + error_code = SSL_OK; /* got here - we are ok */ + +error: + return error_code; +} + +#endif diff --git a/ccast/axTLS/private_key.h b/ccast/axTLS/private_key.h new file mode 100644 index 0000000..ce7985c --- /dev/null +++ b/ccast/axTLS/private_key.h @@ -0,0 +1,54 @@ +unsigned char default_private_key[] = { + 0x30, 0x82, 0x02, 0x5d, 0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xcd, + 0xfd, 0x89, 0x48, 0xbe, 0x36, 0xb9, 0x95, 0x76, 0xd4, 0x13, 0x30, 0x0e, + 0xbf, 0xb2, 0xed, 0x67, 0x0a, 0xc0, 0x16, 0x3f, 0x51, 0x09, 0x9d, 0x29, + 0x2f, 0xb2, 0x6d, 0x3f, 0x3e, 0x6c, 0x2f, 0x90, 0x80, 0xa1, 0x71, 0xdf, + 0xbe, 0x38, 0xc5, 0xcb, 0xa9, 0x9a, 0x40, 0x14, 0x90, 0x0a, 0xf9, 0xb7, + 0x07, 0x0b, 0xe1, 0xda, 0xe7, 0x09, 0xbf, 0x0d, 0x57, 0x41, 0x86, 0x60, + 0xa1, 0xc1, 0x27, 0x91, 0x5b, 0x0a, 0x98, 0x46, 0x1b, 0xf6, 0xa2, 0x84, + 0xf8, 0x65, 0xc7, 0xce, 0x2d, 0x96, 0x17, 0xaa, 0x91, 0xf8, 0x61, 0x04, + 0x50, 0x70, 0xeb, 0xb4, 0x43, 0xb7, 0xdc, 0x9a, 0xcc, 0x31, 0x01, 0x14, + 0xd4, 0xcd, 0xcc, 0xc2, 0x37, 0x6d, 0x69, 0x82, 0xd6, 0xc6, 0xc4, 0xbe, + 0xf2, 0x34, 0xa5, 0xc9, 0xa6, 0x19, 0x53, 0x32, 0x7a, 0x86, 0x0e, 0x91, + 0x82, 0x0f, 0xa1, 0x42, 0x54, 0xaa, 0x01, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x81, 0x81, 0x00, 0x95, 0xaa, 0x6e, 0x11, 0xf5, 0x6a, 0x8b, 0xa2, + 0xc6, 0x48, 0xc6, 0x7c, 0x37, 0x6b, 0x1f, 0x55, 0x10, 0x76, 0x26, 0x24, + 0xc3, 0xf2, 0x5c, 0x5a, 0xdd, 0x2e, 0xf3, 0xa4, 0x1e, 0xbc, 0x7b, 0x1c, + 0x80, 0x10, 0x85, 0xbc, 0xd8, 0x45, 0x3c, 0xb8, 0xb2, 0x06, 0x53, 0xb5, + 0xd5, 0x7a, 0xe7, 0x0e, 0x92, 0xe6, 0x42, 0xc2, 0xe2, 0x2a, 0xd5, 0xd1, + 0x03, 0x9f, 0x6f, 0x53, 0x74, 0x68, 0x72, 0x8e, 0xbf, 0x03, 0xbb, 0xab, + 0xbd, 0xa1, 0xf9, 0x81, 0x7d, 0x12, 0xd4, 0x9d, 0xb6, 0xae, 0x4c, 0xad, + 0xca, 0xa8, 0xc9, 0x80, 0x8d, 0x0d, 0xd5, 0xd0, 0xa1, 0xbf, 0xec, 0x60, + 0x48, 0x49, 0xed, 0x97, 0x0f, 0x5e, 0xed, 0xfc, 0x39, 0x15, 0x96, 0x9e, + 0x5d, 0xe2, 0xb4, 0x5d, 0x2e, 0x04, 0xdc, 0x08, 0xa2, 0x65, 0x29, 0x2d, + 0x37, 0xfb, 0x62, 0x90, 0x1b, 0x7b, 0xe5, 0x3a, 0x58, 0x05, 0x55, 0xc1, + 0x02, 0x41, 0x00, 0xfc, 0x69, 0x28, 0xc9, 0xa8, 0xc4, 0x5c, 0xe3, 0xd0, + 0x5e, 0xaa, 0xda, 0xde, 0x87, 0x74, 0xdb, 0xcb, 0x40, 0x78, 0x8e, 0x1d, + 0x12, 0x96, 0x16, 0x61, 0x3f, 0xb3, 0x3e, 0xa3, 0x0d, 0xdc, 0x49, 0xa5, + 0x25, 0x87, 0xc5, 0x97, 0x85, 0x9d, 0xbb, 0xb4, 0xf0, 0x44, 0xfd, 0x6c, + 0xe8, 0xd2, 0x8c, 0xec, 0x33, 0x81, 0x46, 0x1e, 0x10, 0x12, 0x33, 0x16, + 0x95, 0x00, 0x4f, 0x75, 0xb4, 0xe5, 0x79, 0x02, 0x41, 0x00, 0xd0, 0xeb, + 0x65, 0x07, 0x10, 0x3b, 0xd9, 0x03, 0xeb, 0xdc, 0x6f, 0x4b, 0x8f, 0xc3, + 0x87, 0xce, 0x76, 0xd6, 0xc5, 0x14, 0x21, 0x4e, 0xe7, 0x4f, 0x1b, 0xe8, + 0x05, 0xf8, 0x84, 0x1a, 0xe0, 0xc5, 0xd6, 0xe3, 0x08, 0xb3, 0x54, 0x57, + 0x02, 0x1f, 0xd4, 0xd9, 0xfb, 0xff, 0x40, 0xb1, 0x56, 0x1c, 0x60, 0xf7, + 0xac, 0x91, 0xf3, 0xd3, 0xc6, 0x7f, 0x84, 0xfd, 0x84, 0x9d, 0xea, 0x26, + 0xee, 0xc9, 0x02, 0x41, 0x00, 0xa6, 0xcf, 0x1c, 0x6c, 0x81, 0x03, 0x1c, + 0x5c, 0x56, 0x05, 0x6a, 0x26, 0x70, 0xef, 0xd6, 0x13, 0xb7, 0x74, 0x28, + 0xf7, 0xca, 0x50, 0xd1, 0x2d, 0x83, 0x21, 0x64, 0xe4, 0xdd, 0x3f, 0x38, + 0xb8, 0xd6, 0xd2, 0x41, 0xb3, 0x1c, 0x9a, 0xea, 0x0d, 0xf5, 0xda, 0xdf, + 0xcd, 0x17, 0x9f, 0x9a, 0x1e, 0x15, 0xaf, 0x48, 0x1c, 0xbd, 0x9b, 0x63, + 0x5b, 0xad, 0xed, 0xd4, 0xa1, 0xae, 0xa9, 0x59, 0x09, 0x02, 0x40, 0x4e, + 0x08, 0xce, 0xa8, 0x8f, 0xc0, 0xba, 0xf3, 0x83, 0x02, 0xc8, 0x33, 0x62, + 0x14, 0x77, 0xc2, 0x7f, 0x93, 0x02, 0xf3, 0xdc, 0xe9, 0x1a, 0xee, 0xea, + 0x8e, 0x84, 0xc4, 0x69, 0x9b, 0x9c, 0x7f, 0x69, 0x1f, 0x4e, 0x1d, 0xa5, + 0x90, 0x06, 0x44, 0x1b, 0x7d, 0xfc, 0x69, 0x40, 0x21, 0xbc, 0xf7, 0x46, + 0xa4, 0xdc, 0x39, 0x7b, 0xe8, 0x8b, 0x49, 0x10, 0x44, 0x9d, 0x67, 0x5a, + 0x91, 0x86, 0x39, 0x02, 0x40, 0x41, 0x2c, 0x4e, 0xfe, 0xd9, 0x90, 0x89, + 0x00, 0x5c, 0x94, 0x0a, 0x4a, 0x7e, 0x1b, 0x1a, 0x80, 0x06, 0x01, 0x37, + 0xda, 0x50, 0x61, 0x9d, 0x9c, 0xfe, 0x25, 0x7f, 0xd8, 0xd4, 0xc4, 0x9e, + 0x81, 0xf2, 0x0c, 0x1e, 0x38, 0x21, 0x1e, 0x90, 0x3f, 0xd4, 0xba, 0x6c, + 0x53, 0xcb, 0xf0, 0x77, 0x79, 0x9b, 0xf1, 0xfa, 0x3f, 0x81, 0xdc, 0xf3, + 0x21, 0x02, 0x6d, 0xb7, 0x95, 0xc3, 0x2e, 0xce, 0xd5 +}; +unsigned int default_private_key_len = 609; diff --git a/ccast/axTLS/rc4.c b/ccast/axTLS/rc4.c new file mode 100644 index 0000000..12a1211 --- /dev/null +++ b/ccast/axTLS/rc4.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * An implementation of the RC4/ARC4 algorithm. + * Originally written by Christophe Devine. + */ + +#include +#include "os_port.h" +#include "crypto.h" + +/** + * Get ready for an encrypt/decrypt operation + */ +void RC4_setup(RC4_CTX *ctx, const uint8_t *key, int length) +{ + int i, j = 0, k = 0, a; + uint8_t *m; + + ctx->x = 0; + ctx->y = 0; + m = ctx->m; + + for (i = 0; i < 256; i++) + m[i] = i; + + for (i = 0; i < 256; i++) + { + a = m[i]; + j = (uint8_t)(j + a + key[k]); + m[i] = m[j]; + m[j] = a; + + if (++k >= length) + k = 0; + } +} + +/** + * Perform the encrypt/decrypt operation (can use it for either since + * this is a stream cipher). + * NOTE: *msg and *out must be the same pointer (performance tweak) + */ +void RC4_crypt(RC4_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) +{ + int i; + uint8_t *m, x, y, a, b; + + x = ctx->x; + y = ctx->y; + m = ctx->m; + + for (i = 0; i < length; i++) + { + a = m[++x]; + y += a; + m[x] = b = m[y]; + m[y] = a; + out[i] ^= m[(uint8_t)(a + b)]; + } + + ctx->x = x; + ctx->y = y; +} diff --git a/ccast/axTLS/rsa.c b/ccast/axTLS/rsa.c new file mode 100644 index 0000000..e707f2b --- /dev/null +++ b/ccast/axTLS/rsa.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * Implements the RSA public encryption algorithm. Uses the bigint library to + * perform its calculations. + */ + +#include +#include +#include +#include +#include "os_port.h" +#include "crypto.h" + +void RSA_priv_key_new(RSA_CTX **ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len, + const uint8_t *priv_exp, int priv_len +#ifdef CONFIG_BIGINT_CRT + , const uint8_t *p, int p_len, + const uint8_t *q, int q_len, + const uint8_t *dP, int dP_len, + const uint8_t *dQ, int dQ_len, + const uint8_t *qInv, int qInv_len +#endif + ) +{ + RSA_CTX *rsa_ctx; + BI_CTX *bi_ctx; + RSA_pub_key_new(ctx, modulus, mod_len, pub_exp, pub_len); + rsa_ctx = *ctx; + bi_ctx = rsa_ctx->bi_ctx; + rsa_ctx->d = bi_import(bi_ctx, priv_exp, priv_len); + bi_permanent(rsa_ctx->d); + +#ifdef CONFIG_BIGINT_CRT + rsa_ctx->p = bi_import(bi_ctx, p, p_len); + rsa_ctx->q = bi_import(bi_ctx, q, q_len); + rsa_ctx->dP = bi_import(bi_ctx, dP, dP_len); + rsa_ctx->dQ = bi_import(bi_ctx, dQ, dQ_len); + rsa_ctx->qInv = bi_import(bi_ctx, qInv, qInv_len); + bi_permanent(rsa_ctx->dP); + bi_permanent(rsa_ctx->dQ); + bi_permanent(rsa_ctx->qInv); + bi_set_mod(bi_ctx, rsa_ctx->p, BIGINT_P_OFFSET); + bi_set_mod(bi_ctx, rsa_ctx->q, BIGINT_Q_OFFSET); +#endif +} + +void RSA_pub_key_new(RSA_CTX **ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len) +{ + RSA_CTX *rsa_ctx; + BI_CTX *bi_ctx; + + if (*ctx) /* if we load multiple certs, dump the old one */ + RSA_free(*ctx); + + bi_ctx = bi_initialize(); + *ctx = (RSA_CTX *)calloc(1, sizeof(RSA_CTX)); + rsa_ctx = *ctx; + rsa_ctx->bi_ctx = bi_ctx; + rsa_ctx->num_octets = mod_len; + rsa_ctx->m = bi_import(bi_ctx, modulus, mod_len); + bi_set_mod(bi_ctx, rsa_ctx->m, BIGINT_M_OFFSET); + rsa_ctx->e = bi_import(bi_ctx, pub_exp, pub_len); + bi_permanent(rsa_ctx->e); +} + +/** + * Free up any RSA context resources. + */ +void RSA_free(RSA_CTX *rsa_ctx) +{ + BI_CTX *bi_ctx; + if (rsa_ctx == NULL) /* deal with ptrs that are null */ + return; + + bi_ctx = rsa_ctx->bi_ctx; + + bi_depermanent(rsa_ctx->e); + bi_free(bi_ctx, rsa_ctx->e); + bi_free_mod(rsa_ctx->bi_ctx, BIGINT_M_OFFSET); + + if (rsa_ctx->d) + { + bi_depermanent(rsa_ctx->d); + bi_free(bi_ctx, rsa_ctx->d); +#ifdef CONFIG_BIGINT_CRT + bi_depermanent(rsa_ctx->dP); + bi_depermanent(rsa_ctx->dQ); + bi_depermanent(rsa_ctx->qInv); + bi_free(bi_ctx, rsa_ctx->dP); + bi_free(bi_ctx, rsa_ctx->dQ); + bi_free(bi_ctx, rsa_ctx->qInv); + bi_free_mod(rsa_ctx->bi_ctx, BIGINT_P_OFFSET); + bi_free_mod(rsa_ctx->bi_ctx, BIGINT_Q_OFFSET); +#endif + } + + bi_terminate(bi_ctx); + free(rsa_ctx); +} + +/** + * @brief Use PKCS1.5 for decryption/verification. + * @param ctx [in] The context + * @param in_data [in] The data to encrypt (must be < modulus size-11) + * @param out_data [out] The encrypted data. + * @param is_decryption [in] Decryption or verify operation. + * @return The number of bytes that were originally encrypted. -1 on error. + * @see http://www.rsasecurity.com/rsalabs/node.asp?id=2125 + */ +int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, + uint8_t *out_data, int is_decryption) +{ + const int byte_size = ctx->num_octets; + int i, size; + bigint *decrypted_bi, *dat_bi; + uint8_t *block = (uint8_t *)malloc(byte_size); + + memset(out_data, 0, byte_size); /* initialise */ + + /* decrypt */ + dat_bi = bi_import(ctx->bi_ctx, in_data, byte_size); +#ifdef CONFIG_SSL_CERT_VERIFICATION + decrypted_bi = is_decryption ? /* decrypt or verify? */ + RSA_private(ctx, dat_bi) : RSA_public(ctx, dat_bi); +#else /* always a decryption */ + decrypted_bi = RSA_private(ctx, dat_bi); +#endif + + /* convert to a normal block */ + bi_export(ctx->bi_ctx, decrypted_bi, block, byte_size); + + i = 10; /* start at the first possible non-padded byte */ + +#ifdef CONFIG_SSL_CERT_VERIFICATION + if (is_decryption == 0) /* PKCS1.5 signing pads with "0xff"s */ + { + while (block[i++] == 0xff && i < byte_size); + + if (block[i-2] != 0xff) + i = byte_size; /*ensure size is 0 */ + } + else /* PKCS1.5 encryption padding is random */ +#endif + { + while (block[i++] && i < byte_size); + } + size = byte_size - i; + + /* get only the bit we want */ + if (size > 0) + memcpy(out_data, &block[i], size); + + free(block); + return size ? size : -1; +} + +/** + * Performs m = c^d mod n + */ +bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg) +{ +#ifdef CONFIG_BIGINT_CRT + return bi_crt(c->bi_ctx, bi_msg, c->dP, c->dQ, c->p, c->q, c->qInv); +#else + BI_CTX *ctx = c->bi_ctx; + ctx->mod_offset = BIGINT_M_OFFSET; + return bi_mod_power(ctx, bi_msg, c->d); +#endif +} + +#ifdef CONFIG_SSL_FULL_MODE +/** + * Used for diagnostics. + */ +void RSA_print(const RSA_CTX *rsa_ctx) +{ + if (rsa_ctx == NULL) + return; + + printf("----------------- RSA DEBUG ----------------\n"); + printf("Size:\t%d\n", rsa_ctx->num_octets); + bi_print("Modulus", rsa_ctx->m); + bi_print("Public Key", rsa_ctx->e); + bi_print("Private Key", rsa_ctx->d); +} +#endif + +#if defined(CONFIG_SSL_CERT_VERIFICATION) || defined(CONFIG_SSL_GENERATE_X509_CERT) +/** + * Performs c = m^e mod n + */ +bigint *RSA_public(const RSA_CTX * c, bigint *bi_msg) +{ + c->bi_ctx->mod_offset = BIGINT_M_OFFSET; + return bi_mod_power(c->bi_ctx, bi_msg, c->e); +} + +/** + * Use PKCS1.5 for encryption/signing. + * see http://www.rsasecurity.com/rsalabs/node.asp?id=2125 + */ +int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, + uint8_t *out_data, int is_signing) +{ + int byte_size = ctx->num_octets; + int num_pads_needed = byte_size-in_len-3; + bigint *dat_bi, *encrypt_bi; + + /* note: in_len+11 must be > byte_size */ + out_data[0] = 0; /* ensure encryption block is < modulus */ + + if (is_signing) + { + out_data[1] = 1; /* PKCS1.5 signing pads with "0xff"'s */ + memset(&out_data[2], 0xff, num_pads_needed); + } + else /* randomize the encryption padding with non-zero bytes */ + { + out_data[1] = 2; + get_random_NZ(num_pads_needed, &out_data[2]); + } + + out_data[2+num_pads_needed] = 0; + memcpy(&out_data[3+num_pads_needed], in_data, in_len); + + /* now encrypt it */ + dat_bi = bi_import(ctx->bi_ctx, out_data, byte_size); + encrypt_bi = is_signing ? RSA_private(ctx, dat_bi) : + RSA_public(ctx, dat_bi); + bi_export(ctx->bi_ctx, encrypt_bi, out_data, byte_size); + + /* save a few bytes of memory */ + bi_clear_cache(ctx->bi_ctx); + return byte_size; +} + +#endif /* CONFIG_SSL_CERT_VERIFICATION */ diff --git a/ccast/axTLS/sha1.c b/ccast/axTLS/sha1.c new file mode 100644 index 0000000..1082733 --- /dev/null +++ b/ccast/axTLS/sha1.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * SHA1 implementation - as defined in FIPS PUB 180-1 published April 17, 1995. + * This code was originally taken from RFC3174 + */ + +#include +#include "os_port.h" +#include "crypto.h" + +/* + * Define the SHA1 circular left shift macro + */ +#define SHA1CircularShift(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* ----- static functions ----- */ +static void SHA1PadMessage(SHA1_CTX *ctx); +static void SHA1ProcessMessageBlock(SHA1_CTX *ctx); + +/** + * Initialize the SHA1 context + */ +void SHA1_Init(SHA1_CTX *ctx) +{ + ctx->Length_Low = 0; + ctx->Length_High = 0; + ctx->Message_Block_Index = 0; + ctx->Intermediate_Hash[0] = 0x67452301; + ctx->Intermediate_Hash[1] = 0xEFCDAB89; + ctx->Intermediate_Hash[2] = 0x98BADCFE; + ctx->Intermediate_Hash[3] = 0x10325476; + ctx->Intermediate_Hash[4] = 0xC3D2E1F0; +} + +/** + * Accepts an array of octets as the next portion of the message. + */ +void SHA1_Update(SHA1_CTX *ctx, const uint8_t *msg, int len) +{ + while (len--) + { + ctx->Message_Block[ctx->Message_Block_Index++] = (*msg & 0xFF); + ctx->Length_Low += 8; + + if (ctx->Length_Low == 0) + ctx->Length_High++; + + if (ctx->Message_Block_Index == 64) + SHA1ProcessMessageBlock(ctx); + + msg++; + } +} + +/** + * Return the 160-bit message digest into the user's array + */ +void SHA1_Final(uint8_t *digest, SHA1_CTX *ctx) +{ + int i; + + SHA1PadMessage(ctx); + memset(ctx->Message_Block, 0, 64); + ctx->Length_Low = 0; /* and clear length */ + ctx->Length_High = 0; + + for (i = 0; i < SHA1_SIZE; i++) + { + digest[i] = ctx->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) ); + } +} + +/** + * Process the next 512 bits of the message stored in the array. + */ +static void SHA1ProcessMessageBlock(SHA1_CTX *ctx) +{ + const uint32_t K[] = { /* Constants defined in SHA-1 */ + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + uint32_t temp; /* Temporary word value */ + uint32_t W[80]; /* Word sequence */ + uint32_t A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for (t = 0; t < 16; t++) + { + W[t] = ctx->Message_Block[t * 4] << 24; + W[t] |= ctx->Message_Block[t * 4 + 1] << 16; + W[t] |= ctx->Message_Block[t * 4 + 2] << 8; + W[t] |= ctx->Message_Block[t * 4 + 3]; + } + + for (t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = ctx->Intermediate_Hash[0]; + B = ctx->Intermediate_Hash[1]; + C = ctx->Intermediate_Hash[2]; + D = ctx->Intermediate_Hash[3]; + E = ctx->Intermediate_Hash[4]; + + for (t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + + B = A; + A = temp; + } + + for (t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for (t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for (t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + ctx->Intermediate_Hash[0] += A; + ctx->Intermediate_Hash[1] += B; + ctx->Intermediate_Hash[2] += C; + ctx->Intermediate_Hash[3] += D; + ctx->Intermediate_Hash[4] += E; + ctx->Message_Block_Index = 0; +} + +/* + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call the ProcessMessageBlock function + * provided appropriately. When it returns, it can be assumed that + * the message digest has been computed. + * + * @param ctx [in, out] The SHA1 context + */ +static void SHA1PadMessage(SHA1_CTX *ctx) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (ctx->Message_Block_Index > 55) + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0x80; + while(ctx->Message_Block_Index < 64) + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(ctx); + + while (ctx->Message_Block_Index < 56) + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0; + } + } + else + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0x80; + while(ctx->Message_Block_Index < 56) + { + + ctx->Message_Block[ctx->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + ctx->Message_Block[56] = ctx->Length_High >> 24; + ctx->Message_Block[57] = ctx->Length_High >> 16; + ctx->Message_Block[58] = ctx->Length_High >> 8; + ctx->Message_Block[59] = ctx->Length_High; + ctx->Message_Block[60] = ctx->Length_Low >> 24; + ctx->Message_Block[61] = ctx->Length_Low >> 16; + ctx->Message_Block[62] = ctx->Length_Low >> 8; + ctx->Message_Block[63] = ctx->Length_Low; + SHA1ProcessMessageBlock(ctx); +} diff --git a/ccast/axTLS/ssl.h b/ccast/axTLS/ssl.h new file mode 100644 index 0000000..3f001da --- /dev/null +++ b/ccast/axTLS/ssl.h @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * @mainpage axTLS API + * + * @image html axolotl.jpg + * + * The axTLS library has features such as: + * - The TLSv1 SSL client/server protocol + * - No requirement to use any openssl libraries. + * - A choice between AES block (128/256 bit) and RC4 (128 bit) stream ciphers. + * - RSA encryption/decryption with variable sized keys (up to 4096 bits). + * - Certificate chaining and peer authentication. + * - Session resumption, session renegotiation. + * - ASN.1, X.509, PKCS#8, PKCS#12 keys/certificates with DER/PEM encoding. + * - Highly configurable compile time options. + * - Portable across many platforms (written in ANSI C), and has language + * bindings in C, C#, VB.NET, Java, Perl and Lua. + * - Partial openssl API compatibility (via a wrapper). + * - A very small footprint (around 50-60kB for the library in 'server-only' + * mode). + * - No dependencies on sockets - can use serial connections for example. + * - A very simple API - ~ 20 functions/methods. + * + * A list of these functions/methods are described below. + * + * @ref c_api + * + * @ref bigint_api + * + * @ref csharp_api + * + * @ref java_api + */ +#ifndef HEADER_SSL_H +#define HEADER_SSL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* need to predefine before ssl_lib.h gets to it */ +#define SSL_SESSION_ID_SIZE 32 + +#include "tls1.h" + +/* The optional parameters that can be given to the client/server SSL engine */ +#define SSL_CLIENT_AUTHENTICATION 0x00010000 +#define SSL_SERVER_VERIFY_LATER 0x00020000 +#define SSL_NO_DEFAULT_KEY 0x00040000 +#define SSL_DISPLAY_STATES 0x00080000 +#define SSL_DISPLAY_BYTES 0x00100000 +#define SSL_DISPLAY_CERTS 0x00200000 +#define SSL_DISPLAY_RSA 0x00400000 +#define SSL_CONNECT_IN_PARTS 0x00800000 + +/* errors that can be generated */ +#define SSL_OK 0 +#define SSL_NOT_OK -1 +#define SSL_ERROR_DEAD -2 +#define SSL_CLOSE_NOTIFY -3 +#define SSL_TIMEDOUT -4 +#define SSL_ERROR_CONN_LOST -256 +#define SSL_ERROR_SOCK_SETUP_FAILURE -258 +#define SSL_ERROR_INVALID_HANDSHAKE -260 +#define SSL_ERROR_INVALID_PROT_MSG -261 +#define SSL_ERROR_INVALID_HMAC -262 +#define SSL_ERROR_INVALID_VERSION -263 +#define SSL_ERROR_INVALID_SESSION -265 +#define SSL_ERROR_NO_CIPHER -266 +#define SSL_ERROR_BAD_CERTIFICATE -268 +#define SSL_ERROR_INVALID_KEY -269 +#define SSL_ERROR_FINISHED_INVALID -271 +#define SSL_ERROR_NO_CERT_DEFINED -272 +#define SSL_ERROR_NO_CLIENT_RENOG -273 +#define SSL_ERROR_NOT_SUPPORTED -274 +#define SSL_X509_OFFSET -512 +#define SSL_X509_ERROR(A) (SSL_X509_OFFSET+A) + +/* alert types that are recognized */ +#define SSL_ALERT_TYPE_WARNING 1 +#define SLL_ALERT_TYPE_FATAL 2 + +/* these are all the alerts that are recognized */ +#define SSL_ALERT_CLOSE_NOTIFY 0 +#define SSL_ALERT_UNEXPECTED_MESSAGE 10 +#define SSL_ALERT_BAD_RECORD_MAC 20 +#define SSL_ALERT_HANDSHAKE_FAILURE 40 +#define SSL_ALERT_BAD_CERTIFICATE 42 +#define SSL_ALERT_ILLEGAL_PARAMETER 47 +#define SSL_ALERT_DECODE_ERROR 50 +#define SSL_ALERT_DECRYPT_ERROR 51 +#define SSL_ALERT_INVALID_VERSION 70 +#define SSL_ALERT_NO_RENEGOTIATION 100 + +/* The ciphers that are supported */ +#define SSL_AES128_SHA 0x2f +#define SSL_AES256_SHA 0x35 +#define SSL_RC4_128_SHA 0x05 +#define SSL_RC4_128_MD5 0x04 + +/* build mode ids' */ +#define SSL_BUILD_SKELETON_MODE 0x01 +#define SSL_BUILD_SERVER_ONLY 0x02 +#define SSL_BUILD_ENABLE_VERIFICATION 0x03 +#define SSL_BUILD_ENABLE_CLIENT 0x04 +#define SSL_BUILD_FULL_MODE 0x05 + +/* offsets to retrieve configuration information */ +#define SSL_BUILD_MODE 0 +#define SSL_MAX_CERT_CFG_OFFSET 1 +#define SSL_MAX_CA_CERT_CFG_OFFSET 2 +#define SSL_HAS_PEM 3 + +/* default session sizes */ +#define SSL_DEFAULT_SVR_SESS 5 +#define SSL_DEFAULT_CLNT_SESS 1 + +/* X.509/X.520 distinguished name types */ +#define SSL_X509_CERT_COMMON_NAME 0 +#define SSL_X509_CERT_ORGANIZATION 1 +#define SSL_X509_CERT_ORGANIZATIONAL_NAME 2 +#define SSL_X509_CA_CERT_COMMON_NAME 3 +#define SSL_X509_CA_CERT_ORGANIZATION 4 +#define SSL_X509_CA_CERT_ORGANIZATIONAL_NAME 5 + +/* SSL object loader types */ +#define SSL_OBJ_X509_CERT 1 +#define SSL_OBJ_X509_CACERT 2 +#define SSL_OBJ_RSA_KEY 3 +#define SSL_OBJ_PKCS8 4 +#define SSL_OBJ_PKCS12 5 + +/** + * @defgroup c_api Standard C API + * @brief The standard interface in C. + * @{ + */ + +/** + * @brief Establish a new client/server context. + * + * This function is called before any client/server SSL connections are made. + * + * Each new connection will use the this context's private key and + * certificate chain. If a different certificate chain is required, then a + * different context needs to be be used. + * + * There are two threading models supported - a single thread with one + * SSL_CTX can support any number of SSL connections - and multiple threads can + * support one SSL_CTX object each (the default). But if a single SSL_CTX + * object uses many SSL objects in individual threads, then the + * CONFIG_SSL_CTX_MUTEXING option needs to be configured. + * + * @param options [in] Any particular options. At present the options + * supported are: + * - SSL_SERVER_VERIFY_LATER (client only): Don't stop a handshake if the server + * authentication fails. The certificate can be authenticated later with a + * call to ssl_verify_cert(). + * - SSL_CLIENT_AUTHENTICATION (server only): Enforce client authentication + * i.e. each handshake will include a "certificate request" message from the + * server. Only available if verification has been enabled. + * - SSL_DISPLAY_BYTES (full mode build only): Display the byte sequences + * during the handshake. + * - SSL_DISPLAY_STATES (full mode build only): Display the state changes + * during the handshake. + * - SSL_DISPLAY_CERTS (full mode build only): Display the certificates that + * are passed during a handshake. + * - SSL_DISPLAY_RSA (full mode build only): Display the RSA key details that + * are passed during a handshake. + * - SSL_CONNECT_IN_PARTS (client only): To use a non-blocking version of + * ssl_client_new(). + * @param num_sessions [in] The number of sessions to be used for session + * caching. If this value is 0, then there is no session caching. This option + * is not used in skeleton mode. + * @return A client/server context. + */ +EXP_FUNC SSL_CTX * STDCALL ssl_ctx_new(uint32_t options, int num_sessions); + +/** + * @brief Remove a client/server context. + * + * Frees any used resources used by this context. Each connection will be + * sent a "Close Notify" alert (if possible). + * @param ssl_ctx [in] The client/server context. + */ +EXP_FUNC void STDCALL ssl_ctx_free(SSL_CTX *ssl_ctx); + +/** + * @brief (server only) Establish a new SSL connection to an SSL client. + * + * It is up to the application to establish the logical connection (whether it + * is a socket, serial connection etc). + * @param ssl_ctx [in] The server context. + * @param client_fd [in] The client's file descriptor. + * @return An SSL object reference. + */ +EXP_FUNC SSL * STDCALL ssl_server_new(SSL_CTX *ssl_ctx, int client_fd); + +/** + * @brief (client only) Establish a new SSL connection to an SSL server. + * + * It is up to the application to establish the initial logical connection + * (whether it is a socket, serial connection etc). + * + * This is a normally a blocking call - it will finish when the handshake is + * complete (or has failed). To use in non-blocking mode, set + * SSL_CONNECT_IN_PARTS in ssl_ctx_new(). + * @param ssl_ctx [in] The client context. + * @param client_fd [in] The client's file descriptor. + * @param session_id [in] A 32 byte session id for session resumption. This + * can be null if no session resumption is being used or required. This option + * is not used in skeleton mode. + * @param sess_id_size The size of the session id (max 32) + * @return An SSL object reference. Use ssl_handshake_status() to check + * if a handshake succeeded. + */ +EXP_FUNC SSL * STDCALL ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const uint8_t *session_id, uint8_t sess_id_size); + +/** + * @brief Free any used resources on this connection. + + * A "Close Notify" message is sent on this connection (if possible). It is up + * to the application to close the socket or file descriptor. + * @param ssl [in] The ssl object reference. + */ +EXP_FUNC void STDCALL ssl_free(SSL *ssl); + +/** + * @brief Read the SSL data stream. + * If the socket is non-blocking and data is blocked then SSO_OK will be + * returned. + * @param ssl [in] An SSL object reference. + * @param in_data [out] If the read was successful, a pointer to the read + * buffer will be here. Do NOT ever free this memory as this buffer is used in + * sucessive calls. If the call was unsuccessful, this value will be null. + * @return The number of decrypted bytes: + * - if > 0, then the handshaking is complete and we are returning the number + * of decrypted bytes. + * - SSL_OK if the handshaking stage is successful (but not yet complete). + * - < 0 if an error. + * @see ssl.h for the error code list. + * @note Use in_data before doing any successive ssl calls. + */ +EXP_FUNC int STDCALL ssl_read(SSL *ssl, uint8_t **in_data); + +/* Synchronous internal version of above used for protocol negotiation */ +EXP_FUNC int STDCALL ssl_readi(SSL *ssl, uint8_t **in_data); + +/** + * @brief Write to the SSL data stream. + * if the socket is non-blocking and data is blocked then a check is made + * to ensure that all data is sent (i.e. blocked mode is forced). + * @param ssl [in] An SSL obect reference. + * @param out_data [in] The data to be written + * @param out_len [in] The number of bytes to be written. + * @return The number of bytes sent, or if < 0 if an error. + * @see ssl.h for the error code list. + */ +EXP_FUNC int STDCALL ssl_write(SSL *ssl, const uint8_t *out_data, int out_len); + +/** + * @brief Find an ssl object based on a file descriptor. + * + * Goes through the list of SSL objects maintained in a client/server context + * to look for a file descriptor match. + * @param ssl_ctx [in] The client/server context. + * @param client_fd [in] The file descriptor. + * @return A reference to the SSL object. Returns null if the object could not + * be found. + */ +EXP_FUNC SSL * STDCALL ssl_find(SSL_CTX *ssl_ctx, int client_fd); + +/** + * @brief Get the session id for a handshake. + * + * This will be a 32 byte sequence and is available after the first + * handshaking messages are sent. + * @param ssl [in] An SSL object reference. + * @return The session id as a 32 byte sequence. + * @note A SSLv23 handshake may have only 16 valid bytes. + */ +EXP_FUNC const uint8_t * STDCALL ssl_get_session_id(const SSL *ssl); + +/** + * @brief Get the session id size for a handshake. + * + * This will normally be 32 but could be 0 (no session id) or something else. + * @param ssl [in] An SSL object reference. + * @return The size of the session id. + */ +EXP_FUNC uint8_t STDCALL ssl_get_session_id_size(const SSL *ssl); + +/** + * @brief Return the cipher id (in the SSL form). + * @param ssl [in] An SSL object reference. + * @return The cipher id. This will be one of the following: + * - SSL_AES128_SHA (0x2f) + * - SSL_AES256_SHA (0x35) + * - SSL_RC4_128_SHA (0x05) + * - SSL_RC4_128_MD5 (0x04) + */ +EXP_FUNC uint8_t STDCALL ssl_get_cipher_id(const SSL *ssl); + +/** + * @brief Return the status of the handshake. + * @param ssl [in] An SSL object reference. + * @return SSL_OK if the handshake is complete and ok. + * @see ssl.h for the error code list. + */ +EXP_FUNC int STDCALL ssl_handshake_status(const SSL *ssl); + +/** + * @brief Retrieve various parameters about the axTLS engine. + * @param offset [in] The configuration offset. It will be one of the following: + * - SSL_BUILD_MODE The build mode. This will be one of the following: + * - SSL_BUILD_SERVER_ONLY (basic server mode) + * - SSL_BUILD_ENABLE_VERIFICATION (server can do client authentication) + * - SSL_BUILD_ENABLE_CLIENT (client/server capabilties) + * - SSL_BUILD_FULL_MODE (client/server with diagnostics) + * - SSL_BUILD_SKELETON_MODE (skeleton mode) + * - SSL_MAX_CERT_CFG_OFFSET The maximum number of certificates allowed. + * - SSL_MAX_CA_CERT_CFG_OFFSET The maximum number of CA certificates allowed. + * - SSL_HAS_PEM 1 if supported + * @return The value of the requested parameter. + */ +EXP_FUNC int STDCALL ssl_get_config(int offset); + +/** + * @brief Return a string explaining why the handshake failed. + * + * This call is can be used by diagnostics elsewhere. + * @param error_code [in] An error code. + * @return The appropriate string, which may be static. + * @see ssl.h for the error code list. + */ +EXP_FUNC const char * STDCALL ssl_error_string(int error_code); + +/** + * @brief Display why the handshake failed. + * + * This call is only useful in a 'full mode' build. The output is to stdout. + * @param error_code [in] An error code. + * @see ssl.h for the error code list. + */ +EXP_FUNC void STDCALL ssl_display_error(int error_code); + +/** + * @brief Authenticate a received certificate. + * + * This call is usually made by a client after a handshake is complete and the + * context is in SSL_SERVER_VERIFY_LATER mode. + * @param ssl [in] An SSL object reference. + * @return SSL_OK if the certificate is verified. + */ +EXP_FUNC int STDCALL ssl_verify_cert(const SSL *ssl); + +/** + * @brief Retrieve an X.509 distinguished name component. + * + * When a handshake is complete and a certificate has been exchanged, then the + * details of the remote certificate can be retrieved. + * + * This will usually be used by a client to check that the server's common + * name matches the URL. + * + * @param ssl [in] An SSL object reference. + * @param component [in] one of: + * - SSL_X509_CERT_COMMON_NAME + * - SSL_X509_CERT_ORGANIZATION + * - SSL_X509_CERT_ORGANIZATIONAL_NAME + * - SSL_X509_CA_CERT_COMMON_NAME + * - SSL_X509_CA_CERT_ORGANIZATION + * - SSL_X509_CA_CERT_ORGANIZATIONAL_NAME + * @return The appropriate string (or null if not defined) + * @note Verification build mode must be enabled. + */ +EXP_FUNC const char * STDCALL ssl_get_cert_dn(const SSL *ssl, int component); + +/** + * @brief Retrieve a Subject Alternative DNSName + * + * When a handshake is complete and a certificate has been exchanged, then the + * details of the remote certificate can be retrieved. + * + * This will usually be used by a client to check that the server's DNS + * name matches the URL. + * + * @param ssl [in] An SSL object reference. + * @param dnsindex [in] The index of the DNS name to retrieve. + * @return The appropriate string (or null if not defined) + * @note Verification build mode must be enabled. + */ +EXP_FUNC const char * STDCALL ssl_get_cert_subject_alt_dnsname(const SSL *ssl, int dnsindex); + +/** + * @brief Force the client to perform its handshake again. + * + * For a client this involves sending another "client hello" message. + * For the server is means sending a "hello request" message. + * + * This is a blocking call on the client (until the handshake completes). + * + * @param ssl [in] An SSL object reference. + * @return SSL_OK if renegotiation instantiation was ok + */ +EXP_FUNC int STDCALL ssl_renegotiate(SSL *ssl); + +/** + * @brief Process a file that is in binary DER or ASCII PEM format. + * + * These are temporary objects that are used to load private keys, + * certificates etc into memory. + * @param ssl_ctx [in] The client/server context. + * @param obj_type [in] The format of the file. Can be one of: + * - SSL_OBJ_X509_CERT (no password required) + * - SSL_OBJ_X509_CACERT (no password required) + * - SSL_OBJ_RSA_KEY (AES128/AES256 PEM encryption supported) + * - SSL_OBJ_PKCS8 (RC4-128 encrypted data supported) + * - SSL_OBJ_PKCS12 (RC4-128 encrypted data supported) + * + * PEM files are automatically detected (if supported). The object type is + * also detected, and so is not relevant for these types of files. + * @param filename [in] The location of a file in DER/PEM format. + * @param password [in] The password used. Can be null if not required. + * @return SSL_OK if all ok + * @note Not available in skeleton build mode. + */ +EXP_FUNC int STDCALL ssl_obj_load(SSL_CTX *ssl_ctx, int obj_type, const char *filename, const char *password); + +/** + * @brief Process binary data. + * + * These are temporary objects that are used to load private keys, + * certificates etc into memory. + * @param ssl_ctx [in] The client/server context. + * @param obj_type [in] The format of the memory data. + * @param data [in] The binary data to be loaded. + * @param len [in] The amount of data to be loaded. + * @param password [in] The password used. Can be null if not required. + * @return SSL_OK if all ok + * @see ssl_obj_load for more details on obj_type. + */ +EXP_FUNC int STDCALL ssl_obj_memory_load(SSL_CTX *ssl_ctx, int obj_type, const uint8_t *data, int len, const char *password); + +#ifdef CONFIG_SSL_GENERATE_X509_CERT +/** + * @brief Create an X.509 certificate. + * + * This certificate is a self-signed v1 cert with a fixed start/stop validity + * times. It is signed with an internal private key in ssl_ctx. + * + * @param ssl_ctx [in] The client/server context. + * @param options [in] Not used yet. + * @param dn [in] An array of distinguished name strings. The array is defined + * by: + * - SSL_X509_CERT_COMMON_NAME (0) + * - If SSL_X509_CERT_COMMON_NAME is empty or not defined, then the + * hostname will be used. + * - SSL_X509_CERT_ORGANIZATION (1) + * - If SSL_X509_CERT_ORGANIZATION is empty or not defined, then $USERNAME + * will be used. + * - SSL_X509_CERT_ORGANIZATIONAL_NAME (2) + * - SSL_X509_CERT_ORGANIZATIONAL_NAME is optional. + * @param cert_data [out] The certificate as a sequence of bytes. + * @return < 0 if an error, or the size of the certificate in bytes. + * @note cert_data must be freed when there is no more need for it. + */ +EXP_FUNC int STDCALL ssl_x509_create(SSL_CTX *ssl_ctx, uint32_t options, const char * dn[], uint8_t **cert_data); +#endif + +/** + * @brief Return the axTLS library version as a string. + */ +EXP_FUNC const char * STDCALL ssl_version(void); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ccast/axTLS/temp b/ccast/axTLS/temp new file mode 100644 index 0000000..859e8be --- /dev/null +++ b/ccast/axTLS/temp @@ -0,0 +1,2 @@ +// ~~~~9999 +printf("~1 SOCKET_READ to off %d returned len %d for req %d to 0x%x\n",ssl->bm_read_index, read_len, ssl->need_bytes-ssl->got_bytes,&buf[ssl->bm_read_index]); diff --git a/ccast/axTLS/tls1.c b/ccast/axTLS/tls1.c new file mode 100644 index 0000000..b635263 --- /dev/null +++ b/ccast/axTLS/tls1.c @@ -0,0 +1,2411 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * Common ssl/tlsv1 code to both the client and server implementations. + */ + +#include +#include +#include +#include +#include "os_port.h" +#include "crypto_misc.h" +#include "ssl.h" + +/* The session expiry time */ +#define SSL_EXPIRY_TIME (CONFIG_SSL_EXPIRY_TIME*3600) + +static const uint8_t g_hello_request[] = { HS_HELLO_REQUEST, 0, 0, 0 }; +static const uint8_t g_chg_cipher_spec_pkt[] = { 1 }; +static const char * server_finished = "server finished"; +static const char * client_finished = "client finished"; + +static int do_handshake(SSL *ssl, uint8_t *buf, int read_len); +static int set_key_block(SSL *ssl, int is_write); +static int verify_digest(SSL *ssl, int mode, uint8_t * hmac_header, + const uint8_t *buf, int read_len); +static void *crypt_new(SSL *ssl, uint8_t *key, uint8_t *iv, int is_decrypt); +static int send_raw_packet(SSL *ssl, uint8_t protocol); + +/** + * The server will pick the cipher based on the order that the order that the + * ciphers are listed. This order is defined at compile time. + */ +#ifdef CONFIG_SSL_SKELETON_MODE +const uint8_t ssl_prot_prefs[NUM_PROTOCOLS] = +{ SSL_RC4_128_SHA }; +#else +static void session_free(SSL_SESSION *ssl_sessions[], int sess_index); + +const uint8_t ssl_prot_prefs[NUM_PROTOCOLS] = +#ifdef CONFIG_SSL_PROT_LOW /* low security, fast speed */ +{ SSL_RC4_128_SHA, SSL_AES128_SHA, SSL_AES256_SHA, SSL_RC4_128_MD5 }; +#elif defined(CONFIG_SSL_PROT_MEDIUM) /* medium security, medium speed */ +{ SSL_AES128_SHA, SSL_AES256_SHA, SSL_RC4_128_SHA, SSL_RC4_128_MD5 }; +#else /* CONFIG_SSL_PROT_HIGH */ /* high security, low speed */ +{ SSL_AES256_SHA, SSL_AES128_SHA, SSL_RC4_128_SHA, SSL_RC4_128_MD5 }; +#endif +#endif /* CONFIG_SSL_SKELETON_MODE */ + +/** + * The cipher map containing all the essentials for each cipher. + */ +#ifdef CONFIG_SSL_SKELETON_MODE +static const cipher_info_t cipher_info[NUM_PROTOCOLS] = +{ + { /* RC4-SHA */ + SSL_RC4_128_SHA, /* RC4-SHA */ + 16, /* key size */ + 0, /* iv size */ + 2*(SHA1_SIZE+16), /* key block size */ + 0, /* no padding */ + SHA1_SIZE, /* digest size */ + hmac_sha1, /* hmac algorithm */ + (crypt_func)RC4_crypt, /* encrypt */ + (crypt_func)RC4_crypt /* decrypt */ + }, +}; +#else +static const cipher_info_t cipher_info[NUM_PROTOCOLS] = +{ + { /* AES128-SHA */ + SSL_AES128_SHA, /* AES128-SHA */ + 16, /* key size */ + 16, /* iv size */ + 2*(SHA1_SIZE+16+16), /* key block size */ + 16, /* block padding size */ + SHA1_SIZE, /* digest size */ + hmac_sha1, /* hmac algorithm */ + (crypt_func)AES_cbc_encrypt, /* encrypt */ + (crypt_func)AES_cbc_decrypt /* decrypt */ + }, + { /* AES256-SHA */ + SSL_AES256_SHA, /* AES256-SHA */ + 32, /* key size */ + 16, /* iv size */ + 2*(SHA1_SIZE+32+16), /* key block size */ + 16, /* block padding size */ + SHA1_SIZE, /* digest size */ + hmac_sha1, /* hmac algorithm */ + (crypt_func)AES_cbc_encrypt, /* encrypt */ + (crypt_func)AES_cbc_decrypt /* decrypt */ + }, + { /* RC4-SHA */ + SSL_RC4_128_SHA, /* RC4-SHA */ + 16, /* key size */ + 0, /* iv size */ + 2*(SHA1_SIZE+16), /* key block size */ + 0, /* no padding */ + SHA1_SIZE, /* digest size */ + hmac_sha1, /* hmac algorithm */ + (crypt_func)RC4_crypt, /* encrypt */ + (crypt_func)RC4_crypt /* decrypt */ + }, + /* + * This protocol is from SSLv2 days and is unlikely to be used - but was + * useful for testing different possible digest algorithms. + */ + { /* RC4-MD5 */ + SSL_RC4_128_MD5, /* RC4-MD5 */ + 16, /* key size */ + 0, /* iv size */ + 2*(MD5_SIZE+16), /* key block size */ + 0, /* no padding */ + MD5_SIZE, /* digest size */ + hmac_md5, /* hmac algorithm */ + (crypt_func)RC4_crypt, /* encrypt */ + (crypt_func)RC4_crypt /* decrypt */ + }, +}; +#endif + +static void prf(const uint8_t *sec, int sec_len, uint8_t *seed, int seed_len, + uint8_t *out, int olen); +static const cipher_info_t *get_cipher_info(uint8_t cipher); +static void increment_read_sequence(SSL *ssl); +static void increment_write_sequence(SSL *ssl); +static void add_hmac_digest(SSL *ssl, int snd, uint8_t *hmac_header, + const uint8_t *buf, int buf_len, uint8_t *hmac_buf); + +/* win32 VC6.0 doesn't have variadic macros */ +#if defined(WIN32) && !defined(CONFIG_SSL_FULL_MODE) +void DISPLAY_BYTES(SSL *ssl, const char *format, + const uint8_t *data, int size, ...) {} +#endif + +/** + * Establish a new client/server context. + */ +EXP_FUNC SSL_CTX *STDCALL ssl_ctx_new(uint32_t options, int num_sessions) +{ + SSL_CTX *ssl_ctx = (SSL_CTX *)calloc(1, sizeof (SSL_CTX)); + ssl_ctx->options = options; + RNG_initialize(); + + if (load_key_certs(ssl_ctx) < 0) + { + free(ssl_ctx); /* can't load our key/certificate pair, so die */ + return NULL; + } + +#ifndef CONFIG_SSL_SKELETON_MODE + ssl_ctx->num_sessions = num_sessions; +#endif + + SSL_CTX_MUTEX_INIT(ssl_ctx->mutex); + +#ifndef CONFIG_SSL_SKELETON_MODE + if (num_sessions) + { + ssl_ctx->ssl_sessions = (SSL_SESSION **) + calloc(1, num_sessions*sizeof(SSL_SESSION *)); + } +#endif + + return ssl_ctx; +} + +/* + * Remove a client/server context. + */ +EXP_FUNC void STDCALL ssl_ctx_free(SSL_CTX *ssl_ctx) +{ + SSL *ssl; + int i; + + if (ssl_ctx == NULL) + return; + + ssl = ssl_ctx->head; + + /* clear out all the ssl entries */ + while (ssl) + { + SSL *next = ssl->next; + ssl_free(ssl); + ssl = next; + } + +#ifndef CONFIG_SSL_SKELETON_MODE + /* clear out all the sessions */ + for (i = 0; i < ssl_ctx->num_sessions; i++) + session_free(ssl_ctx->ssl_sessions, i); + + free(ssl_ctx->ssl_sessions); +#endif + + i = 0; + while (i < CONFIG_SSL_MAX_CERTS && ssl_ctx->certs[i].buf) + { + free(ssl_ctx->certs[i].buf); + ssl_ctx->certs[i++].buf = NULL; + } + +#ifdef CONFIG_SSL_CERT_VERIFICATION + remove_ca_certs(ssl_ctx->ca_cert_ctx); +#endif + ssl_ctx->chain_length = 0; + SSL_CTX_MUTEX_DESTROY(ssl_ctx->mutex); + RSA_free(ssl_ctx->rsa_ctx); + RNG_terminate(); + free(ssl_ctx); +} + +/* + * Free any used resources used by this connection. + */ +EXP_FUNC void STDCALL ssl_free(SSL *ssl) +{ + SSL_CTX *ssl_ctx; + + if (ssl == NULL) /* just ignore null pointers */ + return; + + /* only notify if we weren't notified first */ + /* spec says we must notify when we are dying */ + if (!IS_SET_SSL_FLAG(SSL_SENT_CLOSE_NOTIFY)) + send_alert(ssl, SSL_ALERT_CLOSE_NOTIFY); + + ssl_ctx = ssl->ssl_ctx; + + SSL_CTX_LOCK(ssl_ctx->mutex); + + /* adjust the server SSL list */ + if (ssl->prev) + ssl->prev->next = ssl->next; + else + ssl_ctx->head = ssl->next; + + if (ssl->next) + ssl->next->prev = ssl->prev; + else + ssl_ctx->tail = ssl->prev; + + SSL_CTX_UNLOCK(ssl_ctx->mutex); + + /* may already be free - but be sure */ + free(ssl->encrypt_ctx); + free(ssl->decrypt_ctx); + disposable_free(ssl); +#ifdef CONFIG_SSL_CERT_VERIFICATION + x509_free(ssl->x509_ctx); +#endif + + free(ssl); +} + +/* + * Client read the SSL connection and send any alerts for various errors. + * (Not used for protocol negotiation, but can be called asynchronously + * to ssl_write()) + */ +EXP_FUNC int STDCALL ssl_read(SSL *ssl, uint8_t **in_data) +{ + int ret; + ret = basic_read(ssl, in_data); + + /* check for return code so we can send an alert */ + if (ret < SSL_OK && ret != SSL_CLOSE_NOTIFY + && ret != SSL_TIMEDOUT) + { + if (ret != SSL_ERROR_CONN_LOST) + { + send_alert(ssl, ret); +#ifndef CONFIG_SSL_SKELETON_MODE + /* something nasty happened, so get rid of this session */ + kill_ssl_session(ssl->ssl_ctx->ssl_sessions, ssl); +#endif + } + } + + return ret; +} + +/* + * Internal read the SSL connection used for setup + */ +EXP_FUNC int STDCALL ssl_readi(SSL *ssl, uint8_t **in_data) +{ + int ret; + ret = basic_readi(ssl, in_data); + + /* check for return code so we can send an alert */ + if (ret < SSL_OK && ret != SSL_CLOSE_NOTIFY + && ret != SSL_TIMEDOUT) + { + if (ret != SSL_ERROR_CONN_LOST) + { + send_alert(ssl, ret); +#ifndef CONFIG_SSL_SKELETON_MODE + /* something nasty happened, so get rid of this session */ + kill_ssl_session(ssl->ssl_ctx->ssl_sessions, ssl); +#endif + } + } + + return ret; +} + +/* + * Write application data to the client + */ +EXP_FUNC int STDCALL ssl_write(SSL *ssl, const uint8_t *out_data, int out_len) +{ + int n = out_len, nw, i, tot = 0; + + /* maximum size of a TLS packet is around 16kB, so fragment */ + do + { + nw = n; + + if (nw > RT_MAX_PLAIN_LENGTH) /* fragment if necessary */ + nw = RT_MAX_PLAIN_LENGTH; + + if ((i = send_packet(ssl, PT_APP_PROTOCOL_DATA, + &out_data[tot], nw)) <= 0) + { + out_len = i; /* an error */ + break; + } + + tot += i; + n -= i; + } while (n > 0); + + return out_len; +} + +/** + * Add a certificate to the certificate chain. + */ +int add_cert(SSL_CTX *ssl_ctx, const uint8_t *buf, int len) +{ + int ret = SSL_ERROR_NO_CERT_DEFINED, i = 0; + SSL_CERT *ssl_cert; + X509_CTX *cert = NULL; + int offset; + + while (ssl_ctx->certs[i].buf && i < CONFIG_SSL_MAX_CERTS) + i++; + + if (i == CONFIG_SSL_MAX_CERTS) /* too many certs */ + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: maximum number of certs added (%d) - change of " + "compile-time configuration required\n", + CONFIG_SSL_MAX_CERTS); +#endif + goto error; + } + + if ((ret = x509_new(buf, &offset, &cert))) + goto error; + +#if defined (CONFIG_SSL_FULL_MODE) + if (ssl_ctx->options & SSL_DISPLAY_CERTS) + x509_print(cert, NULL); +#endif + + ssl_cert = &ssl_ctx->certs[i]; + ssl_cert->size = len; + ssl_cert->buf = (uint8_t *)malloc(len); + memcpy(ssl_cert->buf, buf, len); + ssl_ctx->chain_length++; + len -= offset; + ret = SSL_OK; /* ok so far */ + + /* recurse? */ + if (len > 0) + { + ret = add_cert(ssl_ctx, &buf[offset], len); + } + +error: + x509_free(cert); /* don't need anymore */ + return ret; +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Add a certificate authority. + */ +int add_cert_auth(SSL_CTX *ssl_ctx, const uint8_t *buf, int len) +{ + int ret = SSL_OK; /* ignore errors for now */ + int i = 0; + CA_CERT_CTX *ca_cert_ctx; + + if (ssl_ctx->ca_cert_ctx == NULL) + ssl_ctx->ca_cert_ctx = (CA_CERT_CTX *)calloc(1, sizeof(CA_CERT_CTX)); + + ca_cert_ctx = ssl_ctx->ca_cert_ctx; + + while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) + i++; + + while (len > 0) + { + int offset; + if (i >= CONFIG_X509_MAX_CA_CERTS) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: maximum number of CA certs added (%d) - change of " + "compile-time configuration required\n", + CONFIG_X509_MAX_CA_CERTS); +#endif + break; + } + + + /* ignore the return code */ + if (x509_new(buf, &offset, &ca_cert_ctx->cert[i]) == X509_OK) + { +#if defined (CONFIG_SSL_FULL_MODE) + if (ssl_ctx->options & SSL_DISPLAY_CERTS) + x509_print(ca_cert_ctx->cert[i], NULL); +#endif + } + + i++; + len -= offset; + } + + return ret; +} + +/* + * Retrieve an X.509 distinguished name component + */ +EXP_FUNC const char * STDCALL ssl_get_cert_dn(const SSL *ssl, int component) +{ + if (ssl->x509_ctx == NULL) + return NULL; + + switch (component) + { + case SSL_X509_CERT_COMMON_NAME: + return ssl->x509_ctx->cert_dn[X509_COMMON_NAME]; + + case SSL_X509_CERT_ORGANIZATION: + return ssl->x509_ctx->cert_dn[X509_ORGANIZATION]; + + case SSL_X509_CERT_ORGANIZATIONAL_NAME: + return ssl->x509_ctx->cert_dn[X509_ORGANIZATIONAL_UNIT]; + + case SSL_X509_CA_CERT_COMMON_NAME: + return ssl->x509_ctx->ca_cert_dn[X509_COMMON_NAME]; + + case SSL_X509_CA_CERT_ORGANIZATION: + return ssl->x509_ctx->ca_cert_dn[X509_ORGANIZATION]; + + case SSL_X509_CA_CERT_ORGANIZATIONAL_NAME: + return ssl->x509_ctx->ca_cert_dn[X509_ORGANIZATIONAL_UNIT]; + + default: + return NULL; + } +} + +/* + * Retrieve a "Subject Alternative Name" from a v3 certificate + */ +EXP_FUNC const char * STDCALL ssl_get_cert_subject_alt_dnsname(const SSL *ssl, + int dnsindex) +{ + int i; + + if (ssl->x509_ctx == NULL || ssl->x509_ctx->subject_alt_dnsnames == NULL) + return NULL; + + for (i = 0; i < dnsindex; ++i) + { + if (ssl->x509_ctx->subject_alt_dnsnames[i] == NULL) + return NULL; + } + + return ssl->x509_ctx->subject_alt_dnsnames[dnsindex]; +} + +#endif /* CONFIG_SSL_CERT_VERIFICATION */ + +/* + * Find an ssl object based on the client's file descriptor. + */ +EXP_FUNC SSL * STDCALL ssl_find(SSL_CTX *ssl_ctx, int client_fd) +{ + SSL *ssl; + + SSL_CTX_LOCK(ssl_ctx->mutex); + ssl = ssl_ctx->head; + + /* search through all the ssl entries */ + while (ssl) + { + if (ssl->client_fd == client_fd) + { + SSL_CTX_UNLOCK(ssl_ctx->mutex); + return ssl; + } + + ssl = ssl->next; + } + + SSL_CTX_UNLOCK(ssl_ctx->mutex); + return NULL; +} + +/* + * Force the client to perform its handshake again. + */ +EXP_FUNC int STDCALL ssl_renegotiate(SSL *ssl) +{ + int ret = SSL_OK; + + disposable_new(ssl); +#ifdef CONFIG_SSL_ENABLE_CLIENT + if (IS_SET_SSL_FLAG(SSL_IS_CLIENT)) + { + ret = do_client_connect(ssl); + } + else +#endif + { + send_packet(ssl, PT_HANDSHAKE_PROTOCOL, + g_hello_request, sizeof(g_hello_request)); + SET_SSL_FLAG(SSL_NEED_RECORD); + ssl->need_record = 1; + } + + return ret; +} + +/** + * @brief Get what we need for key info. + * @param cipher [in] The cipher information we are after + * @param key_size [out] The key size for the cipher + * @param iv_size [out] The iv size for the cipher + * @return The amount of key information we need. + */ +static const cipher_info_t *get_cipher_info(uint8_t cipher) +{ + int i; + + for (i = 0; i < NUM_PROTOCOLS; i++) + { + if (cipher_info[i].cipher == cipher) + { + return &cipher_info[i]; + } + } + + return NULL; /* error */ +} + +/* + * Get a new ssl context for a new connection. + */ +SSL *ssl_new(SSL_CTX *ssl_ctx, int client_fd) +{ + SSL *ssl = (SSL *)calloc(1, sizeof(SSL)); + ssl->ssl_ctx = ssl_ctx; + ssl->client_fd = client_fd; + ssl->need_bytes = SSL_RECORD_SIZE; /* need a record */ + ssl->flag = SSL_NEED_RECORD; + ssl->bm_data = ssl->bm_all_data+BM_RECORD_OFFSET; /* space at the start */ + ssl->rneed_bytes = SSL_RECORD_SIZE; /* need a record */ + ssl->need_record = 1; + ssl->bm_rdata = ssl->bm_read_data+BM_RECORD_OFFSET; /* space at the start */ + ssl->hs_status = SSL_NOT_OK; /* not connected */ +#ifdef CONFIG_ENABLE_VERIFICATION + ssl->ca_cert_ctx = ssl_ctx->ca_cert_ctx; +#endif + disposable_new(ssl); + + /* a bit hacky but saves a few bytes of memory */ + ssl->flag |= ssl_ctx->options; + SSL_CTX_LOCK(ssl_ctx->mutex); + + if (ssl_ctx->head == NULL) + { + ssl_ctx->head = ssl; + ssl_ctx->tail = ssl; + } + else + { + ssl->prev = ssl_ctx->tail; + ssl_ctx->tail->next = ssl; + ssl_ctx->tail = ssl; + } + + SSL_CTX_UNLOCK(ssl_ctx->mutex); + return ssl; +} + +/* + * Add a private key to a context. + */ +int add_private_key(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj) +{ + int ret = SSL_OK; + + /* get the private key details */ + if (asn1_get_private_key(ssl_obj->buf, ssl_obj->len, &ssl_ctx->rsa_ctx)) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + +error: + return ret; +} + +/** + * Increment the read sequence number (as a 64 bit endian indepenent #) + */ +static void increment_read_sequence(SSL *ssl) +{ + int i; + + for (i = 7; i >= 0; i--) + { + if (++ssl->read_sequence[i]) + break; + } +} + +/** + * Increment the read sequence number (as a 64 bit endian indepenent #) + */ +static void increment_write_sequence(SSL *ssl) +{ + int i; + + for (i = 7; i >= 0; i--) + { + if (++ssl->write_sequence[i]) + break; + } +} + +/** + * Work out the HMAC digest in a packet. + */ +static void add_hmac_digest(SSL *ssl, int mode, uint8_t *hmac_header, + const uint8_t *buf, int buf_len, uint8_t *hmac_buf) +{ + int hmac_len = buf_len + 8 + SSL_RECORD_SIZE; + uint8_t *t_buf = (uint8_t *)malloc(hmac_len+10); + + memcpy(t_buf, (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_WRITE) ? + ssl->write_sequence : ssl->read_sequence, 8); + memcpy(&t_buf[8], hmac_header, SSL_RECORD_SIZE); + memcpy(&t_buf[8+SSL_RECORD_SIZE], buf, buf_len); + + ssl->cipher_info->hmac(t_buf, hmac_len, + (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_READ) ? + ssl->server_mac : ssl->client_mac, + ssl->cipher_info->digest_size, hmac_buf); + + free(t_buf); + +#if 0 + print_blob("record", hmac_header, SSL_RECORD_SIZE); + print_blob("buf", buf, buf_len); + if (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_WRITE) + { + print_blob("write seq", ssl->write_sequence, 8); + } + else + { + print_blob("read seq", ssl->read_sequence, 8); + } + + if (mode == SSL_SERVER_WRITE || mode == SSL_CLIENT_READ) + { + print_blob("server mac", + ssl->server_mac, ssl->cipher_info->digest_size); + } + else + { + print_blob("client mac", + ssl->client_mac, ssl->cipher_info->digest_size); + } + print_blob("hmac", hmac_buf, SHA1_SIZE); +#endif +} + +/** + * Verify that the digest of a packet is correct. + */ +static int verify_digest(SSL *ssl, int mode, uint8_t *hmac_header, + const uint8_t *buf, int read_len) +{ + uint8_t hmac_buf[SHA1_SIZE]; + int hmac_offset; + + if (ssl->cipher_info->padding_size) + { + int last_blk_size = buf[read_len-1], i; + hmac_offset = read_len-last_blk_size-ssl->cipher_info->digest_size-1; + + /* guard against a timing attack - make sure we do the digest */ + if (hmac_offset < 0) + { + hmac_offset = 0; + } + else + { + /* already looked at last byte */ + for (i = 1; i < last_blk_size; i++) + { + if (buf[read_len-i] != last_blk_size) + { + hmac_offset = 0; + break; + } + } + } + } + else /* stream cipher */ + { + hmac_offset = read_len - ssl->cipher_info->digest_size; + + if (hmac_offset < 0) + { + hmac_offset = 0; + } + } + + /* sanity check the offset */ + hmac_header[3] = hmac_offset >> 8; /* insert size */ + hmac_header[4] = hmac_offset & 0xff; + add_hmac_digest(ssl, mode, hmac_header, buf, hmac_offset, hmac_buf); + + if (memcmp(hmac_buf, &buf[hmac_offset], ssl->cipher_info->digest_size)) + { + return SSL_ERROR_INVALID_HMAC; + } + + return hmac_offset; +} + +/** + * Add a packet to the end of our sent and received packets, so that we may use + * it to calculate the hash at the end. + */ +void add_packet(SSL *ssl, const uint8_t *pkt, int len) +{ + MD5_Update(&ssl->dc->md5_ctx, pkt, len); + SHA1_Update(&ssl->dc->sha1_ctx, pkt, len); +} + +/** + * Work out the MD5 PRF. + */ +static void p_hash_md5(const uint8_t *sec, int sec_len, + uint8_t *seed, int seed_len, uint8_t *out, int olen) +{ + uint8_t a1[128]; + + /* A(1) */ + hmac_md5(seed, seed_len, sec, sec_len, a1); + memcpy(&a1[MD5_SIZE], seed, seed_len); + hmac_md5(a1, MD5_SIZE+seed_len, sec, sec_len, out); + + while (olen > MD5_SIZE) + { + uint8_t a2[MD5_SIZE]; + out += MD5_SIZE; + olen -= MD5_SIZE; + + /* A(N) */ + hmac_md5(a1, MD5_SIZE, sec, sec_len, a2); + memcpy(a1, a2, MD5_SIZE); + + /* work out the actual hash */ + hmac_md5(a1, MD5_SIZE+seed_len, sec, sec_len, out); + } +} + +/** + * Work out the SHA1 PRF. + */ +static void p_hash_sha1(const uint8_t *sec, int sec_len, + uint8_t *seed, int seed_len, uint8_t *out, int olen) +{ + uint8_t a1[128]; + + /* A(1) */ + hmac_sha1(seed, seed_len, sec, sec_len, a1); + memcpy(&a1[SHA1_SIZE], seed, seed_len); + hmac_sha1(a1, SHA1_SIZE+seed_len, sec, sec_len, out); + + while (olen > SHA1_SIZE) + { + uint8_t a2[SHA1_SIZE]; + out += SHA1_SIZE; + olen -= SHA1_SIZE; + + /* A(N) */ + hmac_sha1(a1, SHA1_SIZE, sec, sec_len, a2); + memcpy(a1, a2, SHA1_SIZE); + + /* work out the actual hash */ + hmac_sha1(a1, SHA1_SIZE+seed_len, sec, sec_len, out); + } +} + +/** + * Work out the PRF. + */ +static void prf(const uint8_t *sec, int sec_len, uint8_t *seed, int seed_len, + uint8_t *out, int olen) +{ + int len, i; + const uint8_t *S1, *S2; + uint8_t xbuf[256]; /* needs to be > the amount of key data */ + uint8_t ybuf[256]; /* needs to be > the amount of key data */ + + len = sec_len/2; + S1 = sec; + S2 = &sec[len]; + len += (sec_len & 1); /* add for odd, make longer */ + + p_hash_md5(S1, len, seed, seed_len, xbuf, olen); + p_hash_sha1(S2, len, seed, seed_len, ybuf, olen); + + for (i = 0; i < olen; i++) + out[i] = xbuf[i] ^ ybuf[i]; +} + +/** + * Generate a master secret based on the client/server random data and the + * premaster secret. + */ +void generate_master_secret(SSL *ssl, const uint8_t *premaster_secret) +{ + uint8_t buf[128]; /* needs to be > 13+32+32 in size */ + strcpy((char *)buf, "master secret"); + memcpy(&buf[13], ssl->dc->client_random, SSL_RANDOM_SIZE); + memcpy(&buf[45], ssl->dc->server_random, SSL_RANDOM_SIZE); + prf(premaster_secret, SSL_SECRET_SIZE, buf, 77, ssl->dc->master_secret, + SSL_SECRET_SIZE); +} + +/** + * Generate a 'random' blob of data used for the generation of keys. + */ +static void generate_key_block(uint8_t *client_random, uint8_t *server_random, + uint8_t *master_secret, uint8_t *key_block, int key_block_size) +{ + uint8_t buf[128]; + strcpy((char *)buf, "key expansion"); + memcpy(&buf[13], server_random, SSL_RANDOM_SIZE); + memcpy(&buf[45], client_random, SSL_RANDOM_SIZE); + prf(master_secret, SSL_SECRET_SIZE, buf, 77, key_block, key_block_size); +} + +/** + * Calculate the digest used in the finished message. This function also + * doubles up as a certificate verify function. + */ +void finished_digest(SSL *ssl, const char *label, uint8_t *digest) +{ + uint8_t mac_buf[128]; + uint8_t *q = mac_buf; + MD5_CTX md5_ctx = ssl->dc->md5_ctx; + SHA1_CTX sha1_ctx = ssl->dc->sha1_ctx; + + if (label) + { + strcpy((char *)q, label); + q += strlen(label); + } + + MD5_Final(q, &md5_ctx); + q += MD5_SIZE; + + SHA1_Final(q, &sha1_ctx); + q += SHA1_SIZE; + + if (label) + { + prf(ssl->dc->master_secret, SSL_SECRET_SIZE, mac_buf, (int)(q-mac_buf), + digest, SSL_FINISHED_HASH_SIZE); + } + else /* for use in a certificate verify */ + { + memcpy(digest, mac_buf, MD5_SIZE + SHA1_SIZE); + } + +#if 0 + printf("label: %s\n", label); + print_blob("master secret", ssl->dc->master_secret, 48); + print_blob("mac_buf", mac_buf, q-mac_buf); + print_blob("finished digest", digest, SSL_FINISHED_HASH_SIZE); +#endif +} + +/** + * Retrieve (and initialise) the context of a cipher. + */ +static void *crypt_new(SSL *ssl, uint8_t *key, uint8_t *iv, int is_decrypt) +{ + switch (ssl->cipher) + { +#ifndef CONFIG_SSL_SKELETON_MODE + case SSL_AES128_SHA: + { + AES_CTX *aes_ctx = (AES_CTX *)malloc(sizeof(AES_CTX)); + AES_set_key(aes_ctx, key, iv, AES_MODE_128); + + if (is_decrypt) + { + AES_convert_key(aes_ctx); + } + + return (void *)aes_ctx; + } + + case SSL_AES256_SHA: + { + AES_CTX *aes_ctx = (AES_CTX *)malloc(sizeof(AES_CTX)); + AES_set_key(aes_ctx, key, iv, AES_MODE_256); + + if (is_decrypt) + { + AES_convert_key(aes_ctx); + } + + return (void *)aes_ctx; + } + + case SSL_RC4_128_MD5: +#endif + case SSL_RC4_128_SHA: + { + RC4_CTX *rc4_ctx = (RC4_CTX *)malloc(sizeof(RC4_CTX)); + RC4_setup(rc4_ctx, key, 16); + return (void *)rc4_ctx; + } + } + + return NULL; /* its all gone wrong */ +} + +/** + * Send a packet over the socket. + */ +static int send_raw_packet(SSL *ssl, uint8_t protocol) +{ + uint8_t *rec_buf = ssl->bm_all_data; + int pkt_size = SSL_RECORD_SIZE+ssl->bm_index; + int sent = 0; + int ret = SSL_OK; + + rec_buf[0] = protocol; + rec_buf[1] = 0x03; /* version = 3.1 or higher */ + rec_buf[2] = ssl->version & 0x0f; + rec_buf[3] = ssl->bm_index >> 8; + rec_buf[4] = ssl->bm_index & 0xff; + + DISPLAY_BYTES(ssl, "sending %d bytes", ssl->bm_all_data, + pkt_size, pkt_size); + + while (sent < pkt_size) + { + ret = SOCKET_WRITE(ssl->client_fd, + &ssl->bm_all_data[sent], pkt_size-sent); + if (ret >= 0) + sent += ret; + else + { +#ifdef WIN32 + if (GetLastError() == WSAETIMEDOUT) +#else + if (errno == EAGAIN) +#endif + return SSL_TIMEDOUT; + +#ifdef WIN32 + if (GetLastError() != WSAEWOULDBLOCK) +#else + if (errno != EWOULDBLOCK) +#endif + return SSL_ERROR_CONN_LOST; + } + + /* keep going until the write buffer has some space */ + if (sent != pkt_size) + { + fd_set wfds; + FD_ZERO(&wfds); + FD_SET(ssl->client_fd, &wfds); + + /* block and wait for it */ + if (select(ssl->client_fd + 1, NULL, &wfds, NULL, NULL) < 0) + return SSL_ERROR_CONN_LOST; + } + } + + SET_SSL_FLAG(SSL_NEED_RECORD); /* reset read for next time */ + ssl->bm_index = 0; + + if (protocol != PT_APP_PROTOCOL_DATA) + { + /* always return SSL_OK during handshake */ + ret = SSL_OK; + } + + return ret; +} + +/** + * Send an encrypted packet with padding bytes if necessary. + */ +int send_packet(SSL *ssl, uint8_t protocol, const uint8_t *in, int length) +{ + int ret, msg_length = 0; + + /* if our state is bad, don't bother */ + if (ssl->hs_status == SSL_ERROR_DEAD) + return SSL_ERROR_CONN_LOST; + + if (in) /* has the buffer already been initialised? */ + { + memcpy(ssl->bm_data, in, length); + } + + msg_length += length; + + if (IS_SET_SSL_FLAG(SSL_TX_ENCRYPTED)) + { + int mode = IS_SET_SSL_FLAG(SSL_IS_CLIENT) ? + SSL_CLIENT_WRITE : SSL_SERVER_WRITE; + uint8_t hmac_header[SSL_RECORD_SIZE] = + { + protocol, + 0x03, /* version = 3.1 or higher */ + ssl->version & 0x0f, + msg_length >> 8, + msg_length & 0xff + }; + + if (protocol == PT_HANDSHAKE_PROTOCOL) + { + DISPLAY_STATE(ssl, 1, ssl->bm_data[0], 0); + + if (ssl->bm_data[0] != HS_HELLO_REQUEST) + { + add_packet(ssl, ssl->bm_data, msg_length); + } + } + + /* add the packet digest */ + add_hmac_digest(ssl, mode, hmac_header, ssl->bm_data, msg_length, + &ssl->bm_data[msg_length]); + msg_length += ssl->cipher_info->digest_size; + + /* add padding? */ + if (ssl->cipher_info->padding_size) + { + int last_blk_size = msg_length%ssl->cipher_info->padding_size; + int pad_bytes = ssl->cipher_info->padding_size - last_blk_size; + + /* ensure we always have at least 1 padding byte */ + if (pad_bytes == 0) + pad_bytes += ssl->cipher_info->padding_size; + + memset(&ssl->bm_data[msg_length], pad_bytes-1, pad_bytes); + msg_length += pad_bytes; + } + + DISPLAY_BYTES(ssl, "unencrypted write", ssl->bm_data, msg_length); + increment_write_sequence(ssl); + + /* add the explicit IV for TLS1.1 */ + if (ssl->version >= SSL_PROTOCOL_VERSION1_1 && + ssl->cipher_info->iv_size) + { + uint8_t iv_size = ssl->cipher_info->iv_size; + uint8_t *t_buf = (uint8_t *)malloc(msg_length + iv_size); + memcpy(t_buf + iv_size, ssl->bm_data, msg_length); + get_random(iv_size, t_buf); + msg_length += iv_size; + memcpy(ssl->bm_data, t_buf, msg_length); + free(t_buf); + } + + /* now encrypt the packet */ + ssl->cipher_info->encrypt(ssl->encrypt_ctx, ssl->bm_data, + ssl->bm_data, msg_length); + } + else if (protocol == PT_HANDSHAKE_PROTOCOL) + { + DISPLAY_STATE(ssl, 1, ssl->bm_data[0], 0); + + if (ssl->bm_data[0] != HS_HELLO_REQUEST) + { + add_packet(ssl, ssl->bm_data, length); + } + } + + ssl->bm_index = msg_length; + if ((ret = send_raw_packet(ssl, protocol)) <= 0) + return ret; + + return length; /* just return what we wanted to send */ +} + +/** + * Work out the cipher keys we are going to use for this session based on the + * master secret. + */ +static int set_key_block(SSL *ssl, int is_write) +{ + const cipher_info_t *ciph_info = get_cipher_info(ssl->cipher); + uint8_t *q; + uint8_t client_key[32], server_key[32]; /* big enough for AES256 */ + uint8_t client_iv[16], server_iv[16]; /* big enough for AES128/256 */ + int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + + if (ciph_info == NULL) + return -1; + + /* only do once in a handshake */ + if (ssl->dc->key_block == NULL) + { + ssl->dc->key_block = (uint8_t *)malloc(ciph_info->key_block_size); + +#if 0 + print_blob("client", ssl->dc->client_random, 32); + print_blob("server", ssl->dc->server_random, 32); + print_blob("master", ssl->dc->master_secret, SSL_SECRET_SIZE); +#endif + generate_key_block(ssl->dc->client_random, ssl->dc->server_random, + ssl->dc->master_secret, ssl->dc->key_block, + ciph_info->key_block_size); +#if 0 + print_blob("keyblock", ssl->dc->key_block, ciph_info->key_block_size); +#endif + } + + q = ssl->dc->key_block; + + if ((is_client && is_write) || (!is_client && !is_write)) + { + memcpy(ssl->client_mac, q, ciph_info->digest_size); + } + + q += ciph_info->digest_size; + + if ((!is_client && is_write) || (is_client && !is_write)) + { + memcpy(ssl->server_mac, q, ciph_info->digest_size); + } + + q += ciph_info->digest_size; + memcpy(client_key, q, ciph_info->key_size); + q += ciph_info->key_size; + memcpy(server_key, q, ciph_info->key_size); + q += ciph_info->key_size; + +#ifndef CONFIG_SSL_SKELETON_MODE + if (ciph_info->iv_size) /* RC4 has no IV, AES does */ + { + memcpy(client_iv, q, ciph_info->iv_size); + q += ciph_info->iv_size; + memcpy(server_iv, q, ciph_info->iv_size); + q += ciph_info->iv_size; + } +#endif + + free(is_write ? ssl->encrypt_ctx : ssl->decrypt_ctx); + + /* now initialise the ciphers */ + if (is_client) + { + finished_digest(ssl, server_finished, ssl->dc->final_finish_mac); + + if (is_write) + ssl->encrypt_ctx = crypt_new(ssl, client_key, client_iv, 0); + else + ssl->decrypt_ctx = crypt_new(ssl, server_key, server_iv, 1); + } + else + { + finished_digest(ssl, client_finished, ssl->dc->final_finish_mac); + + if (is_write) + ssl->encrypt_ctx = crypt_new(ssl, server_key, server_iv, 0); + else + ssl->decrypt_ctx = crypt_new(ssl, client_key, client_iv, 1); + } + + ssl->cipher_info = ciph_info; + return 0; +} + +/** + * Read the SSL connection - client use. + */ +int basic_read(SSL *ssl, uint8_t **in_data) +{ + int ret = SSL_OK; + int read_len, is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + uint8_t *buf = ssl->bm_rdata; + + read_len = SOCKET_READ(ssl->client_fd, &buf[ssl->bm_rread_index], + ssl->rneed_bytes-ssl->rgot_bytes); + + /* See if this is a recoverable error */ + if (read_len < 0) + { +#ifdef WIN32 + if (GetLastError() == WSAETIMEDOUT) +#else + if (errno == EAGAIN) +#endif + return SSL_TIMEDOUT; + +#ifdef WIN32 + if (GetLastError() == WSAEWOULDBLOCK) +#else + if (errno == EWOULDBLOCK) +#endif + return 0; + } + + /* connection has gone, so die */ + if (read_len <= 0) + { + ret = SSL_ERROR_CONN_LOST; + ssl->hs_status = SSL_ERROR_DEAD; /* make sure it stays dead */ + goto error; + } + + DISPLAY_BYTES(ssl, "received %d bytes", + &ssl->bm_rdata[ssl->bm_rread_index], read_len, read_len); + + ssl->rgot_bytes += read_len; + ssl->bm_rread_index += read_len; + + /* haven't quite got what we want, so try again later */ + if (ssl->rgot_bytes < ssl->rneed_bytes) + return SSL_OK; + + /* Got what we want */ + read_len = ssl->rgot_bytes; + ssl->rgot_bytes = 0; + ssl->bm_rread_index = 0; /* reset to go again */ + + if (ssl->need_record) + { + /* check for sslv2 "client hello" */ + if (buf[0] & 0x80 && buf[2] == 1) + { + printf("Error: no SSLv23 handshaking allowed\n"); TTY_FLUSH(); + ret = SSL_ERROR_NOT_SUPPORTED; + goto error; /* not an error - just get out of here */ + } + + ssl->rneed_bytes = (buf[3] << 8) + buf[4]; + + /* do we violate the spec with the message size? */ + /* (Typically indicates protocol corruption - i.e. the first 3 bytes are */ + /* not the expected ones either) */ + if (ssl->rneed_bytes > RT_MAX_PLAIN_LENGTH+RT_EXTRA-BM_RECORD_OFFSET) + { + ret = SSL_ERROR_INVALID_PROT_MSG; + goto error; + } + + ssl->need_record = 0; + memcpy(ssl->hmac_rheader, buf, 3); /* store for hmac */ + ssl->rrecord_type = buf[0]; + goto error; /* no error, we're done */ + } + + /* for next time - just do it now in case of an error */ + ssl->need_record = 1; + ssl->rneed_bytes = SSL_RECORD_SIZE; + + /* decrypt if we need to */ + if (IS_SET_SSL_FLAG(SSL_RX_ENCRYPTED)) + { + ssl->cipher_info->decrypt(ssl->decrypt_ctx, buf, buf, read_len); + + if (ssl->version >= SSL_PROTOCOL_VERSION1_1 && + ssl->cipher_info->iv_size) + { + buf += ssl->cipher_info->iv_size; + read_len -= ssl->cipher_info->iv_size; + } + + read_len = verify_digest(ssl, is_client ? SSL_CLIENT_READ : SSL_SERVER_READ, + ssl->hmac_rheader, buf, read_len); + + /* does the hmac work? */ + if (read_len < 0) + { + ret = read_len; + goto error; + } + + DISPLAY_BYTES(ssl, "decrypted", buf, read_len); + increment_read_sequence(ssl); + } + + /* The main part of the SSL packet */ + /* (Since we're now running client data and have no clean way */ + /* to break into the asynchronous send stream, disalow any */ + /* protocol change or negotiation.) */ + switch (ssl->rrecord_type) + { + case PT_HANDSHAKE_PROTOCOL: + { + ret = SSL_ERROR_NO_CLIENT_RENOG; + goto error; + } + break; + + case PT_CHANGE_CIPHER_SPEC: + { + ret = SSL_ERROR_INVALID_HANDSHAKE; + goto error; + } + break; + + case PT_APP_PROTOCOL_DATA: + if (in_data) + { + *in_data = buf; /* point to the work buffer */ + (*in_data)[read_len] = 0; /* null terminate just in case */ + } + + ret = read_len; + break; + + case PT_ALERT_PROTOCOL: + ret = SSL_ERROR_INVALID_PROT_MSG; + break; + + default: + ret = SSL_ERROR_INVALID_PROT_MSG; + break; + } + +error: + if (ret < SSL_OK && in_data)/* if all wrong, then clear this buffer ptr */ + *in_data = NULL; + + return ret; +} + +/** + * Read the SSL connection - internal. Used during setup. + */ +int basic_readi(SSL *ssl, uint8_t **in_data) +{ + int ret = SSL_OK; + int read_len, is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + uint8_t *buf = ssl->bm_data; + + read_len = SOCKET_READ(ssl->client_fd, &buf[ssl->bm_read_index], + ssl->need_bytes-ssl->got_bytes); + /* See if this is a recoverable error */ + if (read_len < 0) + { +#ifdef WIN32 + if (GetLastError() == WSAETIMEDOUT) +#else + if (errno == EAGAIN) +#endif + return SSL_TIMEDOUT; + +#ifdef WIN32 + if (GetLastError() == WSAEWOULDBLOCK) +#else + if (errno == EWOULDBLOCK) +#endif + return 0; + } + + /* connection has gone, so die */ + if (read_len <= 0) + { + ret = SSL_ERROR_CONN_LOST; + ssl->hs_status = SSL_ERROR_DEAD; /* make sure it stays dead */ + goto error; + } + + DISPLAY_BYTES(ssl, "received %d bytes", + &ssl->bm_data[ssl->bm_read_index], read_len, read_len); + + ssl->got_bytes += read_len; + ssl->bm_read_index += read_len; + + /* haven't quite got what we want, so try again later */ + if (ssl->got_bytes < ssl->need_bytes) + return SSL_OK; + + /* Got what we want */ + read_len = ssl->got_bytes; + ssl->got_bytes = 0; + ssl->bm_read_index = 0; /* reset to go again */ + + if (IS_SET_SSL_FLAG(SSL_NEED_RECORD)) + { + /* check for sslv2 "client hello" */ + if (buf[0] & 0x80 && buf[2] == 1) + { +#ifdef CONFIG_SSL_ENABLE_V23_HANDSHAKE + uint8_t version = (buf[3] << 4) + buf[4]; + DISPLAY_BYTES(ssl, "ssl2 record", buf, 5); + + /* should be v3.1 (TLSv1) or better */ + ssl->version = ssl->client_version = version; + + if (version > SSL_PROTOCOL_VERSION_MAX) + { + /* use client's version */ + ssl->version = SSL_PROTOCOL_VERSION_MAX; + } + else if (version < SSL_PROTOCOL_MIN_VERSION) + { + ret = SSL_ERROR_INVALID_VERSION; + ssl_display_error(ret); + return ret; + } + + add_packet(ssl, &buf[2], 3); + ret = process_sslv23_client_hello(ssl); +#else + printf("Error: no SSLv23 handshaking allowed\n"); TTY_FLUSH(); + ret = SSL_ERROR_NOT_SUPPORTED; +#endif + goto error; /* not an error - just get out of here */ + } + + ssl->need_bytes = (buf[3] << 8) + buf[4]; + + /* do we violate the spec with the message size? */ + /* (Typically indicates protocol corruption - i.e. the first 3 bytes are */ + /* not the expected ones either) */ + if (ssl->need_bytes > RT_MAX_PLAIN_LENGTH+RT_EXTRA-BM_RECORD_OFFSET) + { + ret = SSL_ERROR_INVALID_PROT_MSG; + goto error; + } + + CLR_SSL_FLAG(SSL_NEED_RECORD); + memcpy(ssl->hmac_header, buf, 3); /* store for hmac */ + ssl->record_type = buf[0]; + goto error; /* no error, we're done */ + } + + /* for next time - just do it now in case of an error */ + SET_SSL_FLAG(SSL_NEED_RECORD); + ssl->need_bytes = SSL_RECORD_SIZE; + + /* decrypt if we need to */ + if (IS_SET_SSL_FLAG(SSL_RX_ENCRYPTED)) + { + ssl->cipher_info->decrypt(ssl->decrypt_ctx, buf, buf, read_len); + + if (ssl->version >= SSL_PROTOCOL_VERSION1_1 && + ssl->cipher_info->iv_size) + { + buf += ssl->cipher_info->iv_size; + read_len -= ssl->cipher_info->iv_size; + } + + read_len = verify_digest(ssl, is_client ? SSL_CLIENT_READ : SSL_SERVER_READ, + ssl->hmac_header, buf, read_len); + + /* does the hmac work? */ + if (read_len < 0) + { + ret = read_len; + goto error; + } + + DISPLAY_BYTES(ssl, "decrypted", buf, read_len); + increment_read_sequence(ssl); + } + + /* The main part of the SSL packet */ + switch (ssl->record_type) + { + case PT_HANDSHAKE_PROTOCOL: + if (ssl->dc != NULL) + { + ssl->dc->bm_proc_index = 0; + ret = do_handshake(ssl, buf, read_len); + } + else /* no client renegotiation allowed */ + { + ret = SSL_ERROR_NO_CLIENT_RENOG; + goto error; + } + break; + + case PT_CHANGE_CIPHER_SPEC: + if (ssl->next_state != HS_FINISHED) + { + ret = SSL_ERROR_INVALID_HANDSHAKE; + goto error; + } + + /* all encrypted from now on */ + SET_SSL_FLAG(SSL_RX_ENCRYPTED); + if (set_key_block(ssl, 0) < 0) + { + ret = SSL_ERROR_INVALID_HANDSHAKE; + goto error; + } + + memset(ssl->read_sequence, 0, 8); + break; + + case PT_APP_PROTOCOL_DATA: + if (in_data) + { + *in_data = buf; /* point to the work buffer */ + (*in_data)[read_len] = 0; /* null terminate just in case */ + } + + ret = read_len; + break; + + case PT_ALERT_PROTOCOL: + /* return the alert # with alert bit set */ + if(buf[0] == SSL_ALERT_TYPE_WARNING && + buf[1] == SSL_ALERT_CLOSE_NOTIFY) + { + ret = SSL_CLOSE_NOTIFY; + send_alert(ssl, SSL_ALERT_CLOSE_NOTIFY); + SET_SSL_FLAG(SSL_SENT_CLOSE_NOTIFY); + } + else + { + ret = -buf[1]; + DISPLAY_ALERT(ssl, buf[1]); + } + + break; + + default: + ret = SSL_ERROR_INVALID_PROT_MSG; + break; + } + +error: + if (ret < SSL_OK && in_data)/* if all wrong, then clear this buffer ptr */ + *in_data = NULL; + + return ret; +} + + +/** + * Do some basic checking of data and then perform the appropriate handshaking. + */ +static int do_handshake(SSL *ssl, uint8_t *buf, int read_len) +{ + int hs_len = (buf[2]<<8) + buf[3]; + uint8_t handshake_type = buf[0]; + int ret = SSL_OK; + int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + + /* some integrity checking on the handshake */ + PARANOIA_CHECK(read_len-SSL_HS_HDR_SIZE, hs_len); + + if (handshake_type != ssl->next_state) + { + /* handle a special case on the client */ + if (!is_client || handshake_type != HS_CERT_REQ || + ssl->next_state != HS_SERVER_HELLO_DONE) + { + ret = SSL_ERROR_INVALID_HANDSHAKE; + goto error; + } + } + + hs_len += SSL_HS_HDR_SIZE; /* adjust for when adding packets */ + ssl->bm_index = hs_len; /* store the size and check later */ + DISPLAY_STATE(ssl, 0, handshake_type, 0); + + if (handshake_type != HS_CERT_VERIFY && handshake_type != HS_HELLO_REQUEST) + add_packet(ssl, buf, hs_len); + +#if defined(CONFIG_SSL_ENABLE_CLIENT) + ret = is_client ? + do_clnt_handshake(ssl, handshake_type, buf, hs_len) : + do_svr_handshake(ssl, handshake_type, buf, hs_len); +#else + ret = do_svr_handshake(ssl, handshake_type, buf, hs_len); +#endif + + /* just use recursion to get the rest */ + if (hs_len < read_len && ret == SSL_OK) + ret = do_handshake(ssl, &buf[hs_len], read_len-hs_len); + +error: + return ret; +} + +/** + * Sends the change cipher spec message. We have just read a finished message + * from the client. + */ +int send_change_cipher_spec(SSL *ssl) +{ + int ret = send_packet(ssl, PT_CHANGE_CIPHER_SPEC, + g_chg_cipher_spec_pkt, sizeof(g_chg_cipher_spec_pkt)); + SET_SSL_FLAG(SSL_TX_ENCRYPTED); + + if (ret >= 0 && set_key_block(ssl, 1) < 0) + ret = SSL_ERROR_INVALID_HANDSHAKE; + + memset(ssl->write_sequence, 0, 8); + return ret; +} + +/** + * Send a "finished" message + */ +int send_finished(SSL *ssl) +{ + uint8_t buf[SSL_FINISHED_HASH_SIZE+4] = { + HS_FINISHED, 0, 0, SSL_FINISHED_HASH_SIZE }; + + /* now add the finished digest mac (12 bytes) */ + finished_digest(ssl, + IS_SET_SSL_FLAG(SSL_IS_CLIENT) ? + client_finished : server_finished, &buf[4]); + +#ifndef CONFIG_SSL_SKELETON_MODE + /* store in the session cache */ + if (!IS_SET_SSL_FLAG(SSL_SESSION_RESUME) && ssl->ssl_ctx->num_sessions) + { + memcpy(ssl->session->master_secret, + ssl->dc->master_secret, SSL_SECRET_SIZE); + } +#endif + + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, + buf, SSL_FINISHED_HASH_SIZE+4); +} + +/** + * Send an alert message. + * Return 1 if the alert was an "error". + */ +int send_alert(SSL *ssl, int error_code) +{ + int alert_num = 0; + int is_warning = 0; + uint8_t buf[2]; + + /* Don't bother we're already dead */ + if (ssl->hs_status == SSL_ERROR_DEAD) + { + return SSL_ERROR_CONN_LOST; + } + +#ifdef CONFIG_SSL_FULL_MODE + if (IS_SET_SSL_FLAG(SSL_DISPLAY_STATES)) + ssl_display_error(error_code); +#endif + + switch (error_code) + { + case SSL_ALERT_CLOSE_NOTIFY: + is_warning = 1; + alert_num = SSL_ALERT_CLOSE_NOTIFY; + break; + + case SSL_ERROR_CONN_LOST: /* don't send alert just yet */ + is_warning = 1; + break; + + case SSL_ERROR_INVALID_HANDSHAKE: + case SSL_ERROR_INVALID_PROT_MSG: + alert_num = SSL_ALERT_HANDSHAKE_FAILURE; + break; + + case SSL_ERROR_INVALID_HMAC: + case SSL_ERROR_FINISHED_INVALID: + alert_num = SSL_ALERT_BAD_RECORD_MAC; + break; + + case SSL_ERROR_INVALID_VERSION: + alert_num = SSL_ALERT_INVALID_VERSION; + break; + + case SSL_ERROR_INVALID_SESSION: + case SSL_ERROR_NO_CIPHER: + case SSL_ERROR_INVALID_KEY: + alert_num = SSL_ALERT_ILLEGAL_PARAMETER; + break; + + case SSL_ERROR_BAD_CERTIFICATE: + alert_num = SSL_ALERT_BAD_CERTIFICATE; + break; + + case SSL_ERROR_NO_CLIENT_RENOG: + alert_num = SSL_ALERT_NO_RENEGOTIATION; + break; + + default: + /* a catch-all for any badly verified certificates */ + alert_num = (error_code <= SSL_X509_OFFSET) ? + SSL_ALERT_BAD_CERTIFICATE : SSL_ALERT_UNEXPECTED_MESSAGE; + break; + } + + buf[0] = is_warning ? 1 : 2; + buf[1] = alert_num; + send_packet(ssl, PT_ALERT_PROTOCOL, buf, sizeof(buf)); + DISPLAY_ALERT(ssl, alert_num); + return is_warning ? 0 : 1; +} + +/** + * Process a client finished message. + */ +int process_finished(SSL *ssl, uint8_t *buf, int hs_len) +{ + int ret = SSL_OK; + int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + int resume = IS_SET_SSL_FLAG(SSL_SESSION_RESUME); + + PARANOIA_CHECK(ssl->bm_index, SSL_FINISHED_HASH_SIZE+4); + + /* check that we all work before we continue */ + if (memcmp(ssl->dc->final_finish_mac, &buf[4], SSL_FINISHED_HASH_SIZE)) + return SSL_ERROR_FINISHED_INVALID; + + if ((!is_client && !resume) || (is_client && resume)) + { + if ((ret = send_change_cipher_spec(ssl)) == SSL_OK) + ret = send_finished(ssl); + } + + /* if we ever renegotiate */ + ssl->next_state = is_client ? HS_HELLO_REQUEST : HS_CLIENT_HELLO; + ssl->hs_status = ret; /* set the final handshake status */ + +error: + return ret; +} + +/** + * Send a certificate. + */ +int send_certificate(SSL *ssl) +{ + int i = 0; + uint8_t *buf = ssl->bm_data; + int offset = 7; + int chain_length; + + buf[0] = HS_CERTIFICATE; + buf[1] = 0; + buf[4] = 0; + + while (i < ssl->ssl_ctx->chain_length) + { + SSL_CERT *cert = &ssl->ssl_ctx->certs[i]; + buf[offset++] = 0; + buf[offset++] = cert->size >> 8; /* cert 1 length */ + buf[offset++] = cert->size & 0xff; + memcpy(&buf[offset], cert->buf, cert->size); + offset += cert->size; + i++; + } + + chain_length = offset - 7; + buf[5] = chain_length >> 8; /* cert chain length */ + buf[6] = chain_length & 0xff; + chain_length += 3; + buf[2] = chain_length >> 8; /* handshake length */ + buf[3] = chain_length & 0xff; + ssl->bm_index = offset; + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, offset); +} + +/** + * Create a blob of memory that we'll get rid of once the handshake is + * complete. + */ +void disposable_new(SSL *ssl) +{ + if (ssl->dc == NULL) + { + ssl->dc = (DISPOSABLE_CTX *)calloc(1, sizeof(DISPOSABLE_CTX)); + MD5_Init(&ssl->dc->md5_ctx); + SHA1_Init(&ssl->dc->sha1_ctx); + } +} + +/** + * Remove the temporary blob of memory. + */ +void disposable_free(SSL *ssl) +{ + if (ssl->dc) + { + free(ssl->dc->key_block); + memset(ssl->dc, 0, sizeof(DISPOSABLE_CTX)); + free(ssl->dc); + ssl->dc = NULL; + } + +} + +#ifndef CONFIG_SSL_SKELETON_MODE /* no session resumption in this mode */ +/** + * Find if an existing session has the same session id. If so, use the + * master secret from this session for session resumption. + */ +SSL_SESSION *ssl_session_update(int max_sessions, SSL_SESSION *ssl_sessions[], + SSL *ssl, const uint8_t *session_id) +{ + time_t tm = time(NULL); + time_t oldest_sess_time = tm; + SSL_SESSION *oldest_sess = NULL; + int i; + + /* no sessions? Then bail */ + if (max_sessions == 0) + return NULL; + + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + if (session_id) + { + for (i = 0; i < max_sessions; i++) + { + if (ssl_sessions[i]) + { + /* kill off any expired sessions (including those in + the future) */ + if ((tm > ssl_sessions[i]->conn_time + SSL_EXPIRY_TIME) || + (tm < ssl_sessions[i]->conn_time)) + { + session_free(ssl_sessions, i); + continue; + } + + /* if the session id matches, it must still be less than + the expiry time */ + if (memcmp(ssl_sessions[i]->session_id, session_id, + SSL_SESSION_ID_SIZE) == 0) + { + ssl->session_index = i; + memcpy(ssl->dc->master_secret, + ssl_sessions[i]->master_secret, SSL_SECRET_SIZE); + SET_SSL_FLAG(SSL_SESSION_RESUME); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + return ssl_sessions[i]; /* a session was found */ + } + } + } + } + + /* If we've got here, no matching session was found - so create one */ + for (i = 0; i < max_sessions; i++) + { + if (ssl_sessions[i] == NULL) + { + /* perfect, this will do */ + ssl_sessions[i] = (SSL_SESSION *)calloc(1, sizeof(SSL_SESSION)); + ssl_sessions[i]->conn_time = tm; + ssl->session_index = i; + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + return ssl_sessions[i]; /* return the session object */ + } + else if (ssl_sessions[i]->conn_time <= oldest_sess_time) + { + /* find the oldest session */ + oldest_sess_time = ssl_sessions[i]->conn_time; + oldest_sess = ssl_sessions[i]; + ssl->session_index = i; + } + } + + /* ok, we've used up all of our sessions. So blow the oldest session away */ + oldest_sess->conn_time = tm; + memset(oldest_sess->session_id, 0, sizeof(SSL_SESSION_ID_SIZE)); + memset(oldest_sess->master_secret, 0, sizeof(SSL_SECRET_SIZE)); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + return oldest_sess; +} + +/** + * Free an existing session. + */ +static void session_free(SSL_SESSION *ssl_sessions[], int sess_index) +{ + if (ssl_sessions[sess_index]) + { + free(ssl_sessions[sess_index]); + ssl_sessions[sess_index] = NULL; + } +} + +/** + * This ssl object doesn't want this session anymore. + */ +void kill_ssl_session(SSL_SESSION **ssl_sessions, SSL *ssl) +{ + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + + if (ssl->ssl_ctx->num_sessions) + { + session_free(ssl_sessions, ssl->session_index); + ssl->session = NULL; + } + + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); +} +#endif /* CONFIG_SSL_SKELETON_MODE */ + +/* + * Get the session id for a handshake. This will be a 32 byte sequence. + */ +EXP_FUNC const uint8_t * STDCALL ssl_get_session_id(const SSL *ssl) +{ + return ssl->session_id; +} + +/* + * Get the session id size for a handshake. + */ +EXP_FUNC uint8_t STDCALL ssl_get_session_id_size(const SSL *ssl) +{ + return ssl->sess_id_size; +} + +/* + * Return the cipher id (in the SSL form). + */ +EXP_FUNC uint8_t STDCALL ssl_get_cipher_id(const SSL *ssl) +{ + return ssl->cipher; +} + +/* + * Return the status of the handshake. + */ +EXP_FUNC int STDCALL ssl_handshake_status(const SSL *ssl) +{ + return ssl->hs_status; +} + +/* + * Retrieve various parameters about the SSL engine. + */ +EXP_FUNC int STDCALL ssl_get_config(int offset) +{ + switch (offset) + { + /* return the appropriate build mode */ + case SSL_BUILD_MODE: +#if defined(CONFIG_SSL_FULL_MODE) + return SSL_BUILD_FULL_MODE; +#elif defined(CONFIG_SSL_ENABLE_CLIENT) + return SSL_BUILD_ENABLE_CLIENT; +#elif defined(CONFIG_ENABLE_VERIFICATION) + return SSL_BUILD_ENABLE_VERIFICATION; +#elif defined(CONFIG_SSL_SERVER_ONLY ) + return SSL_BUILD_SERVER_ONLY; +#else + return SSL_BUILD_SKELETON_MODE; +#endif + + case SSL_MAX_CERT_CFG_OFFSET: + return CONFIG_SSL_MAX_CERTS; + +#ifdef CONFIG_SSL_CERT_VERIFICATION + case SSL_MAX_CA_CERT_CFG_OFFSET: + return CONFIG_X509_MAX_CA_CERTS; +#endif +#ifdef CONFIG_SSL_HAS_PEM + case SSL_HAS_PEM: + return 1; +#endif + default: + return 0; + } +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Authenticate a received certificate. + */ +EXP_FUNC int STDCALL ssl_verify_cert(const SSL *ssl) +{ + int ret; + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + ret = x509_verify(ssl->ssl_ctx->ca_cert_ctx, ssl->x509_ctx); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + if (ret) /* modify into an SSL error type */ + { + ret = SSL_X509_ERROR(ret); + } + + return ret; +} + +/** + * Process a certificate message. + */ +int process_certificate(SSL *ssl, X509_CTX **x509_ctx) +{ + int ret = SSL_OK; + uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index]; + int pkt_size = ssl->bm_index; + int cert_size, offset = 5; + int total_cert_size = (buf[offset]<<8) + buf[offset+1]; + int is_client = IS_SET_SSL_FLAG(SSL_IS_CLIENT); + X509_CTX **chain = x509_ctx; + offset += 2; + + PARANOIA_CHECK(total_cert_size, offset); + + while (offset < total_cert_size) + { + offset++; /* skip empty char */ + cert_size = (buf[offset]<<8) + buf[offset+1]; + offset += 2; + + if (x509_new(&buf[offset], NULL, chain)) + { + ret = SSL_ERROR_BAD_CERTIFICATE; + goto error; + } + + chain = &((*chain)->next); + offset += cert_size; + } + + PARANOIA_CHECK(pkt_size, offset); + + /* if we are client we can do the verify now or later */ + if (is_client && !IS_SET_SSL_FLAG(SSL_SERVER_VERIFY_LATER)) + { + ret = ssl_verify_cert(ssl); + } + + ssl->next_state = is_client ? HS_SERVER_HELLO_DONE : HS_CLIENT_KEY_XCHG; + ssl->dc->bm_proc_index += offset; +error: + return ret; +} + +#endif /* CONFIG_SSL_CERT_VERIFICATION */ + +/** + * Debugging routine to return SSL handshaking errors. + */ +EXP_FUNC const char * STDCALL ssl_error_string(int error_code) +{ + static char buf[100]; + + if (error_code == SSL_OK) + return "SSL_OK"; + + /* X509 error? */ + if (error_code < SSL_X509_OFFSET) + { + return x509_display_error(error_code - SSL_X509_OFFSET); + } + + /* SSL alert error code */ + if (error_code > SSL_ERROR_CONN_LOST) + { + switch (error_code) + { + case SSL_OK: + return("Alert - OK"); + + case SSL_NOT_OK: + return("Alert - Not OK"); + + case SSL_ERROR_DEAD: + return("Alert - Error Dead"); + + case SSL_CLOSE_NOTIFY: + return("Alert - Close Notify"); + + case SSL_TIMEDOUT: + return("Alert - Timed Out"); + } + sprintf(buf, "SSL Alert %d", -error_code); + return buf; + } + + /* Something bad */ + switch (error_code) + { + case SSL_ERROR_DEAD: + return("connection dead"); + + case SSL_ERROR_INVALID_HANDSHAKE: + return("invalid handshake"); + + case SSL_ERROR_INVALID_PROT_MSG: + return("invalid protocol message"); + + case SSL_ERROR_INVALID_HMAC: + return("invalid mac"); + + case SSL_ERROR_INVALID_VERSION: + return("invalid version"); + + case SSL_ERROR_INVALID_SESSION: + return("invalid session"); + + case SSL_ERROR_NO_CIPHER: + return("no cipher"); + + case SSL_ERROR_CONN_LOST: + return("connection lost"); + + case SSL_ERROR_BAD_CERTIFICATE: + return("bad certificate"); + + case SSL_ERROR_INVALID_KEY: + return("invalid key"); + + case SSL_ERROR_FINISHED_INVALID: + return("finished invalid"); + + case SSL_ERROR_NO_CERT_DEFINED: + return("no certificate defined"); + + case SSL_ERROR_NO_CLIENT_RENOG: + return("client renegotiation not supported"); + + case SSL_ERROR_NOT_SUPPORTED: + return("Option not supported"); + } + sprintf(buf,"undefined - %d", error_code); + return buf; +} + +/** + * Debugging routine to display SSL handshaking stuff. + */ +#ifdef CONFIG_SSL_FULL_MODE +/** + * Debugging routine to display SSL states. + */ +void DISPLAY_STATE(SSL *ssl, int is_send, uint8_t state, int not_ok) +{ + const char *str; + + if (!IS_SET_SSL_FLAG(SSL_DISPLAY_STATES)) + return; + + printf(not_ok ? "Error - invalid State:\t" : "State:\t"); + printf(is_send ? "sending " : "receiving "); + + switch (state) + { + case HS_HELLO_REQUEST: + str = "Hello Request (0)"; + break; + + case HS_CLIENT_HELLO: + str = "Client Hello (1)"; + break; + + case HS_SERVER_HELLO: + str = "Server Hello (2)"; + break; + + case HS_CERTIFICATE: + str = "Certificate (11)"; + break; + + case HS_SERVER_KEY_XCHG: + str = "Certificate Request (12)"; + break; + + case HS_CERT_REQ: + str = "Certificate Request (13)"; + break; + + case HS_SERVER_HELLO_DONE: + str = "Server Hello Done (14)"; + break; + + case HS_CERT_VERIFY: + str = "Certificate Verify (15)"; + break; + + case HS_CLIENT_KEY_XCHG: + str = "Client Key Exchange (16)"; + break; + + case HS_FINISHED: + str = "Finished (16)"; + break; + + default: + str = "Error (Unknown)"; + + break; + } + + printf("%s\n", str); + TTY_FLUSH(); +} + +/** + * Debugging routine to display RSA objects + */ +void DISPLAY_RSA(SSL *ssl, const RSA_CTX *rsa_ctx) +{ + if (!IS_SET_SSL_FLAG(SSL_DISPLAY_RSA)) + return; + + RSA_print(rsa_ctx); + TTY_FLUSH(); +} + +/** + * Debugging routine to display SSL handshaking bytes. + */ +void DISPLAY_BYTES(SSL *ssl, const char *format, + const uint8_t *data, int size, ...) +{ + va_list(ap); + + if (!IS_SET_SSL_FLAG(SSL_DISPLAY_BYTES)) + return; + + va_start(ap, size); + print_blob(format, data, size, va_arg(ap, char *)); + va_end(ap); + TTY_FLUSH(); +} + +/** + * Debugging routine to display SSL handshaking errors. + */ +EXP_FUNC void STDCALL ssl_display_error(int error_code) +{ + if (error_code == SSL_OK) + return; + + printf("Error: %s\n",ssl_error_string(error_code)); + TTY_FLUSH(); +} + +/** + * Debugging routine to display alerts. + */ +void DISPLAY_ALERT(SSL *ssl, int alert) +{ + if (!IS_SET_SSL_FLAG(SSL_DISPLAY_STATES)) + return; + + printf("Alert: "); + + switch (alert) + { + case SSL_ALERT_CLOSE_NOTIFY: + printf("close notify"); + break; + + case SSL_ALERT_INVALID_VERSION: + printf("invalid version"); + break; + + case SSL_ALERT_BAD_CERTIFICATE: + printf("bad certificate"); + break; + + case SSL_ALERT_UNEXPECTED_MESSAGE: + printf("unexpected message"); + break; + + case SSL_ALERT_BAD_RECORD_MAC: + printf("bad record mac"); + break; + + case SSL_ALERT_HANDSHAKE_FAILURE: + printf("handshake failure"); + break; + + case SSL_ALERT_ILLEGAL_PARAMETER: + printf("illegal parameter"); + break; + + case SSL_ALERT_DECODE_ERROR: + printf("decode error"); + break; + + case SSL_ALERT_DECRYPT_ERROR: + printf("decrypt error"); + break; + + case SSL_ALERT_NO_RENEGOTIATION: + printf("no renegotiation"); + break; + + default: + printf("alert - (unknown %d)", alert); + break; + } + + printf("\n"); + TTY_FLUSH(); +} + +#endif /* CONFIG_SSL_FULL_MODE */ + +/** + * Return the version of this library. + */ +EXP_FUNC const char * STDCALL ssl_version() +{ + static const char * axtls_version = AXTLS_VERSION; + return axtls_version; +} + +/** + * Enable the various language bindings to work regardless of the + * configuration - they just return an error statement and a bad return code. + */ +#if !defined(CONFIG_SSL_FULL_MODE) +EXP_FUNC void STDCALL ssl_display_error(int error_code) {} +#endif + +#ifdef CONFIG_BINDINGS +#if !defined(CONFIG_SSL_ENABLE_CLIENT) +EXP_FUNC SSL * STDCALL ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const + uint8_t *session_id, uint8_t sess_id_size) +{ + puts(unsupported_str); + return NULL; +} +#endif + +#if !defined(CONFIG_SSL_CERT_VERIFICATION) +EXP_FUNC int STDCALL ssl_verify_cert(const SSL *ssl) +{ + puts(unsupported_str); + return -1; +} + + +EXP_FUNC const char * STDCALL ssl_get_cert_dn(const SSL *ssl, int component) +{ + puts(unsupported_str); + return NULL; +} + +EXP_FUNC const char * STDCALL ssl_get_cert_subject_alt_dnsname(const SSL *ssl, int index) +{ + puts(unsupported_str); + return NULL; +} + +#endif /* CONFIG_SSL_CERT_VERIFICATION */ + +#endif /* CONFIG_BINDINGS */ + diff --git a/ccast/axTLS/tls1.h b/ccast/axTLS/tls1.h new file mode 100644 index 0000000..0086302 --- /dev/null +++ b/ccast/axTLS/tls1.h @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * @file tls1.h + * + * @brief The definitions for the TLS library. + */ +#ifndef HEADER_SSL_LIB_H +#define HEADER_SSL_LIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "version.h" +#include "config.h" +#include "os_int.h" +#include "crypto.h" +#include "crypto_misc.h" + +#define SSL_PROTOCOL_MIN_VERSION 0x31 /* TLS v1.0 */ +#define SSL_PROTOCOL_MINOR_VERSION 0x02 /* TLS v1.1 */ +#define SSL_PROTOCOL_VERSION_MAX 0x32 /* TLS v1.1 */ +#define SSL_PROTOCOL_VERSION1_1 0x32 /* TLS v1.1 */ +#define SSL_RANDOM_SIZE 32 +#define SSL_SECRET_SIZE 48 +#define SSL_FINISHED_HASH_SIZE 12 +#define SSL_RECORD_SIZE 5 +#define SSL_SERVER_READ 0 +#define SSL_SERVER_WRITE 1 +#define SSL_CLIENT_READ 2 +#define SSL_CLIENT_WRITE 3 +#define SSL_HS_HDR_SIZE 4 + +/* the flags we use while establishing a connection */ +#define SSL_NEED_RECORD 0x0001 +#define SSL_TX_ENCRYPTED 0x0002 +#define SSL_RX_ENCRYPTED 0x0004 +#define SSL_SESSION_RESUME 0x0008 +#define SSL_IS_CLIENT 0x0010 +#define SSL_HAS_CERT_REQ 0x0020 +#define SSL_SENT_CLOSE_NOTIFY 0x0040 + +/* some macros to muck around with flag bits */ +#define SET_SSL_FLAG(A) (ssl->flag |= A) +#define CLR_SSL_FLAG(A) (ssl->flag &= ~A) +#define IS_SET_SSL_FLAG(A) (ssl->flag & A) + +#define MAX_KEY_BYTE_SIZE 512 /* for a 4096 bit key */ +#define RT_MAX_PLAIN_LENGTH 16384 +#define RT_EXTRA 1024 +#define BM_RECORD_OFFSET 5 + +#ifdef CONFIG_SSL_SKELETON_MODE +#define NUM_PROTOCOLS 1 +#else +#define NUM_PROTOCOLS 4 +#endif + +#define PARANOIA_CHECK(A, B) if (A < B) { \ + ret = SSL_ERROR_INVALID_HANDSHAKE; goto error; } + +/* protocol types */ +enum +{ + PT_CHANGE_CIPHER_SPEC = 20, + PT_ALERT_PROTOCOL, + PT_HANDSHAKE_PROTOCOL, + PT_APP_PROTOCOL_DATA +}; + +/* handshaking types */ +enum +{ + HS_HELLO_REQUEST, + HS_CLIENT_HELLO, + HS_SERVER_HELLO, + HS_CERTIFICATE = 11, + HS_SERVER_KEY_XCHG, + HS_CERT_REQ, + HS_SERVER_HELLO_DONE, + HS_CERT_VERIFY, + HS_CLIENT_KEY_XCHG, + HS_FINISHED = 20 +}; + +typedef struct +{ + uint8_t cipher; + uint8_t key_size; + uint8_t iv_size; + uint8_t key_block_size; + uint8_t padding_size; + uint8_t digest_size; + hmac_func hmac; + crypt_func encrypt; + crypt_func decrypt; +} cipher_info_t; + +struct _SSLObjLoader +{ + uint8_t *buf; + int len; +}; + +typedef struct _SSLObjLoader SSLObjLoader; + +typedef struct +{ + time_t conn_time; + uint8_t session_id[SSL_SESSION_ID_SIZE]; + uint8_t master_secret[SSL_SECRET_SIZE]; +} SSL_SESSION; + +typedef struct +{ + uint8_t *buf; + int size; +} SSL_CERT; + +typedef struct +{ + MD5_CTX md5_ctx; + SHA1_CTX sha1_ctx; + uint8_t final_finish_mac[SSL_FINISHED_HASH_SIZE]; + uint8_t *key_block; + uint8_t master_secret[SSL_SECRET_SIZE]; + uint8_t client_random[SSL_RANDOM_SIZE]; /* client's random sequence */ + uint8_t server_random[SSL_RANDOM_SIZE]; /* server's random sequence */ + uint16_t bm_proc_index; +} DISPOSABLE_CTX; + +struct _SSL +{ + uint32_t flag; + uint16_t need_bytes; + uint16_t got_bytes; + uint8_t record_type; + uint8_t cipher; + uint8_t sess_id_size; + uint8_t version; + uint8_t client_version; + int16_t next_state; + int16_t hs_status; + DISPOSABLE_CTX *dc; /* temporary data which we'll get rid of soon */ + int client_fd; + const cipher_info_t *cipher_info; + void *encrypt_ctx; + void *decrypt_ctx; + /* protocol negotiation shared read/write state */ + uint8_t bm_all_data[RT_MAX_PLAIN_LENGTH+RT_EXTRA]; + uint8_t *bm_data; + uint16_t bm_read_index; + uint16_t bm_index; + struct _SSL *next; /* doubly linked list */ + struct _SSL *prev; + struct _SSL_CTX *ssl_ctx; /* back reference to a clnt/svr ctx */ + + /* asynchronous client ssl_read specific duplicates of above: */ + uint16_t rneed_bytes; + uint16_t rgot_bytes; + uint8_t bm_read_data[RT_MAX_PLAIN_LENGTH+RT_EXTRA]; + uint8_t *bm_rdata; + uint16_t bm_rread_index; + uint8_t hmac_rheader[SSL_RECORD_SIZE]; /* client rx hmac */ + uint8_t rrecord_type; + int need_record; + +#ifndef CONFIG_SSL_SKELETON_MODE + uint16_t session_index; + SSL_SESSION *session; +#endif +#ifdef CONFIG_SSL_CERT_VERIFICATION + X509_CTX *x509_ctx; +#endif + + uint8_t session_id[SSL_SESSION_ID_SIZE]; + uint8_t client_mac[SHA1_SIZE]; /* for HMAC verification */ + uint8_t server_mac[SHA1_SIZE]; /* for HMAC verification */ + uint8_t read_sequence[8]; /* 64 bit sequence number */ + uint8_t write_sequence[8]; /* 64 bit sequence number */ + uint8_t hmac_header[SSL_RECORD_SIZE]; /* rx hmac */ +}; + +typedef struct _SSL SSL; + +struct _SSL_CTX +{ + uint32_t options; + uint8_t chain_length; + RSA_CTX *rsa_ctx; +#ifdef CONFIG_SSL_CERT_VERIFICATION + CA_CERT_CTX *ca_cert_ctx; +#endif + SSL *head; + SSL *tail; + SSL_CERT certs[CONFIG_SSL_MAX_CERTS]; +#ifndef CONFIG_SSL_SKELETON_MODE + uint16_t num_sessions; + SSL_SESSION **ssl_sessions; +#endif +#ifdef CONFIG_SSL_CTX_MUTEXING + SSL_CTX_MUTEX_TYPE mutex; +#endif +#ifdef CONFIG_OPENSSL_COMPATIBLE + void *bonus_attr; +#endif +}; + +typedef struct _SSL_CTX SSL_CTX; + +/* backwards compatibility */ +typedef struct _SSL_CTX SSLCTX; + +extern const uint8_t ssl_prot_prefs[NUM_PROTOCOLS]; + +SSL *ssl_new(SSL_CTX *ssl_ctx, int client_fd); +void disposable_new(SSL *ssl); +void disposable_free(SSL *ssl); +int send_packet(SSL *ssl, uint8_t protocol, + const uint8_t *in, int length); +int do_svr_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len); +int do_clnt_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len); +int process_finished(SSL *ssl, uint8_t *buf, int hs_len); +int process_sslv23_client_hello(SSL *ssl); +int send_alert(SSL *ssl, int error_code); +int send_finished(SSL *ssl); +int send_certificate(SSL *ssl); +int basic_read(SSL *ssl, uint8_t **in_data); +int send_change_cipher_spec(SSL *ssl); +void finished_digest(SSL *ssl, const char *label, uint8_t *digest); +void generate_master_secret(SSL *ssl, const uint8_t *premaster_secret); +void add_packet(SSL *ssl, const uint8_t *pkt, int len); +int add_cert(SSL_CTX *ssl_ctx, const uint8_t *buf, int len); +int add_private_key(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj); +void ssl_obj_free(SSLObjLoader *ssl_obj); +int pkcs8_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password); +int pkcs12_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password); +int load_key_certs(SSL_CTX *ssl_ctx); +#ifdef CONFIG_SSL_CERT_VERIFICATION +int add_cert_auth(SSL_CTX *ssl_ctx, const uint8_t *buf, int len); +void remove_ca_certs(CA_CERT_CTX *ca_cert_ctx); +#endif +#ifdef CONFIG_SSL_ENABLE_CLIENT +int do_client_connect(SSL *ssl); +#endif + +#ifdef CONFIG_SSL_FULL_MODE +void DISPLAY_STATE(SSL *ssl, int is_send, uint8_t state, int not_ok); +void DISPLAY_BYTES(SSL *ssl, const char *format, + const uint8_t *data, int size, ...); +void DISPLAY_CERT(SSL *ssl, const X509_CTX *x509_ctx); +void DISPLAY_RSA(SSL *ssl, const RSA_CTX *rsa_ctx); +void DISPLAY_ALERT(SSL *ssl, int alert); +#else +#define DISPLAY_STATE(A,B,C,D) +#define DISPLAY_CERT(A,B) +#define DISPLAY_RSA(A,B) +#define DISPLAY_ALERT(A, B) +#ifdef WIN32 +void DISPLAY_BYTES(SSL *ssl, const char *format,/* win32 has no variadic macros */ + const uint8_t *data, int size, ...); +#else +#define DISPLAY_BYTES(A,B,C,D,...) +#endif +#endif + +#ifdef CONFIG_SSL_CERT_VERIFICATION +int process_certificate(SSL *ssl, X509_CTX **x509_ctx); +#endif + +SSL_SESSION *ssl_session_update(int max_sessions, + SSL_SESSION *ssl_sessions[], SSL *ssl, + const uint8_t *session_id); +void kill_ssl_session(SSL_SESSION **ssl_sessions, SSL *ssl); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ccast/axTLS/tls1_clnt.c b/ccast/axTLS/tls1_clnt.c new file mode 100644 index 0000000..cb8b990 --- /dev/null +++ b/ccast/axTLS/tls1_clnt.c @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +#include +#include +#include +#include +#include "os_port.h" +#include "ssl.h" + +#ifdef CONFIG_SSL_ENABLE_CLIENT /* all commented out if no client */ + +static int send_client_hello(SSL *ssl); +static int process_server_hello(SSL *ssl); +static int process_server_hello_done(SSL *ssl); +static int send_client_key_xchg(SSL *ssl); +static int process_cert_req(SSL *ssl); +static int send_cert_verify(SSL *ssl); + +/* + * Establish a new SSL connection to an SSL server. + */ +EXP_FUNC SSL * STDCALL ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const + uint8_t *session_id, uint8_t sess_id_size) +{ + SSL *ssl = ssl_new(ssl_ctx, client_fd); + ssl->version = SSL_PROTOCOL_VERSION_MAX; /* try top version first */ + + if (session_id && ssl_ctx->num_sessions) + { + if (sess_id_size > SSL_SESSION_ID_SIZE) /* validity check */ + { + ssl_free(ssl); + return NULL; + } + + memcpy(ssl->session_id, session_id, sess_id_size); + ssl->sess_id_size = sess_id_size; + SET_SSL_FLAG(SSL_SESSION_RESUME); /* just flag for later */ + } + + SET_SSL_FLAG(SSL_IS_CLIENT); + do_client_connect(ssl); + return ssl; +} + +/* + * Process the handshake record. + */ +int do_clnt_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len) +{ + int ret; + + /* To get here the state must be valid */ + switch (handshake_type) + { + case HS_SERVER_HELLO: + ret = process_server_hello(ssl); + break; + + case HS_CERTIFICATE: + ret = process_certificate(ssl, &ssl->x509_ctx); + break; + + case HS_SERVER_HELLO_DONE: + if ((ret = process_server_hello_done(ssl)) == SSL_OK) + { + if (IS_SET_SSL_FLAG(SSL_HAS_CERT_REQ)) + { + if ((ret = send_certificate(ssl)) == SSL_OK && + (ret = send_client_key_xchg(ssl)) == SSL_OK) + { + send_cert_verify(ssl); + } + } + else + { + ret = send_client_key_xchg(ssl); + } + + if (ret == SSL_OK && + (ret = send_change_cipher_spec(ssl)) == SSL_OK) + { + ret = send_finished(ssl); + } + } + break; + + case HS_CERT_REQ: + ret = process_cert_req(ssl); + break; + + case HS_FINISHED: + ret = process_finished(ssl, buf, hs_len); + disposable_free(ssl); /* free up some memory */ + /* note: client renegotiation is not allowed after this */ + break; + + case HS_HELLO_REQUEST: + disposable_new(ssl); + ret = do_client_connect(ssl); + break; + + default: + ret = SSL_ERROR_INVALID_HANDSHAKE; + break; + } + + return ret; +} + +/* + * Do the handshaking from the beginning. + */ +int do_client_connect(SSL *ssl) +{ + int ret = SSL_OK; + + send_client_hello(ssl); /* send the client hello */ + ssl->bm_read_index = 0; + ssl->bm_rread_index = 0; + ssl->next_state = HS_SERVER_HELLO; + ssl->hs_status = SSL_NOT_OK; /* not connected */ + + /* sit in a loop until it all looks good */ + if (!IS_SET_SSL_FLAG(SSL_CONNECT_IN_PARTS)) + { + while (ssl->hs_status != SSL_OK) + { + /* Use internal, synchronous ssl_read */ + ret = ssl_readi(ssl, NULL); + + if (ret < SSL_OK) + break; + } + + ssl->hs_status = ret; /* connected? */ + } + + return ret; +} + +/* + * Send the initial client hello. + */ +static int send_client_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + time_t tm = time(NULL); + uint8_t *tm_ptr = &buf[6]; /* time will go here */ + int i, offset; + + buf[0] = HS_CLIENT_HELLO; + buf[1] = 0; + buf[2] = 0; + /* byte 3 is calculated later */ + buf[4] = 0x03; + buf[5] = ssl->version & 0x0f; + + /* client random value - spec says that 1st 4 bytes are big endian time */ + *tm_ptr++ = (uint8_t)(((long)tm & 0xff000000) >> 24); + *tm_ptr++ = (uint8_t)(((long)tm & 0x00ff0000) >> 16); + *tm_ptr++ = (uint8_t)(((long)tm & 0x0000ff00) >> 8); + *tm_ptr++ = (uint8_t)(((long)tm & 0x000000ff)); + get_random(SSL_RANDOM_SIZE-4, &buf[10]); + memcpy(ssl->dc->client_random, &buf[6], SSL_RANDOM_SIZE); + offset = 6 + SSL_RANDOM_SIZE; + + /* give session resumption a go */ + if (IS_SET_SSL_FLAG(SSL_SESSION_RESUME)) /* set initially by user */ + { + buf[offset++] = ssl->sess_id_size; + memcpy(&buf[offset], ssl->session_id, ssl->sess_id_size); + offset += ssl->sess_id_size; + CLR_SSL_FLAG(SSL_SESSION_RESUME); /* clear so we can set later */ + } + else + { + /* no session id - because no session resumption just yet */ + buf[offset++] = 0; + } + + buf[offset++] = 0; /* number of ciphers */ + buf[offset++] = NUM_PROTOCOLS*2;/* number of ciphers */ + + /* put all our supported protocols in our request */ + for (i = 0; i < NUM_PROTOCOLS; i++) + { + buf[offset++] = 0; /* cipher we are using */ + buf[offset++] = ssl_prot_prefs[i]; + } + + buf[offset++] = 1; /* no compression */ + buf[offset++] = 0; + buf[3] = offset - 4; /* handshake size */ + + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, offset); +} + +/* + * Process the server hello. + */ +static int process_server_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + int pkt_size = ssl->bm_index; + int num_sessions = ssl->ssl_ctx->num_sessions; + uint8_t sess_id_size; + int offset, ret = SSL_OK; + + /* check that we are talking to a TLSv1 server */ + uint8_t version = (buf[4] << 4) + buf[5]; + if (version > SSL_PROTOCOL_VERSION_MAX) + { + version = SSL_PROTOCOL_VERSION_MAX; + } + else if (ssl->version < SSL_PROTOCOL_MIN_VERSION) + { + ret = SSL_ERROR_INVALID_VERSION; + ssl_display_error(ret); + goto error; + } + + ssl->version = version; + + /* get the server random value */ + memcpy(ssl->dc->server_random, &buf[6], SSL_RANDOM_SIZE); + offset = 6 + SSL_RANDOM_SIZE; /* skip of session id size */ + sess_id_size = buf[offset++]; + + if (sess_id_size > SSL_SESSION_ID_SIZE) + { + ret = SSL_ERROR_INVALID_SESSION; + goto error; + } + + if (num_sessions) + { + ssl->session = ssl_session_update(num_sessions, + ssl->ssl_ctx->ssl_sessions, ssl, &buf[offset]); + memcpy(ssl->session->session_id, &buf[offset], sess_id_size); + + /* pad the rest with 0's */ + if (sess_id_size < SSL_SESSION_ID_SIZE) + { + memset(&ssl->session->session_id[sess_id_size], 0, + SSL_SESSION_ID_SIZE-sess_id_size); + } + } + + memcpy(ssl->session_id, &buf[offset], sess_id_size); + ssl->sess_id_size = sess_id_size; + offset += sess_id_size; + + /* get the real cipher we are using */ + ssl->cipher = buf[++offset]; + ssl->next_state = IS_SET_SSL_FLAG(SSL_SESSION_RESUME) ? + HS_FINISHED : HS_CERTIFICATE; + + offset++; // skip the compr + PARANOIA_CHECK(pkt_size, offset); + ssl->dc->bm_proc_index = offset+1; + +error: + return ret; +} + +/** + * Process the server hello done message. + */ +static int process_server_hello_done(SSL *ssl) +{ + ssl->next_state = HS_FINISHED; + return SSL_OK; +} + +/* + * Send a client key exchange message. + */ +static int send_client_key_xchg(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + uint8_t premaster_secret[SSL_SECRET_SIZE]; + int enc_secret_size = -1; + + buf[0] = HS_CLIENT_KEY_XCHG; + buf[1] = 0; + + premaster_secret[0] = 0x03; /* encode the version number */ + premaster_secret[1] = SSL_PROTOCOL_MINOR_VERSION; /* must be TLS 1.1 */ + get_random(SSL_SECRET_SIZE-2, &premaster_secret[2]); + DISPLAY_RSA(ssl, ssl->x509_ctx->rsa_ctx); + + /* rsa_ctx->bi_ctx is not thread-safe */ + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + enc_secret_size = RSA_encrypt(ssl->x509_ctx->rsa_ctx, premaster_secret, + SSL_SECRET_SIZE, &buf[6], 0); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + buf[2] = (enc_secret_size + 2) >> 8; + buf[3] = (enc_secret_size + 2) & 0xff; + buf[4] = enc_secret_size >> 8; + buf[5] = enc_secret_size & 0xff; + + generate_master_secret(ssl, premaster_secret); + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, enc_secret_size+6); +} + +/* + * Process the certificate request. + */ +static int process_cert_req(SSL *ssl) +{ + uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index]; + int ret = SSL_OK; + int offset = (buf[2] << 4) + buf[3]; + int pkt_size = ssl->bm_index; + + /* don't do any processing - we will send back an RSA certificate anyway */ + ssl->next_state = HS_SERVER_HELLO_DONE; + SET_SSL_FLAG(SSL_HAS_CERT_REQ); + ssl->dc->bm_proc_index += offset; + PARANOIA_CHECK(pkt_size, offset); +error: + return ret; +} + +/* + * Send a certificate verify message. + */ +static int send_cert_verify(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + uint8_t dgst[MD5_SIZE+SHA1_SIZE]; + RSA_CTX *rsa_ctx = ssl->ssl_ctx->rsa_ctx; + int n = 0, ret; + + DISPLAY_RSA(ssl, rsa_ctx); + + buf[0] = HS_CERT_VERIFY; + buf[1] = 0; + + finished_digest(ssl, NULL, dgst); /* calculate the digest */ + + /* rsa_ctx->bi_ctx is not thread-safe */ + if (rsa_ctx) + { + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + n = RSA_encrypt(rsa_ctx, dgst, sizeof(dgst), &buf[6], 1); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + if (n == 0) + { + ret = SSL_ERROR_INVALID_KEY; + goto error; + } + } + + buf[4] = n >> 8; /* add the RSA size (not officially documented) */ + buf[5] = n & 0xff; + n += 2; + buf[2] = n >> 8; + buf[3] = n & 0xff; + ret = send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, n+4); + +error: + return ret; +} + +#endif /* CONFIG_SSL_ENABLE_CLIENT */ diff --git a/ccast/axTLS/tls1_svr.c b/ccast/axTLS/tls1_svr.c new file mode 100644 index 0000000..51c9d76 --- /dev/null +++ b/ccast/axTLS/tls1_svr.c @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +#include +#include +#include +#include "os_port.h" +#include "ssl.h" + +static const uint8_t g_hello_done[] = { HS_SERVER_HELLO_DONE, 0, 0, 0 }; + +static int process_client_hello(SSL *ssl); +static int send_server_hello_sequence(SSL *ssl); +static int send_server_hello(SSL *ssl); +static int send_server_hello_done(SSL *ssl); +static int process_client_key_xchg(SSL *ssl); +#ifdef CONFIG_SSL_CERT_VERIFICATION +static int send_certificate_request(SSL *ssl); +static int process_cert_verify(SSL *ssl); +#endif + +/* + * Establish a new SSL connection to an SSL client. + */ +EXP_FUNC SSL * STDCALL ssl_server_new(SSL_CTX *ssl_ctx, int client_fd) +{ + SSL *ssl; + + ssl = ssl_new(ssl_ctx, client_fd); + ssl->next_state = HS_CLIENT_HELLO; + +#ifdef CONFIG_SSL_FULL_MODE + if (ssl_ctx->chain_length == 0) + printf("Warning - no server certificate defined\n"); TTY_FLUSH(); +#endif + + return ssl; +} + +/* + * Process the handshake record. + */ +int do_svr_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len) +{ + int ret = SSL_OK; + ssl->hs_status = SSL_NOT_OK; /* not connected */ + + /* To get here the state must be valid */ + switch (handshake_type) + { + case HS_CLIENT_HELLO: + if ((ret = process_client_hello(ssl)) == SSL_OK) + ret = send_server_hello_sequence(ssl); + break; + +#ifdef CONFIG_SSL_CERT_VERIFICATION + case HS_CERTIFICATE:/* the client sends its cert */ + ret = process_certificate(ssl, &ssl->x509_ctx); + + if (ret == SSL_OK) /* verify the cert */ + { + int cert_res; + cert_res = x509_verify( + ssl->ssl_ctx->ca_cert_ctx, ssl->x509_ctx); + ret = (cert_res == 0) ? SSL_OK : SSL_X509_ERROR(cert_res); + } + break; + + case HS_CERT_VERIFY: + ret = process_cert_verify(ssl); + add_packet(ssl, buf, hs_len); /* needs to be done after */ + break; +#endif + case HS_CLIENT_KEY_XCHG: + ret = process_client_key_xchg(ssl); + break; + + case HS_FINISHED: + ret = process_finished(ssl, buf, hs_len); + disposable_free(ssl); /* free up some memory */ + break; + } + + return ret; +} + +/* + * Process a client hello message. + */ +static int process_client_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + uint8_t *record_buf = ssl->hmac_header; + int pkt_size = ssl->bm_index; + int i, j, cs_len, id_len, offset = 6 + SSL_RANDOM_SIZE; + int ret = SSL_OK; + + uint8_t version = (buf[4] << 4) + buf[5]; + ssl->version = ssl->client_version = version; + + if (version > SSL_PROTOCOL_VERSION_MAX) + { + /* use client's version instead */ + ssl->version = SSL_PROTOCOL_VERSION_MAX; + } + else if (version < SSL_PROTOCOL_MIN_VERSION) /* old version supported? */ + { + ret = SSL_ERROR_INVALID_VERSION; + ssl_display_error(ret); + goto error; + } + + memcpy(ssl->dc->client_random, &buf[6], SSL_RANDOM_SIZE); + + /* process the session id */ + id_len = buf[offset++]; + if (id_len > SSL_SESSION_ID_SIZE) + { + return SSL_ERROR_INVALID_SESSION; + } + +#ifndef CONFIG_SSL_SKELETON_MODE + ssl->session = ssl_session_update(ssl->ssl_ctx->num_sessions, + ssl->ssl_ctx->ssl_sessions, ssl, id_len ? &buf[offset] : NULL); +#endif + + offset += id_len; + cs_len = (buf[offset]<<8) + buf[offset+1]; + offset += 3; /* add 1 due to all cipher suites being 8 bit */ + + PARANOIA_CHECK(pkt_size, offset); + + /* work out what cipher suite we are going to use - client defines + the preference */ + for (i = 0; i < cs_len; i += 2) + { + for (j = 0; j < NUM_PROTOCOLS; j++) + { + if (ssl_prot_prefs[j] == buf[offset+i]) /* got a match? */ + { + ssl->cipher = ssl_prot_prefs[j]; + goto do_state; + } + } + } + + /* ouch! protocol is not supported */ + ret = SSL_ERROR_NO_CIPHER; + +do_state: +error: + return ret; +} + +#ifdef CONFIG_SSL_ENABLE_V23_HANDSHAKE +/* + * Some browsers use a hybrid SSLv2 "client hello" + */ +int process_sslv23_client_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + int bytes_needed = ((buf[0] & 0x7f) << 8) + buf[1]; + int ret = SSL_OK; + + /* we have already read 3 extra bytes so far */ + int read_len = SOCKET_READ(ssl->client_fd, buf, bytes_needed-3); + int cs_len = buf[1]; + int id_len = buf[3]; + int ch_len = buf[5]; + int i, j, offset = 8; /* start at first cipher */ + int random_offset = 0; + + DISPLAY_BYTES(ssl, "received %d bytes", buf, read_len, read_len); + + add_packet(ssl, buf, read_len); + + /* connection has gone, so die */ + if (bytes_needed < 0) + { + return SSL_ERROR_CONN_LOST; + } + + /* now work out what cipher suite we are going to use */ + for (j = 0; j < NUM_PROTOCOLS; j++) + { + for (i = 0; i < cs_len; i += 3) + { + if (ssl_prot_prefs[j] == buf[offset+i]) + { + ssl->cipher = ssl_prot_prefs[j]; + goto server_hello; + } + } + } + + /* ouch! protocol is not supported */ + ret = SSL_ERROR_NO_CIPHER; + goto error; + +server_hello: + /* get the session id */ + offset += cs_len - 2; /* we've gone 2 bytes past the end */ +#ifndef CONFIG_SSL_SKELETON_MODE + ssl->session = ssl_session_update(ssl->ssl_ctx->num_sessions, + ssl->ssl_ctx->ssl_sessions, ssl, id_len ? &buf[offset] : NULL); +#endif + + /* get the client random data */ + offset += id_len; + + /* random can be anywhere between 16 and 32 bytes long - so it is padded + * with 0's to the left */ + if (ch_len == 0x10) + { + random_offset += 0x10; + } + + memcpy(&ssl->dc->client_random[random_offset], &buf[offset], ch_len); + ret = send_server_hello_sequence(ssl); + +error: + return ret; +} +#endif + +/* + * Send the entire server hello sequence + */ +static int send_server_hello_sequence(SSL *ssl) +{ + int ret; + + if ((ret = send_server_hello(ssl)) == SSL_OK) + { +#ifndef CONFIG_SSL_SKELETON_MODE + /* resume handshake? */ + if (IS_SET_SSL_FLAG(SSL_SESSION_RESUME)) + { + if ((ret = send_change_cipher_spec(ssl)) == SSL_OK) + { + ret = send_finished(ssl); + ssl->next_state = HS_FINISHED; + } + } + else +#endif + if ((ret = send_certificate(ssl)) == SSL_OK) + { +#ifdef CONFIG_SSL_CERT_VERIFICATION + /* ask the client for its certificate */ + if (IS_SET_SSL_FLAG(SSL_CLIENT_AUTHENTICATION)) + { + if ((ret = send_certificate_request(ssl)) == SSL_OK) + { + ret = send_server_hello_done(ssl); + ssl->next_state = HS_CERTIFICATE; + } + } + else +#endif + { + ret = send_server_hello_done(ssl); + ssl->next_state = HS_CLIENT_KEY_XCHG; + } + } + } + + return ret; +} + +/* + * Send a server hello message. + */ +static int send_server_hello(SSL *ssl) +{ + uint8_t *buf = ssl->bm_data; + int offset = 0; + + buf[0] = HS_SERVER_HELLO; + buf[1] = 0; + buf[2] = 0; + /* byte 3 is calculated later */ + buf[4] = 0x03; + buf[5] = ssl->version & 0x0f; + + /* server random value */ + get_random(SSL_RANDOM_SIZE, &buf[6]); + memcpy(ssl->dc->server_random, &buf[6], SSL_RANDOM_SIZE); + offset = 6 + SSL_RANDOM_SIZE; + +#ifndef CONFIG_SSL_SKELETON_MODE + if (IS_SET_SSL_FLAG(SSL_SESSION_RESUME)) + { + /* retrieve id from session cache */ + buf[offset++] = SSL_SESSION_ID_SIZE; + memcpy(&buf[offset], ssl->session->session_id, SSL_SESSION_ID_SIZE); + memcpy(ssl->session_id, ssl->session->session_id, SSL_SESSION_ID_SIZE); + ssl->sess_id_size = SSL_SESSION_ID_SIZE; + offset += SSL_SESSION_ID_SIZE; + } + else /* generate our own session id */ +#endif + { +#ifndef CONFIG_SSL_SKELETON_MODE + buf[offset++] = SSL_SESSION_ID_SIZE; + get_random(SSL_SESSION_ID_SIZE, &buf[offset]); + memcpy(ssl->session_id, &buf[offset], SSL_SESSION_ID_SIZE); + ssl->sess_id_size = SSL_SESSION_ID_SIZE; + + /* store id in session cache */ + if (ssl->ssl_ctx->num_sessions) + { + memcpy(ssl->session->session_id, + ssl->session_id, SSL_SESSION_ID_SIZE); + } + + offset += SSL_SESSION_ID_SIZE; +#else + buf[offset++] = 0; /* don't bother with session id in skelton mode */ +#endif + } + + buf[offset++] = 0; /* cipher we are using */ + buf[offset++] = ssl->cipher; + buf[offset++] = 0; /* no compression */ + buf[3] = offset - 4; /* handshake size */ + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, NULL, offset); +} + +/* + * Send the server hello done message. + */ +static int send_server_hello_done(SSL *ssl) +{ + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, + g_hello_done, sizeof(g_hello_done)); +} + +/* + * Pull apart a client key exchange message. Decrypt the pre-master key (using + * our RSA private key) and then work out the master key. Initialise the + * ciphers. + */ +static int process_client_key_xchg(SSL *ssl) +{ + uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index]; + int pkt_size = ssl->bm_index; + int premaster_size, secret_length = (buf[2] << 8) + buf[3]; + uint8_t premaster_secret[MAX_KEY_BYTE_SIZE]; + RSA_CTX *rsa_ctx = ssl->ssl_ctx->rsa_ctx; + int offset = 4; + int ret = SSL_OK; + + if (rsa_ctx == NULL) + { + ret = SSL_ERROR_NO_CERT_DEFINED; + goto error; + } + + /* is there an extra size field? */ + if ((secret_length - 2) == rsa_ctx->num_octets) + offset += 2; + + PARANOIA_CHECK(pkt_size, rsa_ctx->num_octets+offset); + + /* rsa_ctx->bi_ctx is not thread-safe */ + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + premaster_size = RSA_decrypt(rsa_ctx, &buf[offset], premaster_secret, 1); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + if (premaster_size != SSL_SECRET_SIZE || + premaster_secret[0] != 0x03 || /* must be the same as client + offered version */ + premaster_secret[1] != (ssl->client_version & 0x0f)) + { + /* guard against a Bleichenbacher attack */ + get_random(SSL_SECRET_SIZE, premaster_secret); + /* and continue - will die eventually when checking the mac */ + } + +#if 0 + print_blob("pre-master", premaster_secret, SSL_SECRET_SIZE); +#endif + + generate_master_secret(ssl, premaster_secret); + +#ifdef CONFIG_SSL_CERT_VERIFICATION + ssl->next_state = IS_SET_SSL_FLAG(SSL_CLIENT_AUTHENTICATION) ? + HS_CERT_VERIFY : HS_FINISHED; +#else + ssl->next_state = HS_FINISHED; +#endif + + ssl->dc->bm_proc_index += rsa_ctx->num_octets+offset; +error: + return ret; +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +static const uint8_t g_cert_request[] = { HS_CERT_REQ, 0, 0, 4, 1, 0, 0, 0 }; + +/* + * Send the certificate request message. + */ +static int send_certificate_request(SSL *ssl) +{ + return send_packet(ssl, PT_HANDSHAKE_PROTOCOL, + g_cert_request, sizeof(g_cert_request)); +} + +/* + * Ensure the client has the private key by first decrypting the packet and + * then checking the packet digests. + */ +static int process_cert_verify(SSL *ssl) +{ + uint8_t *buf = &ssl->bm_data[ssl->dc->bm_proc_index]; + int pkt_size = ssl->bm_index; + uint8_t dgst_buf[MAX_KEY_BYTE_SIZE]; + uint8_t dgst[MD5_SIZE+SHA1_SIZE]; + X509_CTX *x509_ctx = ssl->x509_ctx; + int ret = SSL_OK; + int n; + + PARANOIA_CHECK(pkt_size, x509_ctx->rsa_ctx->num_octets+6); + DISPLAY_RSA(ssl, x509_ctx->rsa_ctx); + + /* rsa_ctx->bi_ctx is not thread-safe */ + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); + n = RSA_decrypt(x509_ctx->rsa_ctx, &buf[6], dgst_buf, 0); + SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); + + if (n != SHA1_SIZE + MD5_SIZE) + { + ret = SSL_ERROR_INVALID_KEY; + goto end_cert_vfy; + } + + finished_digest(ssl, NULL, dgst); /* calculate the digest */ + if (memcmp(dgst_buf, dgst, MD5_SIZE + SHA1_SIZE)) + { + ret = SSL_ERROR_INVALID_KEY; + } + +end_cert_vfy: + ssl->next_state = HS_FINISHED; +error: + return ret; +} + +#endif diff --git a/ccast/axTLS/version.h b/ccast/axTLS/version.h new file mode 100644 index 0000000..e8158cc --- /dev/null +++ b/ccast/axTLS/version.h @@ -0,0 +1 @@ +#define AXTLS_VERSION "1.4.9" diff --git a/ccast/axTLS/x509.c b/ccast/axTLS/x509.c new file mode 100644 index 0000000..c2ca49f --- /dev/null +++ b/ccast/axTLS/x509.c @@ -0,0 +1,561 @@ +/* + * Copyright (c) 2007, Cameron Rich + * + * 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 the axTLS project 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. + */ + +/** + * @file x509.c + * + * Certificate processing. + */ + +#include +#include +#include +#include +#include "os_port.h" +#include "crypto_misc.h" + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Retrieve the signature from a certificate. + */ +static const uint8_t *get_signature(const uint8_t *asn1_sig, int *len) +{ + int offset = 0; + const uint8_t *ptr = NULL; + + if (asn1_next_obj(asn1_sig, &offset, ASN1_SEQUENCE) < 0 || + asn1_skip_obj(asn1_sig, &offset, ASN1_SEQUENCE)) + goto end_get_sig; + + if (asn1_sig[offset++] != ASN1_OCTET_STRING) + goto end_get_sig; + *len = get_asn1_length(asn1_sig, &offset); + ptr = &asn1_sig[offset]; /* all ok */ + +end_get_sig: + return ptr; +} + +#endif + +/** + * Construct a new x509 object. + * @return 0 if ok. < 0 if there was a problem. + */ +int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx) +{ + int begin_tbs, end_tbs; + int ret = X509_NOT_OK, offset = 0, cert_size = 0; + X509_CTX *x509_ctx; + BI_CTX *bi_ctx; + + *ctx = (X509_CTX *)calloc(1, sizeof(X509_CTX)); + x509_ctx = *ctx; + + /* get the certificate size */ + asn1_skip_obj(cert, &cert_size, ASN1_SEQUENCE); + + if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) + goto end_cert; + + begin_tbs = offset; /* start of the tbs */ + end_tbs = begin_tbs; /* work out the end of the tbs */ + asn1_skip_obj(cert, &end_tbs, ASN1_SEQUENCE); + + if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) + goto end_cert; + + if (cert[offset] == ASN1_EXPLICIT_TAG) /* optional version */ + { + if (asn1_version(cert, &offset, x509_ctx)) + goto end_cert; + } + + if (asn1_skip_obj(cert, &offset, ASN1_INTEGER) || /* serial number */ + asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) + goto end_cert; + + /* make sure the signature is ok */ + if (asn1_signature_type(cert, &offset, x509_ctx)) + { + ret = X509_VFY_ERROR_UNSUPPORTED_DIGEST; + goto end_cert; + } + + if (asn1_name(cert, &offset, x509_ctx->ca_cert_dn) || + asn1_validity(cert, &offset, x509_ctx) || + asn1_name(cert, &offset, x509_ctx->cert_dn) || + asn1_public_key(cert, &offset, x509_ctx)) + { + goto end_cert; + } + + bi_ctx = x509_ctx->rsa_ctx->bi_ctx; + +#ifdef CONFIG_SSL_CERT_VERIFICATION /* only care if doing verification */ + /* use the appropriate signature algorithm (SHA1/MD5/MD2) */ + if (x509_ctx->sig_type == SIG_TYPE_MD5) + { + MD5_CTX md5_ctx; + uint8_t md5_dgst[MD5_SIZE]; + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, &cert[begin_tbs], end_tbs-begin_tbs); + MD5_Final(md5_dgst, &md5_ctx); + x509_ctx->digest = bi_import(bi_ctx, md5_dgst, MD5_SIZE); + } + else if (x509_ctx->sig_type == SIG_TYPE_SHA1) + { + SHA1_CTX sha_ctx; + uint8_t sha_dgst[SHA1_SIZE]; + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, &cert[begin_tbs], end_tbs-begin_tbs); + SHA1_Final(sha_dgst, &sha_ctx); + x509_ctx->digest = bi_import(bi_ctx, sha_dgst, SHA1_SIZE); + } + else if (x509_ctx->sig_type == SIG_TYPE_MD2) + { + MD2_CTX md2_ctx; + uint8_t md2_dgst[MD2_SIZE]; + MD2_Init(&md2_ctx); + MD2_Update(&md2_ctx, &cert[begin_tbs], end_tbs-begin_tbs); + MD2_Final(md2_dgst, &md2_ctx); + x509_ctx->digest = bi_import(bi_ctx, md2_dgst, MD2_SIZE); + } + + if (cert[offset] == ASN1_V3_DATA) + { + int suboffset; + + ++offset; + get_asn1_length(cert, &offset); + + if ((suboffset = asn1_find_subjectaltname(cert, offset)) > 0) + { + if (asn1_next_obj(cert, &suboffset, ASN1_OCTET_STRING) > 0) + { + int altlen; + + if ((altlen = asn1_next_obj(cert, + &suboffset, ASN1_SEQUENCE)) > 0) + { + int endalt = suboffset + altlen; + int totalnames = 0; + + while (suboffset < endalt) + { + int type = cert[suboffset++]; + int dnslen = get_asn1_length(cert, &suboffset); + + if (type == ASN1_CONTEXT_DNSNAME) + { + x509_ctx->subject_alt_dnsnames = (char**) + realloc(x509_ctx->subject_alt_dnsnames, + (totalnames + 2) * sizeof(char*)); + x509_ctx->subject_alt_dnsnames[totalnames] = + (char*)malloc(dnslen + 1); + x509_ctx->subject_alt_dnsnames[totalnames+1] = NULL; + memcpy(x509_ctx->subject_alt_dnsnames[totalnames], + cert + suboffset, dnslen); + x509_ctx->subject_alt_dnsnames[ + totalnames][dnslen] = 0; + ++totalnames; + } + + suboffset += dnslen; + } + } + } + } + } + + offset = end_tbs; /* skip the rest of v3 data */ + if (asn1_skip_obj(cert, &offset, ASN1_SEQUENCE) || + asn1_signature(cert, &offset, x509_ctx)) + goto end_cert; +#endif + ret = X509_OK; +end_cert: + if (len) + { + *len = cert_size; + } + + if (ret) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("Error: Invalid X509 ASN.1 file (%s)\n", + x509_display_error(ret)); +#endif + x509_free(x509_ctx); + *ctx = NULL; + } + + return ret; +} + +/** + * Free an X.509 object's resources. + */ +void x509_free(X509_CTX *x509_ctx) +{ + X509_CTX *next; + int i; + + if (x509_ctx == NULL) /* if already null, then don't bother */ + return; + + for (i = 0; i < X509_NUM_DN_TYPES; i++) + { + free(x509_ctx->ca_cert_dn[i]); + free(x509_ctx->cert_dn[i]); + } + + free(x509_ctx->signature); + +#ifdef CONFIG_SSL_CERT_VERIFICATION + if (x509_ctx->digest) + { + bi_free(x509_ctx->rsa_ctx->bi_ctx, x509_ctx->digest); + } + + if (x509_ctx->subject_alt_dnsnames) + { + for (i = 0; x509_ctx->subject_alt_dnsnames[i]; ++i) + free(x509_ctx->subject_alt_dnsnames[i]); + + free(x509_ctx->subject_alt_dnsnames); + } +#endif + + RSA_free(x509_ctx->rsa_ctx); + next = x509_ctx->next; + free(x509_ctx); + x509_free(next); /* clear the chain */ +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Take a signature and decrypt it. + */ +static bigint *sig_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, + bigint *modulus, bigint *pub_exp) +{ + int i, size; + bigint *decrypted_bi, *dat_bi; + bigint *bir = NULL; + uint8_t *block = (uint8_t *)malloc(sig_len); + + /* decrypt */ + dat_bi = bi_import(ctx, sig, sig_len); + ctx->mod_offset = BIGINT_M_OFFSET; + + /* convert to a normal block */ + decrypted_bi = bi_mod_power2(ctx, dat_bi, modulus, pub_exp); + + bi_export(ctx, decrypted_bi, block, sig_len); + ctx->mod_offset = BIGINT_M_OFFSET; + + i = 10; /* start at the first possible non-padded byte */ + while (block[i++] && i < sig_len); + size = sig_len - i; + + /* get only the bit we want */ + if (size > 0) + { + int len; + const uint8_t *sig_ptr = get_signature(&block[i], &len); + + if (sig_ptr) + { + bir = bi_import(ctx, sig_ptr, len); + } + } + + /* save a few bytes of memory */ + bi_clear_cache(ctx); + free(block); + return bir; +} + +/** + * Do some basic checks on the certificate chain. + * + * Certificate verification consists of a number of checks: + * - The date of the certificate is after the start date. + * - The date of the certificate is before the finish date. + * - A root certificate exists in the certificate store. + * - That the certificate(s) are not self-signed. + * - The certificate chain is valid. + * - The signature of the certificate is valid. + */ +int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) +{ + int ret = X509_OK, i = 0; + bigint *cert_sig; + X509_CTX *next_cert = NULL; + BI_CTX *ctx = NULL; + bigint *mod = NULL, *expn = NULL; + int match_ca_cert = 0; + struct timeval tv; + uint8_t is_self_signed = 0; + + if (cert == NULL) + { + ret = X509_VFY_ERROR_NO_TRUSTED_CERT; + goto end_verify; + } + + /* a self-signed certificate that is not in the CA store - use this + to check the signature */ + if (asn1_compare_dn(cert->ca_cert_dn, cert->cert_dn) == 0) + { + is_self_signed = 1; + ctx = cert->rsa_ctx->bi_ctx; + mod = cert->rsa_ctx->m; + expn = cert->rsa_ctx->e; + } + + gettimeofday(&tv, NULL); + + /* check the not before date */ + if (tv.tv_sec < cert->not_before) + { + ret = X509_VFY_ERROR_NOT_YET_VALID; + goto end_verify; + } + + /* check the not after date */ + if (tv.tv_sec > cert->not_after) + { + ret = X509_VFY_ERROR_EXPIRED; + goto end_verify; + } + + next_cert = cert->next; + + /* last cert in the chain - look for a trusted cert */ + if (next_cert == NULL) + { + if (ca_cert_ctx != NULL) + { + /* go thu the CA store */ + while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) + { + if (asn1_compare_dn(cert->ca_cert_dn, + ca_cert_ctx->cert[i]->cert_dn) == 0) + { + /* use this CA certificate for signature verification */ + match_ca_cert = 1; + ctx = ca_cert_ctx->cert[i]->rsa_ctx->bi_ctx; + mod = ca_cert_ctx->cert[i]->rsa_ctx->m; + expn = ca_cert_ctx->cert[i]->rsa_ctx->e; + break; + } + + i++; + } + } + + /* couldn't find a trusted cert (& let self-signed errors + be returned) */ + if (!match_ca_cert && !is_self_signed) + { + ret = X509_VFY_ERROR_NO_TRUSTED_CERT; + goto end_verify; + } + } + else if (asn1_compare_dn(cert->ca_cert_dn, next_cert->cert_dn) != 0) + { + /* check the chain */ + ret = X509_VFY_ERROR_INVALID_CHAIN; + goto end_verify; + } + else /* use the next certificate in the chain for signature verify */ + { + ctx = next_cert->rsa_ctx->bi_ctx; + mod = next_cert->rsa_ctx->m; + expn = next_cert->rsa_ctx->e; + } + + /* cert is self signed */ + if (!match_ca_cert && is_self_signed) + { + ret = X509_VFY_ERROR_SELF_SIGNED; + goto end_verify; + } + + /* check the signature */ + cert_sig = sig_verify(ctx, cert->signature, cert->sig_len, + bi_clone(ctx, mod), bi_clone(ctx, expn)); + + if (cert_sig && cert->digest) + { + if (bi_compare(cert_sig, cert->digest) != 0) + ret = X509_VFY_ERROR_BAD_SIGNATURE; + + + bi_free(ctx, cert_sig); + } + else + { + ret = X509_VFY_ERROR_BAD_SIGNATURE; + } + + if (ret) + goto end_verify; + + /* go down the certificate chain using recursion. */ + if (next_cert != NULL) + { + ret = x509_verify(ca_cert_ctx, next_cert); + } + +end_verify: + return ret; +} +#endif + +#if defined (CONFIG_SSL_FULL_MODE) +/** + * Used for diagnostics. + */ +static const char *not_part_of_cert = ""; +void x509_print(const X509_CTX *cert, CA_CERT_CTX *ca_cert_ctx) +{ + if (cert == NULL) + return; + + printf("=== CERTIFICATE ISSUED TO ===\n"); + printf("Common Name (CN):\t\t"); + printf("%s\n", cert->cert_dn[X509_COMMON_NAME] ? + cert->cert_dn[X509_COMMON_NAME] : not_part_of_cert); + + printf("Organization (O):\t\t"); + printf("%s\n", cert->cert_dn[X509_ORGANIZATION] ? + cert->cert_dn[X509_ORGANIZATION] : not_part_of_cert); + + printf("Organizational Unit (OU):\t"); + printf("%s\n", cert->cert_dn[X509_ORGANIZATIONAL_UNIT] ? + cert->cert_dn[X509_ORGANIZATIONAL_UNIT] : not_part_of_cert); + + printf("=== CERTIFICATE ISSUED BY ===\n"); + printf("Common Name (CN):\t\t"); + printf("%s\n", cert->ca_cert_dn[X509_COMMON_NAME] ? + cert->ca_cert_dn[X509_COMMON_NAME] : not_part_of_cert); + + printf("Organization (O):\t\t"); + printf("%s\n", cert->ca_cert_dn[X509_ORGANIZATION] ? + cert->ca_cert_dn[X509_ORGANIZATION] : not_part_of_cert); + + printf("Organizational Unit (OU):\t"); + printf("%s\n", cert->ca_cert_dn[X509_ORGANIZATIONAL_UNIT] ? + cert->ca_cert_dn[X509_ORGANIZATIONAL_UNIT] : not_part_of_cert); + + printf("Not Before:\t\t\t%s", ctime(&cert->not_before)); + printf("Not After:\t\t\t%s", ctime(&cert->not_after)); + printf("RSA bitsize:\t\t\t%d\n", cert->rsa_ctx->num_octets*8); + printf("Sig Type:\t\t\t"); + switch (cert->sig_type) + { + case SIG_TYPE_MD5: + printf("MD5\n"); + break; + case SIG_TYPE_SHA1: + printf("SHA1\n"); + break; + case SIG_TYPE_MD2: + printf("MD2\n"); + break; + default: + printf("Unrecognized: %d\n", cert->sig_type); + break; + } + + if (ca_cert_ctx) + { + printf("Verify:\t\t\t\t%s\n", + x509_display_error(x509_verify(ca_cert_ctx, cert))); + } + +#if 0 + print_blob("Signature", cert->signature, cert->sig_len); + bi_print("Modulus", cert->rsa_ctx->m); + bi_print("Pub Exp", cert->rsa_ctx->e); +#endif + + if (ca_cert_ctx) + { + x509_print(cert->next, ca_cert_ctx); + } + + TTY_FLUSH(); +} + +#endif /* CONFIG_SSL_FULL_MODE */ + +const char * x509_display_error(int error) +{ + switch (error) + { + case X509_OK: + return "Certificate verify successful"; + + case X509_NOT_OK: + return "X509 not ok"; + + case X509_VFY_ERROR_NO_TRUSTED_CERT: + return "No trusted cert is available"; + + case X509_VFY_ERROR_BAD_SIGNATURE: + return "Bad signature"; + + case X509_VFY_ERROR_NOT_YET_VALID: + return "Cert is not yet valid"; + + case X509_VFY_ERROR_EXPIRED: + return "Cert has expired"; + + case X509_VFY_ERROR_SELF_SIGNED: + return "Cert is self-signed"; + + case X509_VFY_ERROR_INVALID_CHAIN: + return "Chain is invalid (check order of certs)"; + + case X509_VFY_ERROR_UNSUPPORTED_DIGEST: + return "Unsupported digest"; + + case X509_INVALID_PRIV_KEY: + return "Invalid private key"; + + default: + return "Unknown"; + } +} + diff --git a/ccast/cast_channel.proto b/ccast/cast_channel.proto new file mode 100644 index 0000000..d321eaf --- /dev/null +++ b/ccast/cast_channel.proto @@ -0,0 +1,79 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package extensions.api.cast_channel; + +message CastMessage { + // Always pass a version of the protocol for future compatibility + // requirements. + enum ProtocolVersion { + CASTV2_1_0 = 0; + } + required ProtocolVersion protocol_version = 1; + + // source and destination ids identify the origin and destination of the + // message. They are used to route messages between endpoints that share a + // device-to-device channel. + // + // For messages between applications: + // - The sender application id is a unique identifier generated on behalf of + // the sender application. + // - The receiver id is always the the session id for the application. + // + // For messages to or from the sender or receiver platform, the special ids + // 'sender-0' and 'receiver-0' can be used. + // + // For messages intended for all endpoints using a given channel, the + // wildcard destination_id '*' can be used. + required string source_id = 2; + required string destination_id = 3; + + // This is the core multiplexing key. All messages are sent on a namespace + // and endpoints sharing a channel listen on one or more namespaces. The + // namespace defines the protocol and semantics of the message. + required string namespace = 4; + + // Encoding and payload info follows. + + // What type of data do we have in this message. + enum PayloadType { + STRING = 0; + BINARY = 1; + } + required PayloadType payload_type = 5; + + // Depending on payload_type, exactly one of the following optional fields + // will always be set. + optional string payload_utf8 = 6; + optional bytes payload_binary = 7; +} + +// Messages for authentication protocol between a sender and a receiver. +message AuthChallenge { +} + +message AuthResponse { + required bytes signature = 1; + required bytes client_auth_certificate = 2; +} + +message AuthError { + enum ErrorType { + INTERNAL_ERROR = 0; + NO_TLS = 1; // The underlying connection is not TLS + } + required ErrorType error_type = 1; +} + +message DeviceAuthMessage { + // Request fields + optional AuthChallenge challenge = 1; + // Response fields + optional AuthResponse response = 2; + optional AuthError error = 3; +} diff --git a/ccast/ccast.c b/ccast/ccast.c new file mode 100644 index 0000000..5c1ed7b --- /dev/null +++ b/ccast/ccast.c @@ -0,0 +1,1319 @@ + +/* + * Argyll Color Correction System + * ChromCast support. + * + * Author: Graeme W. Gill + * Date: 10/9/2014 + * + * Copyright 2014 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License2.txt file for licencing details. + * + */ + +#include +#include +#include +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#ifndef SALONEINSTLIB +#include "numlib.h" +#else +#include "numsup.h" +#endif +#include "yajl.h" +#include "conv.h" +#include "base64.h" +#include "ccpacket.h" +#include "ccmes.h" +#include "ccast.h" + +#undef DEBUG +#undef CHECK_JSON + +#ifdef DEBUG +# define dbgo stdout +# define DBG(xxx) fprintf xxx ; +void cc_dump_bytes(FILE *fp, char *pfx, unsigned char *buf, int len); +#else +# define DBG(xxx) ; +#endif /* DEBUG */ + +#define START_TRIES 6 +#define LOAD_TRIES 4 + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef CHECK_JSON + +/* Check if JSON is invalid */ +/* Exits if invalid */ +static void check_json(char *mesbuf) { + yajl_val node; + char errbuf[1024]; + + if ((node = yajl_tree_parse(mesbuf, errbuf, sizeof(errbuf))) == NULL) { + fprintf(dbgo,"yajl_tree_parse of send message failed with '%s'\n",errbuf); + fprintf(dbgo,"JSON = '%s'\n",mesbuf); + exit(1); + } + yajl_tree_free(node); +} +#endif + +/* ============================================================ */ +/* Receive thread */ + +/* Read messages. If they are ones we deal with, send a reply */ +/* If they are anonomous (sessionId == 0), then ignore them */ +/* (Could save last known anonomous message if they prove useful) */ +/* and if they are numbered, keep then in a sorted list. */ + +static int cc_rec_thread(void *context) { + ccast *p = (ccast *)context; + ccmessv *sv = p->messv; + ccmessv_err merr; + ccmes mes, smes; + int errc = 0; + char errbuf[1024]; + yajl_val tyn, idn; + int rv = 0; + + DBG((dbgo,"ccthread starting\n")) + + ccmes_init(&mes); + ccmes_init(&smes); + + /* Preset PONG message */ + smes.source_id = "sender-0"; + smes.destination_id = "receiver-0"; + smes.namespace = "urn:x-cast:com.google.cast.tp.heartbeat"; + smes.binary = 0; + smes.data = (ORD8 *)"{ \"type\": \"PONG\" }"; + + for(;!p->stop;) { + + if ((merr = sv->receive(sv, &mes)) != ccmessv_OK) { + if (merr == ccmessv_timeout) { + DBG((dbgo,"ccthread: got receive timeout (OK)\n")) + msec_sleep(100); + } else { + DBG((dbgo,"ccthread: messv->receive failed with '%s'\n",ccmessv_emes(merr))) + msec_sleep(100); +#ifdef NEVER +//This won't work - we need to re-join the session etc., + if (p->messv->pk->reconnect(p->messv->pk)) { + DBG((dbgo,"ccthread: reconnect after error failed\n")) + rv = 1; + break; + } +#endif + if (errc++ > 20) { /* Too many failures */ + DBG((dbgo,"ccthread: too many errors - giving up\n")) + /* Hmm. The connection seems to have gone down ? */ + rv = 1; + break; + } + } + continue; + } + + errc = 0; + + /* Got status query */ + if (mes.mtype != NULL && strcmp(mes.mtype, "CLOSE") == 0) { + /* Hmm. That indicates an error */ + + DBG((dbgo,"ccthread: got CLOSE message - giving up\n")) + rv = 1; + break; + + } else if (mes.mtype != NULL && strcmp(mes.mtype, "PING") == 0) { + if ((merr = sv->send(sv, &smes)) != ccmessv_OK) { + DBG((dbgo,"ccthread: send PONG failed with '%s'\n",ccmessv_emes(merr))) + } + + /* Got reply - add to linked list */ + } else { + int found; +#ifdef DEBUG + if (p->w_rq) { + DBG((dbgo,"ccthread: waiting for ns '%s' and mes->ns '%s'\n", + p->w_rqns, mes.namespace)) + DBG((dbgo,"ccthread: waiting for id %d and mes->id %d\n", + p->w_rqid, mes.rqid)) + } else { + DBG((dbgo,"ccthread: has no client waiting\n")) + } +#endif + + /* Is it the one the client is waiting for ? */ + found = (p->w_rq != 0 + && (p->w_rqns == NULL || strcmp(p->w_rqns, mes.namespace) == 0) + && (p->w_rqid == 0 || p->w_rqid == mes.rqid)); + + if (found || mes.rqid != 0) { + ccmes *nmes; + if ((nmes = (ccmes *)calloc(1, sizeof(ccmes))) == NULL) { + DBG((dbgo,"ccthread: calloc failed\n")) + } else { + ccmes_transfer(nmes, &mes); + + DBG((dbgo,"ccthread: adding message type '%s' id %d to list (found %d)\n",nmes->mtype,nmes->rqid,found)) + amutex_lock(p->rlock); /* We're modifying p->rmes list */ + nmes->next = p->rmes; /* Put at start of list */ + p->rmes = nmes; + + /* Client is waiting for this */ + if (found) { + DBG((dbgo,"ccthread: client was waiting for this message\n")) + acond_signal(p->rcond); + } + amutex_unlock(p->rlock); /* We've finished modifying p->rmes list */ + } + } + } + + /* Got anonomous status message */ + ccmes_empty(&mes); + } + DBG((dbgo, "ccthread: about to exit - stop = %d\n",p->stop)) + + /* We're bailing out or stopping */ + p->stopped = 1; + + /* Release client if it was waiting */ + amutex_lock(p->rlock); + if (p->w_rq != 0) { + DBG((dbgo,"ccthread: client was waiting for message - abort it\n")) + acond_signal(p->rcond); + } + amutex_unlock(p->rlock); + + DBG((dbgo,"ccthread returning %d\n",rv)) + + return rv; +} + +/* Wait for a specific message rqid on a specific channel. */ +/* Use rqid = 0 to get first message rather than specific one. */ +/* Use namespace = NULL to ignore channel */ +/* Return 1 on an error */ +/* Return 2 on a timeout */ +static int get_a_reply_id(ccast *p, char *namespace, int rqid, ccmes *rmes, int to) { + ccmes *nlist = NULL; + ccmes *mes, *xmes, *fmes = NULL; + int rv = 0; + + DBG((dbgo," get_a_reply_id getting namespace '%s' id %d\n", + namespace == NULL ? "(none)" : namespace, rqid)) + + amutex_lock(p->rlock); /* We're modifying p->rmes list */ + + if (p->stop || p->stopped) { + amutex_unlock(p->rlock); /* Allow thread to modify p->rmes list */ + DBG((dbgo," get_a_reply_id: thread is stopping or stopped\n")) + return 1; + } + + /* Setup request to thread */ + p->w_rq = 1; + p->w_rqns = namespace; + p->w_rqid = rqid; + + /* Until we've got our message, we time out, or the thread is being stopped */ + for (;!p->stop && !p->stopped;) { + + /* Check if the message has already been received */ + for (mes = p->rmes; mes != NULL; mes = xmes) { + int ins = (namespace == NULL || strcmp(namespace, mes->namespace) == 0); + xmes = mes->next; + if (ins && rqid != 0 && mes->rqid < rqid) { + ccmes_del(mes); /* Too old - throw away */ + } else if (ins && (rqid == 0 || mes->rqid == rqid)) { + fmes = mes; /* The one we want */ + } else { + mes->next = nlist; /* Keep in list */ + nlist = mes; + } + } + p->rmes = nlist; + + if (fmes != NULL) + break; /* Got it */ + + +#ifndef NEVER + /* We need to wait until it turns up */ + /* Allow thread to modify p->rmes list and signal us */ + if (acond_timedwait(p->rcond, p->rlock, to) != 0) { + DBG((dbgo," get_a_reply_id timed out after %f secs\n",to/1000.0)) + rv = 2; + break; + } +#else + acond_wait(p->rcond, p->rlock); +#endif + DBG((dbgo," get_a_reply_id got released\n")) + } + p->w_rq = 0; /* We're not looking for anything now */ + amutex_unlock(p->rlock); /* Allow thread to modify p->rmes list */ + + if (p->stop || p->stopped) { + DBG((dbgo," get_a_reply_id failed because thread is stopped or stopping\n")) + ccmes_init(rmes); + return 1; + } + + if (rv != 0) { + DBG((dbgo," get_a_reply_id returning error %d\n",rv)) + } else { + ccmes_transfer(rmes, fmes); + DBG((dbgo," get_a_reply_id returning type '%s' id %d\n",rmes->mtype,rmes->rqid)) + } + + return rv; +} + +/* ============================================================ */ + +void ccast_delete_from_cleanup_list(ccast *p); + +/* Cleanup any created objects */ +static void cleanup_ccast(ccast *p) { + + DBG((dbgo," cleanup_ccast() called\n")) + + p->stop = 1; /* Tell the thread to exit */ + + /* Wait for thread (could use semaphore) */ + /* and then delete it */ + if (p->rmesth != NULL) { + + while (!p->stopped) { + msec_sleep(10); + } + p->rmesth->del(p->rmesth); + p->rmesth = NULL; + } + + if (p->sessionId != NULL) { + free(p->sessionId); + p->sessionId = NULL; + } + + if (p->transportId != NULL) { + free(p->transportId); + p->transportId = NULL; + } + + p->mediaSessionId = 0; + + if (p->messv != NULL) { + p->messv->del(p->messv); + p->messv = NULL; + } + + /* Clean up linked list */ + { + ccmes *mes, *xmes; + for (mes = p->rmes; mes != NULL; mes = xmes) { + xmes = mes->next; + ccmes_del(mes); + } + p->rmes = NULL; + } +} + +/* Shut down the connection, in such a way that we can */ +/* try and re-connect. */ +static void shutdown_ccast(ccast *p) { + ccmes mes; + + DBG((dbgo," shutdown_ccast() called\n")) + + ccmes_init(&mes); + + p->stop = 1; /* Tell the thread to exit */ + + /* Close the media channel */ + if (p->transportId != NULL && p->messv != NULL) { + mes.source_id = "sender-0"; + mes.destination_id = p->transportId; + mes.namespace = "urn:x-cast:com.google.cast.tp.connection"; + mes.binary = 0; + mes.data = (ORD8 *)"{ \"type\": \"CLOSE\" }"; + p->messv->send(p->messv, &mes); + } + + /* Stop the application */ + if (p->sessionId != NULL && p->messv != NULL) { + int reqid = ++p->requestId; + char mesbuf[1024]; + sprintf(mesbuf, "{ \"requestId\": %d, \"type\": \"STOP\", \"sessionId\": \"%s\" }", + reqid, p->sessionId); + mes.source_id = "sender-0"; + mes.destination_id = "receiver-0"; + mes.namespace = "urn:x-cast:com.google.cast.receiver"; + mes.binary = 0; + mes.data = (ORD8 *)mesbuf; + p->messv->send(p->messv, &mes); + } + + /* Close the platform channel */ + if (p->messv != NULL) { + mes.source_id = "sender-0"; + mes.destination_id = "receiver-0"; + mes.namespace = "urn:x-cast:com.google.cast.receiver"; + mes.binary = 0; + mes.data = (ORD8 *)"{ \"type\": \"CLOSE\" }"; + p->messv->send(p->messv, &mes); + } + + cleanup_ccast(p); +} + +static void del_ccast(ccast *p) { + if (p != NULL) { + + shutdown_ccast(p); + + amutex_del(p->rlock); + acond_del(p->rcond); + + free(p); + } +} + +static int load_ccast(ccast *p, char *url, unsigned char *ibuf, size_t ilen, + double bg[3], double x, double y, double w, double h); +void ccast_install_signal_handlers(ccast *p); + +/* Startup a ChromCast session */ +/* Return nz on error */ +static int start_ccast(ccast *p) { + ccpacket *pk = NULL; + ccpacket_err perr; + ccmessv_err merr; + ccmes mes, rmes; + char mesbuf[1024]; + int reqid, tries, maxtries = START_TRIES; + char *connection_chan = "urn:x-cast:com.google.cast.tp.connection"; + char *heartbeat_chan = "urn:x-cast:com.google.cast.tp.heartbeat"; + char *receiver_chan = "urn:x-cast:com.google.cast.receiver"; + + /* Try this a few times if we fail in some way */ + for (tries = 0; tries < maxtries; tries++) { + int app = 0, naps = 2; + + /* Use the default receiver rather than pattern generator */ + if (p->forcedef || getenv("ARGYLL_CCAST_DEFAULT_RECEIVER") != NULL) + app = 1; + + ccmes_init(&mes); + ccmes_init(&rmes); + + p->stop = 0; + p->stopped = 0; +// p->requestId = 0; + + amutex_init(p->rlock); + acond_init(p->rcond); + + /* Hmm. Could put creation of pk inside new_ccmessv() ? */ + if ((pk = new_ccpacket()) == NULL) { + DBG((dbgo,"start_ccast: new_ccpacket() failed\n")) + goto retry; + } + + if ((perr = pk->connect(pk, p->id.ip, 8009)) != ccpacket_OK) { + DBG((dbgo,"start_ccast: ccpacket connect failed with '%s'\n",ccpacket_emes(perr))) + goto retry; + } + + DBG((dbgo,"Got TLS connection to '%s\n'",p->id.name)) + + if ((p->messv = new_ccmessv(pk)) == NULL) { + DBG((dbgo,"start_ccast: new_ccmessv() failed\n")) + goto retry; + } + pk = NULL; /* Will get deleted with messv now */ + + /* Attempt a connection */ + mes.source_id = "sender-0"; + mes.destination_id = "receiver-0"; + mes.namespace = connection_chan; + mes.binary = 0; + mes.data = (ORD8 *)"{ \"type\": \"CONNECT\" }"; + if ((merr = p->messv->send(p->messv, &mes)) != ccmessv_OK) { + DBG((dbgo,"start_ccast: CONNECT failed with '%s'\n",ccmessv_emes(merr))) + goto retry; + } + + /* Start the thread. */ + /* We don't want to start this until the TLS negotiations and */ + /* the synchronous ssl_readi()'s it uses are complete. */ + if ((p->rmesth = new_athread(cc_rec_thread, (void *)p)) == NULL) { + DBG((dbgo,"start_ccast: creating message thread failed\n")) + goto retry; + } + +#ifdef NEVER + /* Send a ping */ + mes.namespace = heartbeat_chan; + mes.data = (ORD8 *)"{ \"type\": \"PING\" }"; + if ((merr = p->messv->send(p->messv, &mes)) != ccmessv_OK) { + DBG((dbgo,"start_ccast: PING failed with '%s'\n",ccmessv_emes(merr))) + return 1; + } + + /* Wait for a PONG */ +// get_a_reply(p->messv, NULL); +#endif + + /* Try and find an app we can work with */ + for (; app < naps; app++) { + char *appid; + + reqid = ++p->requestId; + + if (app == 0) { + appid = "B5C2CBFC"; /* Pattern generator reciever */ + p->patgenrcv = 1; + p->load_delay = 350.0; + } else { + appid = "CC1AD845"; /* Default Receiver */ + p->patgenrcv = 0; + p->load_delay = 1500.0; /* Actually about 600msec, but fade produces a soft */ + /* transition that instrument meas_delay() doesn't cope with accurately. */ + } + + /* Attempt to launch the Default receiver */ + sprintf(mesbuf, "{ \"requestId\": %d, \"type\": \"LAUNCH\", \"appId\": \"%s\" }", + reqid, appid); + + DBG((dbgo,"start_ccast: about to do LAUNCH\n")) + /* Launch the default application */ + /* (Presumably we would use the com.google.cast.receiver channel */ + /* for monitoring and controlling the reciever) */ + mes.namespace = receiver_chan; + mes.data = (ORD8 *)mesbuf; + if ((merr = p->messv->send(p->messv, &mes)) != ccmessv_OK) { + DBG((dbgo,"start_ccast: LAUNCH failed with '%s'\n",ccmessv_emes(merr))) + goto retry; + } + + /* Receive the RECEIVER_STATUS status messages until it is ready to cast */ + /* and get the sessionId and transportId */ + /* We get periodic notification messages (requestId=0) as well as */ + /* a response messages to our requestId */ + + /* Wait for a reply to the LAUNCH (15 sec) */ + if (get_a_reply_id(p, receiver_chan, reqid, &rmes, 15000) != 0) { + DBG((dbgo,"start_ccast: LAUNCH failed to get a reply\n")) + goto retry; + } + + if (rmes.mtype != NULL + && strcmp(rmes.mtype, "RECEIVER_STATUS") == 0 + && rmes.tnode != NULL) { + break; /* Launched OK */ + } + + if (rmes.mtype == NULL + || strcmp(rmes.mtype, "LAUNCH_ERROR") != 0 + || rmes.tnode == NULL + || (app+1) >= naps) { + DBG((dbgo,"start_ccast: LAUNCH failed to get a RECEIVER_STATUS or LAUNCH ERROR reply\n")) + ccmes_empty(&rmes); + goto retry; + } + + /* Try the next application */ + } + + DBG((dbgo,"start_ccast: LAUNCH soceeded, load delay = %d msec\n",p->load_delay)) + { + yajl_val idn, tpn; + if ((idn = yajl_tree_get_first(rmes.tnode, "sessionId", yajl_t_string)) == NULL + || (tpn = yajl_tree_get_first(rmes.tnode, "transportId", yajl_t_string)) == NULL) { + DBG((dbgo,"start_ccast: LAUNCH failed to get sessionId & transportId\n")) + ccmes_empty(&rmes); + goto retry; + } + p->sessionId = strdup(YAJL_GET_STRING(idn)); + p->transportId = strdup(YAJL_GET_STRING(tpn)); + if (p->sessionId == NULL || p->transportId == NULL) { + DBG((dbgo,"start_ccast: strdup failed\n")) + ccmes_empty(&rmes); + goto retry; + } + } + ccmes_empty(&rmes); + + DBG((dbgo,"### Got sessionId = '%s', transportId = '%s'\n",p->sessionId, p->transportId)) + + /* Connect up to the reciever media channels */ + mes.destination_id = p->transportId; + mes.namespace = connection_chan; + mes.data = (ORD8 *)"{ \"type\": \"CONNECT\" }"; + if ((merr = p->messv->send(p->messv, &mes)) != ccmessv_OK) { + DBG((dbgo,"messv->send CONNECT failed with '%s'\n",ccmessv_emes(merr))) + goto retry; + } + + // Hmm. Should we wait for a RECEIVER_STATUS message with id 0 here ? + + /* If we get here, we assume that we've suceeded */ + break; + + retry:; + + /* If we get here, we're going around again, so start again from scratch */ + if (pk != NULL) { + pk->del(pk); + pk = NULL; + } + + cleanup_ccast(p); + + } + + if (tries >= maxtries) { + DBG((dbgo,"Failed to start ChromeCast\n")) + return 1; + } + + DBG((dbgo,"Succeeded in starting ChromeCast\n")) + + ccast_install_signal_handlers(p); + + return 0; +} + + + +/* Get the extra load delay */ +static int get_load_delay(ccast *p) { + return p->load_delay; +} + +/* Return nz if we can send PNG directly as base64 + bg RGB, */ +/* else have to setup webserver and send URL */ +static int get_direct_send(ccast *p) { + return p->patgenrcv; +} + +/* Create a new ChromeCast */ +/* Return NULL on error */ +ccast *new_ccast(ccast_id *id, +int forcedef) { + ccast *p = NULL; + + if ((p = (ccast *)calloc(1, sizeof(ccast))) == NULL) { + DBG((dbgo, "new_ccast: calloc failed\n")) + return NULL; + } + + /* Init method pointers */ + p->del = del_ccast; + p->load = load_ccast; + p->shutdown = shutdown_ccast; + p->get_load_delay = get_load_delay; + p->get_direct_send = get_direct_send; + + p->forcedef = forcedef; + + ccast_id_copy(&p->id, id); + + /* Establish communications */ + if (start_ccast(p)) { + del_ccast(p); + return NULL; + } + + return p; +} + +/* Load up a URL */ +/* Returns nz on error: */ +/* 1 send error */ +/* 2 receieve error */ +/* 3 invalid player state/load failed/load cancelled */ +static int load_ccast( + ccast *p, + char *url, /* URL to load, NULL if sent in-line */ + unsigned char *ibuf, size_t ilen,/* PNG image to load, NULL if url */ + double bg[3], /* Background color RGB */ + double x, double y, /* Window location and size as prop. of display */ + double w, double h /* Size as multiplier of default 10% width */ +) { + ccmessv_err merr; + int reqid, firstid, lastid; + ccmes mes; + char *media_chan = "urn:x-cast:com.google.cast.media"; + char *direct_chan = "urn:x-cast:net.hoech.cast.patterngenerator"; + char *receiver_chan = "urn:x-cast:com.google.cast.receiver"; +// char *player_message_chan = "urn:x-cast:com.google.cast.player.message"; + int dchan = 0; /* Using direct channel */ + int i, maxtries = LOAD_TRIES, rv = 0; + + ccmes_init(&mes); + + /* Retry loop */ + for (i = 0; ; i++) { + unsigned char *iibuf = ibuf; + size_t iilen = ilen; + firstid = lastid = reqid = ++p->requestId; + + DBG((dbgo,"##### load_ccast try %d/%d\n",i+1,maxtries)) + + if (p->messv == NULL) { + DBG((dbgo,"mes->send LOAD failed due to lost connection\n")) + + } else { + + if (url == NULL && !p->patgenrcv) { + DBG((dbgo,"mes->send not given URL\n")) + return 1; + } + + /* Send the LOAD URL command */ + if (url != NULL) { + char *xl, mesbuf[1024]; + + xl = strrchr(url, '.'); // Get extension location + + if (xl != NULL && stricmp(xl, ".webm") == 0) { + sprintf(mesbuf, "{ \"requestId\": %d, \"type\": \"LOAD\", \"media\": " + "{ \"contentId\": \"%s\",", reqid, url); + strcat(mesbuf, + "\"contentType\": \"video/webm\" }," + "\"autplay\": \"true\" }"); + + } else if (xl != NULL && stricmp(xl, ".mp4") == 0) { + sprintf(mesbuf, "{ \"requestId\": %d, \"type\": \"LOAD\", \"media\": " + "{ \"contentId\": \"%s\",", reqid, url); + strcat(mesbuf, + "\"contentType\": \"video/mp4\" }," + "\"autplay\": \"true\" }"); + + } else { /* Assume PNG */ + sprintf(mesbuf, "{ \"requestId\": %d, \"type\": \"LOAD\", \"media\": " + "{ \"contentId\": \"%s\",", reqid, url); + strcat(mesbuf, + "\"contentType\": \"image/png\" } }"); + } + + mes.source_id = "sender-0"; + mes.destination_id = p->transportId; + mes.namespace = media_chan; + mes.binary = 0; + mes.data = (ORD8 *)mesbuf; +#ifdef CHECK_JSON + check_json((char *)mes.data); +#endif + if ((merr = p->messv->send(p->messv, &mes)) != ccmessv_OK) { + DBG((dbgo,"mes->send LOAD failed with '%s'\n",ccmessv_emes(merr))) + rv = 1; + goto retry; /* Failed */ + } + +#ifdef NEVER + /* Send base64 PNG image & background color definition in one message */ + /* This will fail if the message is > 64K */ + } else if (iibuf != NULL) { + char *mesbuf, *cp; + int dlen; + + if ((mesbuf = malloc(1024 + EBASE64LEN(iilen))) == NULL) { + DBG((dbgo,"mes->send malloc failed\n")) + return 1; + } + + cp = mesbuf; + cp += sprintf(cp, "{ \"requestId\": %d, \"type\": \"LOAD\", \"media\": " + "{ \"contentId\": \"data:image/png;base64,",reqid); + ebase64(&dlen, cp, iibuf, iilen); + cp += dlen; + DBG((dbgo,"base64 encoded PNG = %d bytes\n",dlen)) + sprintf(cp, "|rgb(%d, %d, %d)|%f|%f|%f|%f\"," + "\"streamType\": \"LIVE\",\"contentType\": \"text/plain\" } }", + (int)(bg[0] * 255.0 + 0.5), (int)(bg[1] * 255.0 + 0.5), (int)(bg[2] * 255.0 + 0.5), + x, y, w, h); + + mes.source_id = "sender-0"; + mes.destination_id = p->transportId; + mes.namespace = media_chan; + mes.binary = 0; + mes.data = (ORD8 *)mesbuf; +#ifdef CHECK_JSON + check_json((char *)mes.data); +#endif + if ((merr = p->messv->send(p->messv, &mes)) != ccmessv_OK) { + DBG((dbgo,"mes->send LOAD failed with '%s'\n",ccmessv_emes(merr))) + free(mesbuf); + rv = 1; + goto retry; /* Failed */ + } + free(mesbuf); + +#else /* !NEVER */ + /* Send base64 PNG image & background color definition in multiple packets */ + } else if (iibuf != NULL) { + char *mesbuf, *cp; + size_t maxlen, meslen; + size_t enclen, senclen = 0; + int dlen; /* Encoded length to send */ + + if ((mesbuf = malloc(61 * 1024)) == NULL) { + DBG((dbgo,"mes->send malloc failed\n")) + return 1; + } + + dchan = 1; + + enclen = EBASE64LEN(ilen); /* Encoded length of whole image */ + maxlen = DBASE64LEN(60 * 1024); /* Maximum image bytes to send per message */ + meslen = iilen; + if (meslen > maxlen) + meslen = maxlen; + + cp = mesbuf; + cp += sprintf(cp, "{ "); + cp += sprintf(cp, "\"requestId\": %d,",reqid); + cp += sprintf(cp, "\"foreground\": { "); + cp += sprintf(cp, "\"contentType\": \"image/png\","); + cp += sprintf(cp, "\"encoding\": \"base64\","); + cp += sprintf(cp, "\"data\": \""); + ebase64(&dlen, cp, iibuf, meslen); + DBG((dbgo,"part base64 encoded PNG = %d bytes\n",dlen)) + iibuf += meslen; + iilen -= meslen; + senclen += dlen; + cp += dlen; + cp += sprintf(cp, "\","); + cp += sprintf(cp, "\"size\": %lu",(unsigned long)EBASE64LEN(ilen)); + cp += sprintf(cp, " },"); + + cp += sprintf(cp, "\"background\": \"rgb(%d, %d, %d)\",", + (int)(bg[0] * 255.0 + 0.5), + (int)(bg[1] * 255.0 + 0.5), + (int)(bg[2] * 255.0 + 0.5)); + + cp += sprintf(cp, "\"offset\": [%f, %f],",x,y); + cp += sprintf(cp, "\"scale\": [%f, %f]",w,h); + cp += sprintf(cp, " }"); + + mes.source_id = "sender-0"; + mes.destination_id = p->transportId; + mes.namespace = direct_chan; + mes.binary = 0; + mes.data = (ORD8 *)mesbuf; +#ifdef CHECK_JSON + check_json((char *)mes.data); +#endif + + if ((merr = p->messv->send(p->messv, &mes)) != ccmessv_OK) { + DBG((dbgo,"mes->send LOAD failed with '%s'\n",ccmessv_emes(merr))) + free(mesbuf); + rv = 1; + goto retry; /* Failed */ + } + + /* If we didn't send the whole image in one go */ + while (iilen > 0) { + + /* Send the next chunk */ + meslen = iilen; + if (meslen > maxlen) + meslen = maxlen; + + DBG((dbgo,"Sending %d bytes of %d remaining in image\n",meslen,iilen)) + + lastid = reqid = ++p->requestId; + + cp = mesbuf; + cp += sprintf(cp, "{ "); + cp += sprintf(cp, "\"requestId\": %d,",reqid); + cp += sprintf(cp, "\"foreground\": \""); + ebase64(&dlen, cp, iibuf, meslen); + DBG((dbgo,"part base64 encoded PNG = %d bytes\n",dlen)) + iibuf += meslen; + iilen -= meslen; + senclen += dlen; + + + cp += dlen; + cp += sprintf(cp, "\" }"); + + mes.source_id = "sender-0"; + mes.destination_id = p->transportId; + mes.namespace = direct_chan; + mes.binary = 0; + mes.data = (ORD8 *)mesbuf; +#ifdef CHECK_JSON + check_json((char *)mes.data); +#endif + if ((merr = p->messv->send(p->messv, &mes)) != ccmessv_OK) { + DBG((dbgo,"mes->send LOAD failed with '%s'\n",ccmessv_emes(merr))) + free(mesbuf); + rv = 1; + goto retry; /* Failed */ + } + + if ((lastid - firstid) > 0) { + + /* Wait for an ACK, to make sure the channel doesn't get choked up */ + if (get_a_reply_id(p, direct_chan, firstid, &mes, 10000) != 0) { + DBG((dbgo,"load_ccast: failed to get reply\n")) + rv = 2; + goto retry; /* Failed */ + } + if (mes.mtype == NULL) { + DBG((dbgo,"load_ccast: mtype == NULL\n")) + rv = 2; + goto retry; /* Failed */ + + } else if (dchan && strcmp(mes.mtype, "ACK") == 0) { + DBG((dbgo,"load_ccast: got ACK\n")) + + } else if (dchan && strcmp(mes.mtype, "NACK") == 0) { + /* Failed. Get error status */ + yajl_val errors; + if ((errors = yajl_tree_get_first(mes.tnode, "errors", yajl_t_array)) != NULL) { + DBG((dbgo,"NACK returned errors:\n")) + if (YAJL_IS_ARRAY(errors)) { + for (i = 0; i < errors->u.array.len; i++) { + yajl_val error = errors->u.array.values[i]; + DBG((dbgo,"%s\n",error->u.string)) + } + + } else { + DBG((dbgo,"NACK errors is not an array!\n")) + } + } else { + DBG((dbgo,"NACK failed to return errors\n")) + } + rv = 2; + goto retry; /* Failed */ + + } else { + rv = 3; + DBG((dbgo,"load_ccast: got mtype '%s'\n",mes.mtype)) + goto retry; /* Failed */ + } + ccmes_empty(&mes); + firstid++; + } + }; + free(mesbuf); + + /* This would be bad... */ + if (iilen == 0 && senclen != enclen) + fprintf(stderr,"ccast load finished but senclen %lu != enclen %lu\n", + (unsigned long)senclen,(unsigned long)enclen); +#endif /* !NEVER */ + + } else { + DBG((dbgo,"mes->send not given URL or png data\n")) + return 1; + } + } + + /* Wait for a reply to each load message */ + for (reqid = firstid; reqid <= lastid; reqid++) { + + if (get_a_reply_id(p, dchan ? direct_chan : media_chan, reqid, &mes, 5000) != 0) { + DBG((dbgo,"load_ccast: failed to get reply\n")) + rv = 2; + goto retry; /* Failed */ + } + /* Reply could be: + MEDIA_STATUS + INVALID_PLAYER_STATE, + LOAD_FAILED, + LOAD_CANCELLED + + For net.hoech.cast.patterngenerator + ACK, + NACK + */ + if (mes.mtype == NULL) { + DBG((dbgo,"load_ccast: mtype == NULL\n")) + rv = 2; + goto retry; /* Failed */ + + } else if (dchan && strcmp(mes.mtype, "ACK") == 0) { + DBG((dbgo,"load_ccast: got ACK\n")) + + } else if (dchan && strcmp(mes.mtype, "NACK") == 0) { + /* Failed. Get error status */ + yajl_val errors; + if ((errors = yajl_tree_get_first(mes.tnode, "errors", yajl_t_array)) != NULL) { + DBG((dbgo,"NACK returned errors:\n")) + if (YAJL_IS_ARRAY(errors)) { + for (i = 0; i < errors->u.array.len; i++) { + yajl_val error = errors->u.array.values[i]; + DBG((dbgo,"%s\n",error->u.string)) + } + + } else { + DBG((dbgo,"NACK errors is not an array!\n")) + } + } else { + DBG((dbgo,"NACK failed to return errors\n")) + } + rv = 2; + goto retry; /* Failed */ + + } else if (strcmp(mes.mtype, "MEDIA_STATUS") == 0) { + yajl_val node, i; + if ((i = yajl_tree_get_first(mes.tnode, "mediaSessionId", yajl_t_number)) != NULL) { + p->mediaSessionId = YAJL_GET_INTEGER(i); + DBG((dbgo,"MEDIA_STATUS returned mediaSessionId %d\n",p->mediaSessionId)) + } else { + DBG((dbgo,"MEDIA_STATUS failed to return mediaSessionId\n")) + } + /* Suceeded */ + + } else { + rv = 3; + DBG((dbgo,"load_ccast: got mtype '%s'\n",mes.mtype)) + goto retry; /* Failed */ + } + ccmes_empty(&mes); + } + /* If we got here, we succeeded */ + break; + + retry:; + +// if (!p->loaded1 || (i+1) >= maxtries) + if ((i+1) >= maxtries) { + return rv; /* Too many tries - give up */ + } + + DBG((dbgo,"load_ccast: failed on try %d/%d - re-connecting to chrome cast\n",i+1,maxtries)) + shutdown_ccast(p); /* Tear connection down */ + if (start_ccast(p)) { /* Set it up again */ + DBG((dbgo,"load_ccast: re-connecting failed\n")) + return 1; + } + /* And retry */ + rv = 0; + } /* Retry loop */ + + /* Success */ + p->loaded1 = 1; /* Loaded at least once */ + + /* Currently there is a 1.5 second fade up delay imposed by */ + /* the base ChromeCast software or default receiver. */ + if (p->load_delay > 0.0) + msec_sleep(p->load_delay); + + return rv; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Static list so that all open ChromCast connections can be closed on a SIGKILL */ +static ccast *ccast_list = NULL; + +/* Clean up any open ChromeCast connections */ +static void ccast_cleanup() { + ccast *pp, *np; + + for (pp = ccast_list; pp != NULL; pp = np) { + np = pp->next; + a1logd(g_log, 2, "ccast_cleanup: closing 0x%x\n",pp); + pp->shutdown(pp); + } +} + +#ifdef NT +static void (__cdecl *ccast_int)(int sig) = SIG_DFL; +static void (__cdecl *ccast_term)(int sig) = SIG_DFL; +#endif +#ifdef UNIX +static void (*ccast_hup)(int sig) = SIG_DFL; +static void (*ccast_int)(int sig) = SIG_DFL; +static void (*ccast_term)(int sig) = SIG_DFL; +#endif + +/* On something killing our process, deal with USB cleanup */ +static void ccast_sighandler(int arg) { + static amutex_static(lock); + + a1logd(g_log, 2, "ccast_sighandler: invoked with arg = %d\n",arg); + + /* Make sure we don't re-enter */ + if (amutex_trylock(lock)) { + return; + } + + ccast_cleanup(); + a1logd(g_log, 2, "ccast_sighandler: done ccast_sighandler()\n"); + + /* Call the existing handlers */ +#ifdef UNIX + if (arg == SIGHUP && ccast_hup != SIG_DFL && ccast_hup != SIG_IGN) + ccast_hup(arg); +#endif /* UNIX */ + if (arg == SIGINT && ccast_int != SIG_DFL && ccast_int != SIG_IGN) + ccast_int(arg); + if (arg == SIGTERM && ccast_term != SIG_DFL && ccast_term != SIG_IGN) + ccast_term(arg); + + a1logd(g_log, 2, "ccast_sighandler: calling exit()\n"); + + amutex_unlock(lock); + exit(0); +} + + +/* Install the cleanup signal handlers */ +void ccast_install_signal_handlers(ccast *p) { + + if (ccast_list == NULL) { + a1logd(g_log, 2, "ccast_install_signal_handlers: called\n"); +#if defined(UNIX) + ccast_hup = signal(SIGHUP, ccast_sighandler); +#endif /* UNIX */ + ccast_int = signal(SIGINT, ccast_sighandler); + ccast_term = signal(SIGTERM, ccast_sighandler); + } + + /* Add it to our static list, to allow automatic cleanup on signal */ + p->next = ccast_list; + ccast_list = p; + a1logd(g_log, 6, "ccast_install_signal_handlers: done\n"); +} + +/* Delete an ccast from our static signal cleanup list */ +void ccast_delete_from_cleanup_list(ccast *p) { + + /* Find it and delete it from our static cleanup list */ + if (ccast_list != NULL) { + a1logd(g_log, 6, "ccast_install_signal_handlers: called\n"); + if (ccast_list == p) { + ccast_list = p->next; + if (ccast_list == NULL) { +#if defined(UNIX) + signal(SIGHUP, ccast_hup); +#endif /* UNIX */ + signal(SIGINT, ccast_int); + signal(SIGTERM, ccast_term); + } + } else { + ccast *pp; + for (pp = ccast_list; pp != NULL; pp = pp->next) { + if (pp->next == p) { + pp->next = p->next; + break; + } + } + } + a1logd(g_log, 6, "ccast_install_signal_handlers: done\n"); + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Quantization model of ChromCast and TV */ +/* ctx is a placeholder, and can be NULL */ + +#define QUANT(xx, qq) (floor((xx) * (qq) + 0.5)/(qq)) + +/* Input & output RGB 0.0 - 1.0 levels */ +void ccastQuant(void *ctx, double out[3], double in[3]) { + double r = in[0], g = in[1], b = in[2]; + double Y, Cb, Cr; + +// printf("ccastQuant: %f %f %f",r,g,b); + + /* Scale RGB to 8 bit then quantize, since that's the limit of the frame buffer */ + r = floor(r * 255.0 + 0.5); + g = floor(g * 255.0 + 0.5); + b = floor(b * 255.0 + 0.5); + + /* ChromCast hardware seems to use 9 bit coeficients */ + Y = r * QUANT((235.0 - 16.0) * 0.2126/255.0, 512.0) + + g * QUANT((235.0 - 16.0) * 0.7152/255.0, 512.0) + + b * QUANT((235.0 - 16.0) * 0.0722/255.0, 512.0); + + Cb = r * -QUANT((240.0 - 16.0) * 0.2126 /(255.0 * 1.8556), 512.0) + + g * -QUANT((240.0 - 16.0) * 0.7152 /(255.0 * 1.8556), 512.0) + + b * QUANT((240.0 - 16.0) * (1.0 - 0.0722)/(255.0 * 1.8556), 512.0); + + Cr = r * QUANT((240.0 - 16.0) * (1.0 - 0.2126)/(255.0 * 1.5748), 512.0) + + g * -QUANT((240.0 - 16.0) * 0.7152 /(255.0 * 1.5748), 512.0) + + b * -QUANT((240.0 - 16.0) * 0.0722 /(255.0 * 1.5748), 512.0); + + /* (Don't bother with offsets, since they don't affect quantization) */ + + /* Quantize YCbCr to 8 bit, since that's what ChromCast HDMI delivers */ + Y = floor(Y + 0.5); + Cb = floor(Cb + 0.5); + Cr = floor(Cr + 0.5); + + /* We simply assume that the TV decoding is perfect, */ + /* but is limited to 8 bit full range RGB output */ + r = Y * 255.0/(235.0 - 16.0) + + Cr * (1.574800000 * 255.0)/(240.0 - 16.0); + + g = Y * 255.0/(235.0 - 16.0) + + Cb * (-0.187324273 * 255.0)/(240.0 - 16.0) + + Cr * (-0.468124273 * 255.0)/(240.0 - 16.0); + + b = Y * 255.0/(235.0 - 16.0) + + Cb * (1.855600000 * 255.0)/(240.0 - 16.0); + + /* Clip */ + if (r > 255.0) + r = 255.0; + else if (r < 0.0) + r = 0.0; + if (g > 255.0) + g = 255.0; + else if (g < 0.0) + g = 0.0; + if (b > 255.0) + b = 255.0; + else if (b < 0.0) + b = 0.0; + + /* We assume that the TV is 8 bit, so */ + /* quantize to 8 bits and return to 0.0 - 1.0 range. */ + r = floor(r + 0.5)/255.0; + g = floor(g + 0.5)/255.0; + b = floor(b + 0.5)/255.0; + + out[0] = r; + out[1] = g; + out[2] = b; + +// printf(" -> %f %f %f\n",r,g,b); +} + +/* Input RGB 8 bit 0 - 255 levels, output YCbCr 8 bit 16 - 235 levels */ +/* Non-clipping, non-8 bit quantizing */ +void ccast2YCbCr_nq(void *ctx, double out[3], double in[3]) { + double r = in[0], g = in[1], b = in[2]; + double Y, Cb, Cr; + +// printf("ccast2YCbCr_nq: %f %f %f",r,g,b); + + /* ChromCast hardware seems to use 9 bit coeficients */ + Y = r * QUANT((235.0 - 16.0) * 0.2126/255.0, 512.0) + + g * QUANT((235.0 - 16.0) * 0.7152/255.0, 512.0) + + b * QUANT((235.0 - 16.0) * 0.0722/255.0, 512.0) + + 16.0; + + Cb = r * -QUANT((240.0 - 16.0) * 0.2126 /(255.0 * 1.8556), 512.0) + + g * -QUANT((240.0 - 16.0) * 0.7152 /(255.0 * 1.8556), 512.0) + + b * QUANT((240.0 - 16.0) * (1.0 - 0.0722)/(255.0 * 1.8556), 512.0) + + 16.0; + + Cr = r * QUANT((240.0 - 16.0) * (1.0 - 0.2126)/(255.0 * 1.5748), 512.0) + + g * -QUANT((240.0 - 16.0) * 0.7152 /(255.0 * 1.5748), 512.0) + + b * -QUANT((240.0 - 16.0) * 0.0722 /(255.0 * 1.5748), 512.0) + + 16.0; + + out[0] = Y; + out[1] = Cb; + out[2] = Cr; + +// printf(" -> %f %f %f\n",out[0],out[1],out[2]); +} + +/* Input RGB 8 bit 0 - 255 levels, output YCbCr 8 bit 16 - 235 levels */ +void ccast2YCbCr(void *ctx, double out[3], double in[3]) { + double rgb[3]; + +// printf("ccast2YCbCr: %f %f %f",r,g,b); + + /* Quantize RGB to 8 bit, since that's the limit of the frame buffer */ + rgb[0] = floor(in[0] + 0.5); + rgb[1] = floor(in[1] + 0.5); + rgb[2] = floor(in[2] + 0.5); + + ccast2YCbCr_nq(ctx, out, rgb); + + /* Quantize YCbCr to 8 bit, since that's what ChromCast HDMI delivers */ + out[0] = floor(out[0] + 0.5); + out[1] = floor(out[1] + 0.5); + out[2] = floor(out[2] + 0.5); +// printf(" -> %f %f %f\n",out[0],out[1],out[2]); +} + +/* Input YCbCr 8 bit 16 - 235 levels output RGB 8 bit 0 - 255 levels. */ +/* Non-clipping, non-8 bit quantizing */ +void YCbCr2ccast_nq(void *ctx, double out[3], double in[3]) { + double Y = in[0], Cb = in[1], Cr = in[2]; + double r, g, b; + +// printf("YCbCr2ccast_nq: %f %f %f",Y,Cb,Cr); + + Y -= 16.0; + Cb -= 16.0; + Cr -= 16.0; + + /* We simply assume that the TV decoding is perfect, */ + /* but is limited to 8 bit full range RGB output */ + r = Y * 255.0/(235.0 - 16.0) + + Cr * (1.574800000 * 255.0)/(240.0 - 16.0); + + g = Y * 255.0/(235.0 - 16.0) + + Cb * (-0.187324273 * 255.0)/(240.0 - 16.0) + + Cr * (-0.468124273 * 255.0)/(240.0 - 16.0); + + b = Y * 255.0/(235.0 - 16.0) + + Cb * (1.855600000 * 255.0)/(240.0 - 16.0); + + out[0] = r; + out[1] = g; + out[2] = b; + +// printf(" -> %f %f %f\n",out[0],out[1],out[2]); +} + +/* Input YCbCr 8 bit 16 - 235 levels output RGB 8 bit 0 - 255 levels. */ +/* Quantize input, and quantize and clip output. */ +/* Return nz if the output was clipped */ +int YCbCr2ccast(void *ctx, double out[3], double in[3]) { + double YCbCr[3]; + int k, rv = 0; + +// printf("YCbCr2ccast: %f %f %f",Y,Cb,Cr); + + /* Quantize YCbCr to 8 bit, since that's what ChromCast HDMI delivers */ + YCbCr[0] = floor(in[0] + 0.5); + YCbCr[1] = floor(in[1] + 0.5); + YCbCr[2] = floor(in[2] + 0.5); + + YCbCr2ccast_nq(ctx, out, YCbCr); + + /* Quantize and clip to RGB, since we assume the TV does this */ + for (k = 0; k < 3; k++) { + out[k] = floor(out[k] + 0.5); + + if (out[k] > 255.0) { + out[k] = 255.0; + rv = 1; + } else if (out[k] < 0.0) { + out[k] = 0.0; + rv = 1; + } + } + +// printf(" -> %f %f %f, clip %d\n",out[0],out[1],out[2],rv); + + return rv; +} + diff --git a/ccast/ccast.h b/ccast/ccast.h new file mode 100644 index 0000000..7014679 --- /dev/null +++ b/ccast/ccast.h @@ -0,0 +1,108 @@ +#ifndef CCAST_H +#define CCAST_H + +/* + * Argyll Color Correction System + * ChromCast public API support + * + * Author: Graeme W. Gill + * Date: 8/9/2014 + * + * Copyright 2014 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License2.txt file for licencing details. + * + */ + +/* + * This class provides simple access to the Google ChromeCast + * for the purposes of generating Video Test patches. + */ + +#include "ccmdns.h" /* Function to get a list of ChromeCasts */ + +/* A ChromCast instance */ +struct _ccast { + ccast_id id; /* Id of CromeCast being accessed */ + struct _ccmessv *messv; + int requestId; + + char *sessionId; + char *transportId; + + /* Delete the ccast */ + void (*del)(struct _ccast *p); + + /* Load a URL */ + int (*load)(struct _ccast *p, char *url, unsigned char *data, size_t dlen, + double bg[3], double x, double y, double w, double h); + + /* Shut it down */ + void (*shutdown)(struct _ccast *p); + + /* Get the extra load delay */ + int (*get_load_delay)(struct _ccast *p); + + /* Return nz if we can send PNG directly as base64 + bg RGB, */ + /* else have to setup webserver and send URL */ + int (*get_direct_send)(struct _ccast *p); + + /* Thread context */ + athread *rmesth; /* Receive message thread */ + struct _ccmes *rmes; /* Linked list of received messages */ + amutex rlock; /* Receive list lock protecting rmes */ + acond rcond; /* Condition for client to wait on */ + int w_rq; /* NZ if client is waiting on message */ + char *w_rqns; /* Message namespac client is waiting for (if !NULL) */ + int w_rqid; /* Message rqid client is waiting for (0 = any) */ + volatile int stop; /* Thread stop flag */ + volatile int stopped; /* Thread reply flag */ + + /* Other */ + int mediaSessionId; + int loaded1; /* Set to nz if at least one load succeeded */ + int forcedef; /* Force using default reciever rather than patgen */ + int patgenrcv; /* nz if pattern generator receiver, else default receiver */ + int load_delay; /* Delay needed after succesful LOAD */ + + struct _ccast *next; /* Next in static list for signal cleanup */ +}; typedef struct _ccast ccast; + +/* Create a new ChromeCast */ +/* Return NULL on error */ +/* nz forcedef to force using default reciever & web server rather than pattern generator app. */ +/* Can also set environment variabl "ARGYLL_CCAST_DEFAULT_RECEIVER" to do this. */ +ccast *new_ccast(ccast_id *id, int forcedef); + +/* Quantization model of ChromCast and TV */ +/* ctx is a placeholder, and can be NULL */ + +/* Input & output RGB 0.0 - 1.0 levels */ +void ccastQuant(void *ctx, double out[3], double in[3]); + +/* Input RGB 8 bit 0 - 255 levels, output YCbCr 8 bit 16 - 235 levels */ +void ccast2YCbCr(void *ctx, double out[3], double in[3]); + +/* Input RGB 8 bit 0 - 255 levels, output YCbCr 8 bit 16 - 235 levels */ +/* non-quanized or clipped */ +void ccast2YCbCr_nq(void *ctx, double out[3], double in[3]); + +/* Input YCbCr 8 bit 16 - 235 levels output RGB 8 bit 0 - 255 levels. */ +/* Return nz if the output was clipped */ +int YCbCr2ccast(void *ctx, double out[3], double in[3]); + +/* Input YCbCr 8 bit 16 - 235 levels output RGB 8 bit 0 - 255 levels. */ +/* non-quanized or clipped */ +void YCbCr2ccast_nq(void *ctx, double out[3], double in[3]); + +#define CCDITHSIZE 4 + +/* Compute pattern [width][height] */ +/* return the delta to the target */ +double get_ccast_dith(double ipat[CCDITHSIZE][CCDITHSIZE][3], double val[3]); + +#define CCAST_H +#endif /* CCAST_H */ + diff --git a/ccast/ccmdns.c b/ccast/ccmdns.c new file mode 100644 index 0000000..8dd889e --- /dev/null +++ b/ccast/ccmdns.c @@ -0,0 +1,1096 @@ + +/* + * Argyll Color Correction System + * ChromCast support + * + * Author: Graeme W. Gill + * Date: 28/8/2014 + * + * Copyright 2014 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License2.txt file for licencing details. + * + */ + +/* + * This class provides simple access to the Google ChromeCast + * for the purposes of generating Video Test patches. + */ + +/* + TTBD: Ideally we should use the system mDNS resource if it is available, + and fall back to our mDNS code if not. + + ie. Linux may have nothing, be using avahi-daemon, + possibly Zeroconf (== Bonjour ?) + Avahi has Bonjour compatibility library ?? + + ie. OS X is using mDNSRespo (Bonjour ?) + + MSWin may have Bonjour installed on it ? + Has dns-sd command - what's that using (SSDP/UPnP ?) + + From + + Bonjour for Windows allows any software using the standard name resolution APIs + to resolve mDNS names; it does so by registering a DLL (mdnsnsp.dll) as a + namespace provider using WSCInstallNameSpace. + + The corresponding code is included in the mDNSResponder source (in particular, + look at the mdnsNSP and NSPTool components). + + To test on Linux: + + To check what's listening: sudo netstat -anp | grep 5353 + + /etc/init.d/avahi-daemon stop + /etc/init.d/avahi-daemon start + + To test on OS X: + + To check what's listening: sudo netstat -an | grep 5353 + + launchctl unload -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist + launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#include +#ifndef SALONEINSTLIB +#include "numlib.h" +#else +#include "numsup.h" +#endif +#include "ccmdns.h" + +#undef DEBUG + +#if defined(NT) // Windows specific +# if _WIN32_WINNT < 0x0400 +# undef _WIN32_WINNT +# define _WIN32_WINNT 0x0400 // To make it link in VS2005 +# endif +# define WIN32_LEAN_AND_MEAN +# include +# include +# include + +# include +# include +# include + +# define ERRNO GetLastError() +# define UDP_SOCKET_TIMEOUT WSAETIMEDOUT + +#else /* !NT = Unix, OS X */ + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +# include +# include +# include + +typedef int SOCKET; +# define ERRNO errno + +# define INVALID_SOCKET -1 +# define SOCKET_ERROR -1 +# define UDP_SOCKET_TIMEOUT EAGAIN + +#define closesocket(xxx) close(xxx); + +#endif + +#ifdef DEBUG +# define dbgo stdout +# define DBG(xxx) fprintf xxx ; +void cc_dump_bytes(FILE *fp, char *pfx, unsigned char *buf, int len); +#else +# define DBG(xxx) ; +#endif /* DEBUG */ + +/* ================================================================ */ + +/* mDNS details */ +#define DESTINATION_MCAST "224.0.0.251" +#define DESTINATION_MCASTV6 "FF02::FB" +#define SOURCE_PORT 5353 +#define DESTINATION_PORT 5353 + +#define BUFSIZE 1024 + +/* Various DNS defines */ +#define DNS_CLASS_IN 0x0001 + +// Used by ChromeCast +#define DNS_TYPE_PTR 12 /* Domain name pointer */ +#define DNS_TYPE_TXT 16 /* Text String */ +#define DNS_TYPE_SRV 33 /* Server selection (SRV) record */ +#define DNS_TYPE_A 1 /* Host address (A) record */ +#define DNS_TYPE_AAAA 28 /* IPv6 address ??? */ +#define DNS_TYPE_NSEC 47 /* DNS Next Secure Name */ + + +#ifdef NEVER +/* Print out a V6 address in zero compresed form */ +/* (It would be nice to add zero compression) */ +static char *print_IPV6_Address(ORD8 *buf) { + static char sbuf[40], *p = sbuf; + int i; + for (i = 0; i < 8; i++) { + p += sprintf(p, "%x", buf[i * 2 + 1] * 256 + buf[i * 2 + 0]); + if (i < 7) + *p++ = ':'; + } + *p++ = '\000'; + return sbuf; +} +#endif + +/* Write a DNS string to a buffer. */ +/* return the offset of the next byte after the string */ +static int write_string(ORD8 *buf, int off, char *s) { + int len = strlen(s); + if (len >= 0xc0) + len = 0xc0; // Hmm. + buf[off] = len; + off++; + memcpy(buf + off, s, len); + off += len; + return off; +} + +/* Return NZ on error */ +static int init_mDNS() { + +#ifdef NT + WSADATA data; + if (WSAStartup(MAKEWORD(2,2), &data)) { + DBG((dbgo,"WSAStartup failed\n")) + return 1; + } +#endif + + return 0; +} + +/* Setup the send socket */ +/* Return nz on error */ +static int init_send_mDNS(SOCKET *psock) { + int nRet, nOptVal; + int off; + SOCKET sock; + struct sockaddr_in stSourceAddr; + + DBG((dbgo,"init_send_mDNS() called\n")) + + /* get a datagram (UDP) socket */ + sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock == INVALID_SOCKET) { + DBG((dbgo,"opening UDP socked failed with %d\n",ERRNO)) + return 1; + } + + /*----------------------- to send --------------------------- */ + + /* Theoretically, you do not need any special preparation to + * send to a multicast address. However, you may want a few + * things to overcome the limits of the default behavior + */ + + // If we're doing a one-shot, we shouldn't transmit from port 5353, */ + // but ChromCast won't see the packet if we don't. */ + + /* We cant send from port 5353 if someone else is using it, */ + /* so set the SO_REUSEADDR option (which is enough for MSWin), */ + /* and SO_REUSEPORT for OS X and Linux */ + { + int on = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on))) { + DBG((dbgo,"setsockopt(SO_REUSEADDR) failed with %d\n",ERRNO)) + closesocket(sock); + return 1; + } + + /* Need this to be able to open port on Unix like systems */ +#ifndef NT +# ifndef SO_REUSEPORT +# ifdef __APPLE__ +# define SO_REUSEPORT 0x0200 +# else /* Linux */ +# define SO_REUSEPORT 15 +# endif +# endif + if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char *)&on, sizeof(on))) { + DBG((dbgo,"setsockopt(SO_REUSEPORT) failed with %d\n",ERRNO)) + } +#endif + } + + /* init source address structure */ + stSourceAddr.sin_family = PF_INET; + stSourceAddr.sin_port = htons(SOURCE_PORT); + stSourceAddr.sin_addr.s_addr = INADDR_ANY; + + /* + * Calling bind() is not required, but some implementations need it + * before you can reference any multicast socket options + * and in this case we must be on port 5353 for ChromeCast to see the packet + */ + nRet = bind(sock, (struct sockaddr *)&stSourceAddr, + sizeof(struct sockaddr)); + if (nRet == SOCKET_ERROR) { + DBG((dbgo,"bind failed with %d\n",ERRNO)) + closesocket(sock); + return 1; + } + + /* disable loopback of multicast datagrams we send, since the + * default--according to Steve Deering--is to loopback all + * datagrams sent on any interface which is a member of the + * destination group address of that datagram. + */ + nOptVal = 0; + nRet = setsockopt (sock, IPPROTO_IP, IP_MULTICAST_LOOP, + (char *)&nOptVal, sizeof(int)); + if (nRet == SOCKET_ERROR) { + /* rather than notifying the user, we make note that this option + * failed. Some WinSocks don't support this option, and default + * with loopback disabled), so this failure is of no consequence. + * However, if we *do* get loop-backed data, we'll know why + */ + DBG((dbgo,"[disabling loopback failed with %d]\n",ERRNO)) + } + +#ifdef NEVER // We only want this to be local + /* increase the IP TTL from the default of one to 64, so our + * multicast datagrams can get off of the local network + */ + nOptVal = 64; + nRet = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, + (char *)&nOptVal, sizeof(int)); + if (nRet == SOCKET_ERROR) { + DBG((dbgo,"increasing TTL failed with %d\n",ERRNO)) + closesocket(sock); + return 1; + } +#endif + + if (psock != NULL) + *psock = sock; + + DBG((dbgo,"init sending mDNS succeed\n",ERRNO)) + + return 0; +} + +/* Send an mDNS quesry */ +/* on some platforms if we try to transmit & recieve at the same time */ +/* Return nz on error */ +static int send_mDNS(SOCKET sock) { + int nRet; + int off; + ORD8 achOutBuf[BUFSIZE]; + struct sockaddr_in stDestAddr; + + DBG((dbgo,"send_mDNS() called\n")) + + /* Initialize the Destination Address structure and send */ + stDestAddr.sin_family = PF_INET; + stDestAddr.sin_addr.s_addr = inet_addr(DESTINATION_MCAST); + stDestAddr.sin_port = htons(DESTINATION_PORT); + + /* Setup the FQDN data packet to send */ + write_ORD16_be(0, achOutBuf + 0); /* ID */ + write_ORD16_be(0, achOutBuf + 2); /* Flags */ + write_ORD16_be(1, achOutBuf + 4); /* QDCOUNT - one question */ + write_ORD16_be(0, achOutBuf + 6); /* ANCOUNT */ + write_ORD16_be(0, achOutBuf + 8); /* NSCOUNT */ + write_ORD16_be(0, achOutBuf + 10); /* ARCOUNT */ + off = 12; + off = write_string(achOutBuf, off, "_googlecast"); + off = write_string(achOutBuf, off, "_tcp"); + off = write_string(achOutBuf, off, "local"); + write_ORD8(0, achOutBuf + off); /* null string */ + off += 1; + write_ORD16_be(0x000c, achOutBuf + off); /* QCOUNT */ + off += 2; + /* Set top bit to get a unicast response (not for ChromeCast though) */ + write_ORD16_be(0x0001, achOutBuf + off); /* QCLASS */ + off += 2; + + nRet = sendto(sock, (char *)achOutBuf, off, + 0, + (struct sockaddr *) &stDestAddr, + sizeof(struct sockaddr)); + if (nRet == SOCKET_ERROR) { + DBG((dbgo,"sending mDNS query failed with %d\n",ERRNO)) + return 1; + } + + DBG((dbgo,"sending mDNS query succeed\n",ERRNO)) + + return 0; +} + +static int parse_dns(char **name, char **ip, ORD8 *buf, int size); + +/* Free up what get_ccids returned */ +void free_ccids(ccast_id **ids) { + if (ids != NULL) { + int i; + + for (i = 0; ids[i] != NULL; i++) { + free(ids[i]->name); + free(ids[i]->ip); + free(ids[i]); + } + free(ids); + } +} + +/* Spend the given time waiting for replies. */ +/* (Note than on MSWin this will be a minimum of 500msec) */ +/* Add any ChromeCast replies to the list. */ +/* return nz on error */ +static int init_receive_mDNS(SOCKET *psock) { + int nRet; + int off, size; + struct sockaddr_in stSourceAddr; + struct ip_mreq stIpMreq; + SOCKET sock; + + DBG((dbgo,"init_receive_mDNS() called\n")) + + /* get a datagram (UDP) socket */ + sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock == INVALID_SOCKET) { + DBG((dbgo,"opening UDP socked failed with %d\n",ERRNO)) + return 1; + } + + /* We can't receive from port 5353 if someone else is using it, */ + /* so set the SO_REUSEADDR option (which is enough for MSWin), */ + /* and SO_REUSEPORT for OS X and Linux */ + { + int on = 1, off = 0; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on))) { + DBG((dbgo,"setsockopt(SO_REUSEADDR) failed with %d\n",ERRNO)) + closesocket(sock); + return 1; + } + + /* Need this to be able to open port on Unix like systems */ +#ifndef NT +# ifndef SO_REUSEPORT +# ifdef __APPLE__ +# define SO_REUSEPORT 0x0200 +# else /* Linux */ +# define SO_REUSEPORT 15 +# endif +# endif + if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char *)&on, sizeof(on))) { + DBG((dbgo,"setsockopt(SO_REUSEPORT) failed with %d\n",ERRNO)) + } +#endif + } + + /* init source address structure */ + stSourceAddr.sin_family = PF_INET; + stSourceAddr.sin_port = htons(SOURCE_PORT); + stSourceAddr.sin_addr.s_addr = INADDR_ANY; + + /* + * Calling bind() is not required, but some implementations need it + * before you can reference any multicast socket options + * and in this case we must be on port 5353. + */ + nRet = bind(sock, (struct sockaddr *)&stSourceAddr, + sizeof(struct sockaddr)); + if (nRet == SOCKET_ERROR) { + DBG((dbgo,"bind failed with %d\n",ERRNO)) + closesocket(sock); + return 1; + } + + /* join the multicast group we want to receive datagrams from */ + stIpMreq.imr_multiaddr.s_addr = inet_addr(DESTINATION_MCAST); /* group addr */ + stIpMreq.imr_interface.s_addr = INADDR_ANY; /* use default */ + nRet = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (char *)&stIpMreq, sizeof (struct ip_mreq)); + + if (nRet == SOCKET_ERROR) { + DBG((dbgo,"registering for read events failed with %d\n",ERRNO)) + closesocket(sock); + return 1; + } + + /* Make this timeout after 100 msec second */ +#ifdef NT + { + DWORD tv; + tv = 100; + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) < 0) { + DBG((dbgo,"setsockopt timout failed with %d\n",ERRNO)) + closesocket(sock); + return 1; + } + } +#else + { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100 * 1000; + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) < 0) { + DBG((dbgo,"setsockopt timout failed with %d\n",ERRNO)) + closesocket(sock); + return 1; + } + } +#endif + + if (psock != NULL) + *psock = sock; + + return 0; +} + +/* Spend the given time waiting for replies. */ +/* (Note than on MSWin this will be a minimum of 500msec) */ +/* Add any ChromeCast replies to the list. */ +/* return nz on error & free *ids */ +static int receive_mDNS(SOCKET sock, ccast_id ***ids, int emsec) { + int nids = 0; + unsigned int smsec; + unsigned int nSize; + int off, size; + ORD8 achInBuf[BUFSIZE]; + struct sockaddr_in stSourceAddr; + + /* Count the number of current id's */ + if (*ids != NULL) { + for (nids = 0; (*ids)[nids] != NULL; nids++) + ; + } + + DBG((dbgo,"receive_mDNS() called with %d ids\n",nids)) + + for (smsec = msec_time(), emsec += smsec;msec_time() <= emsec;) { + int i; + char *name, *ip; + struct sockaddr stSockAddr; + + /* Recv the available data */ + nSize = sizeof(struct sockaddr); + size = recvfrom(sock, (char *)achInBuf, + BUFSIZE, 0, (struct sockaddr *) &stSockAddr, &nSize); + if (size == SOCKET_ERROR) { + if (ERRNO == UDP_SOCKET_TIMEOUT) + continue; /* Timeout */ + DBG((dbgo,"recvfrom failed with %d\n",ERRNO)) + free_ccids(*ids); + *ids = NULL; + return 1; + } + DBG((dbgo,"Got mDNS message length %d bytes\n",size)) +#ifdef DEBUG + cc_dump_bytes(dbgo, " ", achInBuf, size); +#endif + + if (parse_dns(&name, &ip, achInBuf, size) != 0) { + DBG((dbgo,"Failed to parse the reply\n")) + } else { + DBG((dbgo,"Parsed reply OK\n")) + +//if (*pnids > 0) { +// name = strdup("Argyll1234"); +// ip = strdup("10.0.0.129"); +//} + /* If we found an entry */ + if (name != NULL && ip != NULL) { + DBG((dbgo,"Got a name '%s' & IP '%s'\n",name,ip)) + /* Check if it is a duplcate */ + for (i = 0; i < nids; i++) { + if (strcmp((*ids)[i]->name, name) == 0 + && strcmp((*ids)[i]->ip, ip) == 0) + break; /* yes */ + } + if (i < nids) { + DBG((dbgo,"Duplicate\n")) + free(name); + free(ip); + } else { + ccast_id **tids; + if ((tids = realloc(*ids, (nids + 2) * sizeof(ccast_id *))) == NULL + || (*ids = tids, (*ids)[nids] = malloc(sizeof(ccast_id)), (*ids)[nids]) == NULL) { + DBG((dbgo,"realloc/malloc fail\n")) + free(name); + free(ip); + free_ccids(*ids); + *ids = NULL; + return 1; + } else { + DBG((dbgo,"Adding entry\n")) + (*ids)[nids]->name = name; + (*ids)[nids]->ip = ip; + (*ids)[++nids] = NULL; /* End marker */ + } + } + } + } + } + + DBG((dbgo,"receive_mDNS() returning %d in list\n",nids)) + + return 0; +} + +/* ==================================================================== */ + +/* Get a list of Chromecasts. Return NULL on error */ +/* Last pointer in array is NULL */ +/* Takes 0.5 second to return */ +ccast_id **get_ccids() { + ccast_id **ids = NULL; + int i, j; + unsigned int smsec; + SOCKET ssock, rsock; + + if (init_mDNS()) { + DBG((dbgo,"init_mDNS() failed\n")) + return NULL; + } + + if (init_send_mDNS(&ssock)) { + DBG((dbgo,"init_send_mDNS() failed\n")) + return NULL; + } + + if (init_receive_mDNS(&rsock)) { + DBG((dbgo,"init_receive_mDNS() failed\n")) + closesocket(ssock); + return NULL; + } + + smsec = msec_time(); + + DBG((dbgo,"Sending mDNS query:\n")) + if (send_mDNS(ssock)) { + DBG((dbgo,"send_mDNS() #1 failed\n")) + closesocket(ssock); + closesocket(rsock); + return NULL; + } + + if (receive_mDNS(rsock, &ids, 100)) { + DBG((dbgo,"receive_mDNS() #1 failed\n")) + closesocket(ssock); + closesocket(rsock); + return NULL; + } + + if (ids == NULL && (msec_time() - smsec) < 200) { + + DBG((dbgo,"Sending another mDNS query:\n")) + if (send_mDNS(ssock)) { + DBG((dbgo,"send_mDNS() #2 failed\n")) + closesocket(ssock); + closesocket(rsock); + return NULL; + } + + if (receive_mDNS(rsock, &ids, 500)) { + DBG((dbgo,"receive_mDNS() #2 failed\n")) + closesocket(ssock); + closesocket(rsock); + return NULL; + } + } + + if (ids == NULL) { + + DBG((dbgo,"Sending a final mDNS query:\n")) + if (send_mDNS(ssock)) { + DBG((dbgo,"send_mDNS() #3 failed\n")) + closesocket(ssock); + closesocket(rsock); + return NULL; + } + + if (receive_mDNS(rsock, &ids, 500)) { + DBG((dbgo,"receive_mDNS() #3 failed\n")) + closesocket(ssock); + closesocket(rsock); + return NULL; + } + } + + closesocket(ssock); + closesocket(rsock); + + /* If no ChromCasts found, return an empty list */ + if (ids == NULL) { + if ((ids = calloc(sizeof(ccast_id *), 1)) == NULL) { + DBG((dbgo,"calloc fail\n")) + return NULL; + } + } + /* Sort the results so that it is stable */ + for (i = 0; ids[i] != NULL && ids[i+1] != NULL; i++) { + for (j = i+1; ids[j] != NULL; j++) { + if (strcmp(ids[i]->name, ids[j]->name) > 0) { + ccast_id *tmp; + tmp = ids[i]; + ids[i] = ids[j]; + ids[j] = tmp; + } + } + } + + return ids; +} + +void ccast_id_copy(ccast_id *dst, ccast_id *src) { + dst->name = strdup(src->name); + dst->ip = strdup(src->ip); +} + +ccast_id *ccast_id_clone(ccast_id *src) { + ccast_id *dst; + + if ((dst = calloc(sizeof(ccast_id), 1)) == NULL) + return dst; + + if (src->name != NULL && (dst->name = strdup(src->name)) == NULL) { + free(dst); + return NULL; + } + if (src->ip != NULL && (dst->ip = strdup(src->ip)) == NULL) { + free(dst->name); + free(dst); + return NULL; + } + return dst; +} + +void ccast_id_del(ccast_id *id) { + free(id->name); + free(id->ip); + free(id); +} + +/* ==================================================================== */ + +/* Read a string with a trailing '.' added. */ +/* Return the next offset or -1 and *rv = NULL on error */ +static int read_string_imp(char **rv, int *slen, ORD8 *buf, int off, int size, int rec) { + int len; + int d1 = 0; + +//printf("~1 read_string_imp called for off 0x%x rec %d\n",off,rec); + + if (rec > 10) /* Too many recursions */ + return -1; /* Error */ + + if (off >= size) + return -1; /* Error */ + + for (;;) { +//printf("~1 top of loop at off 0x%x\n",off); + len = buf[off]; + if (len == 0xc0) { /* Is it an offset marker */ + int poff; +//printf("~1 got pointer\n"); + if ((size - off) < 2) + return -1; + + poff = read_ORD16_be(buf + off); off += 2; + poff -= 0xc000; + + if (poff < 0 || poff >= size) + return -1; + + read_string_imp(rv, slen, buf, poff, size, rec+1); +//if (slen != NULL) printf("~1 after recurse, slen = %d, off = 0x%x\n",*slen,off); + + break; /* we're done */ + + } else { +//printf("~1 got string length %d\n",len); + off++; + if ((off + len) >= size) + return -1; + + if (len == 0) + break; /* we're done */ + + if (rv != NULL) { + memcpy(*rv, buf + off, len); +//(*rv)[len] = '\000'; printf("Copied string %p = '%s'\n",*rv,*rv); + + *rv += len; + } + off += len; + + if (slen != NULL) { + (*slen) += len; +//printf("~1 slen now = %d\n",*slen); + } + + } + d1 = 1; + + if (slen != NULL) + (*slen)++; + + if (rv != NULL) { + (*rv)[0] = '.'; + (*rv)++; + } + } +//if (slen != NULL) printf("~1 returning slen = %d\n",*slen); + return off; +} + +/* Read a string */ +/* Return the next offset or -1 and *rv = NULL on error */ +static int read_string(char **rv, ORD8 *buf, int off, int size) { + int len = 0, toff = off; + char *trv; + +//printf("~1 read_string called for off 0x%x\n",off); + + /* See how long it will be */ + if ((toff = read_string_imp(NULL, &len, buf, off, size, 0)) < 0) { +//printf("~1 read_string_imp length returned error\n"); + return toff; + } +//printf("~1 read_string_imp got length %d\n",len); + + if (len == 0) { +//printf("~1 Got zero length string\n"); + len++; /* Room for null string */ + } + if ((*rv = trv = malloc(len)) == NULL) { + return -1; + } +//printf("Malloced %p\n",*rv); + off = read_string_imp(&trv, NULL, buf, off, size, 0); + if (off >= 0) { + (*rv)[len-1] = '\000'; +//printf("~1 read string ok: %p = '%s'\n",*rv, *rv); + } +//else printf("~1 reading string failed\n"); + return off; +} + +/* Parse an mDNS query */ +/* Return updated off value or -1 on error */ +int parse_query(ORD8 *buf, int off, int size) { + char *sv; + int qtype, qclass; + + DBG((dbgo,"Parsing query at 0x%x:\n",off)) + if ((off = read_string(&sv, buf, off, size)) < 0) + return -1; + DBG((dbgo," Got string '%s'\n",sv)) + free(sv); + + if ((size - off) < 2) + return -1; + qtype = read_ORD16_be(buf + off); off += 2; + DBG((dbgo," QTYPE = 0x%04x\n",qtype)) + + if ((size - off) < 2) + return -1; + qclass = read_ORD16_be(buf + off); off += 2; + DBG((dbgo," QCLASS = 0x%04x\n",qtype)) + + return off; +} + +/* Parse an mDNS reply */ +/* Return updated off value or -1 on error */ +int parse_reply(char **pname, char **pip, ORD8 *buf, int off, int size) { + char *sv; + int rtype, rclass, rdlength; + unsigned int ttl; + + DBG((dbgo,"Parsing reply at 0x%x:\n",off)) + if ((off = read_string(&sv, buf, off, size)) < 0) + return -1; + + DBG((dbgo," Got string '%s'\n",sv)) + + if ((size - off) < 2) { + free(sv); + return -1; + } + rtype = read_ORD16_be(buf + off); off += 2; + DBG((dbgo," RTYPE = %d\n",rtype)) + + if ((size - off) < 2) { + free(sv); + return -1; + } + rclass = read_ORD16_be(buf + off); off += 2; + DBG((dbgo," RCLASS = 0x%04x\n",rclass)) + /* rclass top bit is cache flush bit */ + + if ((rclass & 0x7fff) != DNS_CLASS_IN) { + DBG((dbgo," response has RCLASS != 0x%04x\n",DNS_CLASS_IN)) + free(sv); + return -1; + } + + if ((size - off) < 4) + return -1; + ttl = read_ORD32_be(buf + off); off += 4; + DBG((dbgo," TTL = %u\n",ttl)) + + if ((size - off) < 2) + return -1; + rdlength = read_ORD16_be(buf + off); off += 2; + DBG((dbgo," RDLENGTH = %d\n",rdlength)) + + if ((off + rdlength) > size) { + DBG((dbgo," response RDLENGTH is longer than remaining buffer (%d)\n",size - off)) + free(sv); + return -1; + } + + /* Just decode the replies we need */ + if (rtype == DNS_TYPE_TXT) { /* Check it's a ChromeCast & get its name */ + char *cp; + if ((cp = strchr(sv, '.')) == NULL) { + free(sv); + return -1; + } + *cp = '\000'; + if (strcmp(cp+1, "_googlecast._tcp.local") != 0) { + DBG((dbgo,"Not a chromecast (got '%s')\n",cp+1)) + free(sv); + return -1; + } + + DBG((dbgo," Chromacast '%s'\n", sv)) + if ((*pname = strdup(sv)) == NULL) { + DBG((dbgo,"strdup failed\n")) + free(sv); + return -1; + } + } else if (rtype == DNS_TYPE_A) { + /* Should we check name matches ? */ + if ((*pip = malloc(3 * 4 + 3 + 1)) == NULL) { + DBG((dbgo,"malloc failed\n")) + free(*pname); + free(sv); + } + sprintf(*pip, "%d.%d.%d.%d", buf[off], buf[off+1], buf[off+2], buf[off+3]); + DBG((dbgo," V4 address = %s\n",*pip)) + + } else if (rtype == DNS_TYPE_AAAA) { /* The IPV6 address */ + /* Should we check name matches ? */ + if ((*pip = malloc(8 * 4 + 7 + 1)) == NULL) { + DBG((dbgo,"malloc failed\n")) + free(*pname); + free(sv); + } + sprintf(*pip, "%x:%x:%x:%x:%x:%x:%x:%x", + buf[off+0] * 245 + buf[off+1], + buf[off+2] * 245 + buf[off+3], + buf[off+4] * 245 + buf[off+5], + buf[off+6] * 245 + buf[off+7], + buf[off+8] * 245 + buf[off+9], + buf[off+10] * 245 + buf[off+11], + buf[off+12] * 245 + buf[off+13], + buf[off+14] * 245 + buf[off+15]); + DBG((dbgo," V6 address = %s\n",*pip)) + + } else { + DBG((dbgo," Skipping reply at 0x%x\n",off)) + } + off += rdlength; + free(sv); + + return off; +} + +/* Parse an mDNS reply into a ChromCast name & IP address */ +/* Allocate and return name and IP on finding ChromeCast reply, NULL otherwise */ +/* Return nz on failure */ +static int parse_dns(char **pname, char **pip, ORD8 *buf, int size) { + int i, off = 0; + int id, flags, qdcount, ancount, nscount, arcount; + + DBG((dbgo,"Parsing mDNS reply:\n")) + + *pname = NULL; + *pip = NULL; + + // Parse reply header + if ((size - off) < 2) return 1; + id = read_ORD16_be(buf + off); off += 2; + DBG((dbgo," ID = %d\n",id)) + + if ((size - off) < 2) return 1; + flags = read_ORD16_be(buf + off); off += 2; + DBG((dbgo," FLAGS = 0x%04x\n",flags)) + + if ((size - off) < 2) return 1; + qdcount = read_ORD16_be(buf + off); off += 2; + DBG((dbgo," QDCOUNT = %d\n",qdcount)) + + if ((size - off) < 2) return 1; + ancount = read_ORD16_be(buf + off); off += 2; + DBG((dbgo," ANCOUNT = %d\n",ancount)) + + if ((size - off) < 2) return 1; + nscount = read_ORD16_be(buf + off); off += 2; + DBG((dbgo," NSCOUNT = %d\n",nscount)) + + if ((size - off) < 2) return 1; + arcount = read_ORD16_be(buf + off); off += 2; + DBG((dbgo," ARCOUNT = %d\n",arcount)) + +//printf("~1 after strings, off = 0x%x\n",off); + + // Parse all the queries (QDCOUNT) + for (i = 0; i < qdcount; i++) { + if ((off = parse_query(buf, off, size)) < 0) { + DBG((dbgo," ### Parsing query failed ###\n")) + return 1; + } + } + + // Parse all the answers (ANCOUNT) + for (i = 0; i < ancount; i++) { + if ((off = parse_reply(pname, pip, buf, off, size)) < 0) { + DBG((dbgo," ### Parsing answer failed ###\n")) + return 1; + } + } + + // Parse all the NS records (NSCOUNT) + for (i = 0; i < nscount; i++) { + if ((off = parse_reply(pname, pip, buf, off, size)) < 0) { + DBG((dbgo," ### Parsing NS record failed ###\n")) + return 1; + } + } + + // Parse all the addition RR answers (ARCOUNT) + for (i = 0; i < arcount; i++) { + if ((off = parse_reply(pname, pip, buf, off, size)) < 0) { + DBG((dbgo," ### Parsing additional records failed ###\n")) + return 1; + } + } + return 0; +} + +/* + Analysis of ChromeCast reply + + 0000: 00 00 84 00 00 00 00 01 00 00 00 05 0b 5f 67 6f ............._go + + ID 0x0000 // ID 0 + Flags 0x8400 // Standard query response, No error + QDCOUNT 0x0000 // No queries + ANCOUNT 0x0001 // 1 Answer + NSCOUNT 0x0000 + ARCOUNT 0x0005 // 5 Additional records + + // Answer + 0b 5f 67 6f _go + 0010: 6f 67 6c 65 63 61 73 74 oglecast + + 0010: 04 5f 74 63 70 _tcp + + 0010: 05 6c 6f lo + 0020: 63 61 6c 00 cal. + + "_googlecast" "_tcp" "cal" "" //TLD + + 0020: 00 0c 00 01 00 00 11 94 00 11 0e 43 ...........C + + RTYPE 0x000c + RCLASS 0x0001 + TTL 0x00001194 + RDLENGTH 0x0011 + + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + 0000: 00 00 84 00 00 00 00 01 00 00 00 05 0b 5f 67 6f ............._go + 0010: 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f oglecast._tcp.lo + 0020: 63 61 6c 00 00 0c 00 01 00 00 11 94 00 11 0e 43 cal............C + 0030: 68 72 6f 6d 65 63 61 73 74 36 38 39 32 c0 0c c0 hromecast6892... + 0040: 2e 00 10 80 01 00 00 11 94 00 67 23 69 64 3d 63 ..........g#id=c + 0050: 31 37 61 65 38 38 32 65 37 65 31 38 37 35 64 30 17ae882e7e1875d0 + 0060: 31 33 65 64 32 31 32 63 31 36 62 62 64 34 30 05 13ed212c16bbd40. + 0070: 76 65 3d 30 32 0d 6d 64 3d 43 68 72 6f 6d 65 63 ve=02.md=Chromec + 0080: 61 73 74 12 69 63 3d 2f 73 65 74 75 70 2f 69 63 ast.ic=/setup/ic + 0090: 6f 6e 2e 70 6e 67 11 66 6e 3d 43 68 72 6f 6d 65 on.png.fn=Chrome + 00a0: 63 61 73 74 36 38 39 32 04 63 61 3d 35 04 73 74 cast6892.ca=5.st + 00b0: 3d 30 c0 2e 00 21 80 01 00 00 00 78 00 17 00 00 =0...!.....x.... + 00c0: 00 00 1f 49 0e 43 68 72 6f 6d 65 63 61 73 74 36 ...I.Chromecast6 + 00d0: 38 39 32 c0 1d c0 c4 00 01 80 01 00 00 00 78 00 892...........x. + 00e0: 04 0a 00 00 80 c0 2e 00 2f 80 01 00 00 11 94 00 ......../....... + 00f0: 09 c0 2e 00 05 00 00 80 00 40 c0 c4 00 2f 80 01 .........@.../.. + 0100: 00 00 00 78 00 05 c0 c4 00 01 40 ...x......@ + + +*/ + +/* ================================================================== */ +/* Debug */ + +// Print bytes as hex to stdout +void cc_dump_bytes(FILE *fp, char *pfx, unsigned char *buf, int len) { + int i, j, ii; + char oline[200] = { '\000' }, *bp = oline; + for (i = j = 0; i < len; i++) { + if ((i % 16) == 0) + bp += sprintf(bp,"%s%04x:",pfx,i); + bp += sprintf(bp," %02x",buf[i]); + if ((i+1) >= len || ((i+1) % 16) == 0) { + for (ii = i; ((ii+1) % 16) != 0; ii++) + bp += sprintf(bp," "); + bp += sprintf(bp," "); + for (; j <= i; j++) { + if (!(buf[j] & 0x80) && isprint(buf[j])) + bp += sprintf(bp,"%c",buf[j]); + else + bp += sprintf(bp,"."); + } + bp += sprintf(bp,"\n"); + fprintf(fp, "%s", oline); + bp = oline; + } + } +} + diff --git a/ccast/ccmdns.h b/ccast/ccmdns.h new file mode 100644 index 0000000..5144448 --- /dev/null +++ b/ccast/ccmdns.h @@ -0,0 +1,42 @@ +#ifndef CCMDNST_H + +/* + * Argyll Color Correction System + * ChromCast mDNS support + * + * Author: Graeme W. Gill + * Date: 28/8/2014 + * + * Copyright 2014 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License2.txt file for licencing details. + * + */ + +/* + * This class provides simple access to the Google ChromeCast + * for the purposes of generating Video Test patches. + */ + +/* A record of a Chromecast that may be accessed */ +struct _ccast_id { + char *name; /* Chromecast name */ + char *ip; /* IP address as string (ie. "10.0.0.128") */ +}; typedef struct _ccast_id ccast_id; + +/* Get a list of Chromecasts. Return NULL on error */ +/* Last pointer in array is NULL */ +/* Takes 0.5 second to return */ +ccast_id **get_ccids(void); + +/* Free up what get_ccids returned */ +void free_ccids(ccast_id **ids); + +void ccast_id_copy(ccast_id *dst, ccast_id *src); +ccast_id *ccast_id_clone(ccast_id *src); +void ccast_id_del(ccast_id *id); + +#define CCMDNST_H +#endif /* CCMDNST_H */ diff --git a/ccast/ccmes.c b/ccast/ccmes.c new file mode 100644 index 0000000..dbd5fd9 --- /dev/null +++ b/ccast/ccmes.c @@ -0,0 +1,334 @@ + + +/* + * Class to deal with protobuf messages + * to/from ChromCast. + * + * Author: Graeme W. Gill + * Date: 3/9/2014 + * + * Copyright 2014 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License2.txt file for licencing details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#include +#ifndef SALONEINSTLIB +#include "numlib.h" +#else +#include "numsup.h" +#endif + +#include "ssl.h" /* axTLS header */ +#include "yajl.h" +#include "conv.h" +#include "ccpacket.h" +#include "cast_channel.pb-c.h" +#include "ccmes.h" + +#undef LOWVERBTRACE /* Low verboseness message trace */ +#undef DEBUG /* Full message trace + debug */ + +/* ------------------------------------------------------------------- */ + +#ifdef DEBUG +# define DBG(xxx) fprintf xxx ; +#else +# define DBG(xxx) ; +#endif /* DEBUG */ +# define dbgo stdout + +/* Error message from error number */ +char *ccmessv_emes(ccmessv_err rv) { +#ifndef SERVER_ONLY + if (rv & 0x10000000) { + return ccmessv_emes(rv & 0x0fffffff); + } +#endif + + switch(rv) { + case ccmessv_OK: + return "ccmes: OK"; + case ccmessv_context: + return "ccmes: getting a ssl contextfailed"; + case ccmessv_malloc: + return "ccmes: malloc failed"; + case ccmessv_connect: + return "ccmes: connecting to host failed"; + case ccmessv_ssl: + return "ccmes: ssl connect to host failed"; + + case ccmessv_send: + return "ccmes: message failed to send"; + case ccmessv_recv: + return "ccmes: failed to receive"; + case ccmessv_unpack: + return "ccmes: failed to unpack"; + case ccmessv_timeout: + return "ccmes: i/o has timed out"; + case ccmessv_closed: + return "ccmes: connection has been closed"; + } + + return "Uknown ccmessv error"; +} + +#if defined(LOWVERBTRACE) || defined(DEBUG) +static void mes_dump(ccmes *mes, char *pfx) { +#ifdef DEBUG + fprintf(dbgo,"%s message:\n",pfx); + fprintf(dbgo," source_id = '%s'\n",mes->source_id); + fprintf(dbgo," destination_id = '%s'\n",mes->destination_id); + fprintf(dbgo," namespace = '%s'\n",mes->namespace); + if (mes->binary) { + fprintf(dbgo," payload =\n"); + cc_dump_bytes(dbgo," ", mes->data, mes->bin_len); + } else { + fprintf(dbgo," payload = '%s'\n",mes->data); + } +#else + fprintf(dbgo,"%s '%s'",pfx,mes->namespace); +#endif + if (mes->binary) { +#ifdef DEBUG + fprintf(dbgo," %d bytes of binary data:\n",mes->bin_len); + cc_dump_bytes(dbgo," ", mes->data, mes->bin_len); +#else + fprintf(dbgo,", %d bytes of bin data:\n",mes->bin_len); +#endif + } else { + /* Would like to pretty print the JSON data */ + /* ie. convert json_reformat.c to a function */ +#ifdef DEBUG + fprintf(dbgo," %d bytes of text data:\n",strlen(mes->data)); + fprintf(dbgo," '%s'\n",mes->data); +#else + yajl_val tnode, v, i; + int len = strlen((char *)mes->data); + tnode = yajl_tree_parse((char *)mes->data, NULL, 0); + if (tnode == NULL) { + fprintf(dbgo,", %d bytes of text data\n",len); + } else { + if ((v = yajl_tree_get_first(tnode, "type", yajl_t_string)) != NULL) { + char *mtype = YAJL_GET_STRING(v); + if ((i = yajl_tree_get_first(tnode, "requestId", yajl_t_number)) != NULL) { + int rqid = YAJL_GET_INTEGER(i); + fprintf(dbgo,", %d bytes of JSON data, type '%s' id %d\n", + len,mtype,rqid); + } else { + fprintf(dbgo,", %d bytes of JSON data, type '%s'\n",len,mtype); + } + } else { + fprintf(dbgo,", %d bytes of JSON data\n",len); + } + } +#endif + } +} +#endif + +/* Transfer the data from one message to another */ +void ccmes_transfer(ccmes *dst, ccmes *src) { + *dst = *src; /* Struct copy */ + memset((void *)src, 0, sizeof(*src)); +} + +/* Initialise a message */ +void ccmes_init(ccmes *mes) { + memset((void *)mes, 0, sizeof(*mes)); +} + +/* Free up just the contents */ +void ccmes_empty(ccmes *mes) { + if (mes->tnode != NULL) + yajl_tree_free(mes->tnode); + + if (mes->data != NULL) + free(mes->data); + + memset((void *)mes, 0, sizeof(*mes)); +} + +/* Free up the message and its contents */ +void ccmes_del(ccmes *mes) { + if (mes != NULL) { + ccmes_empty(mes); + free(mes); + } +} + +/* Send a normal raw message */ +/* Return ccmessv_err on error */ +ccmessv_err send_ccmessv(ccmessv *p, ccmes *mes) { + ccpacket_err perr; + ORD8 *buf; + unsigned int len; + Extensions__Api__CastChannel__CastMessage msg + = EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__INIT; + + if (p->pk == NULL) + return ccmessv_closed; + +#if defined(LOWVERBTRACE) || defined(DEBUG) + mes_dump(mes, "Send"); +#endif + + msg.protocol_version + = EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__PROTOCOL_VERSION__CASTV2_1_0; + msg.source_id = mes->source_id; + msg.destination_id = mes->destination_id; + msg.namespace_ = mes->namespace; + + if (mes->binary) { + msg.payload_type = EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__PAYLOAD_TYPE__BINARY; + msg.has_payload_binary = 1; + msg.payload_binary.len = mes->bin_len; + msg.payload_binary.data = (uint8_t *)mes->data; + msg.payload_utf8 = NULL; + } else { + msg.payload_type = EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__PAYLOAD_TYPE__STRING; + msg.payload_utf8 = (char *)mes->data; + msg.has_payload_binary = 0; + msg.payload_binary.len = 0; + msg.payload_binary.data = NULL; + } + len = extensions__api__cast_channel__cast_message__get_packed_size(&msg); + + if ((buf = malloc(len)) == NULL) + return ccmessv_malloc; + + extensions__api__cast_channel__cast_message__pack(&msg, buf); + + amutex_lock(p->slock); + if ((perr = p->pk->send(p->pk, buf, len)) != ccpacket_OK) { + amutex_unlock(p->slock); + free(buf); + if (perr == ccpacket_timeout) + return ccmessv_timeout; + return ccmessv_send; + } + amutex_unlock(p->slock); + free(buf); + + return ccmessv_OK; +} + +/* Receive a raw message. ccmes_clear() or ccmes_del() afterwards */ +/* Fills in tnode, mtype, rqid */ +/* Return ccmessv_err on error */ +ccmessv_err receive_ccmessv(ccmessv *p, ccmes *mes) { + ccpacket_err perr; + ORD8 *buf; + unsigned int len; + Extensions__Api__CastChannel__CastMessage *msg; + + if (p->pk == NULL) + return ccmessv_closed; + + if ((perr = p->pk->receive(p->pk, &buf, &len)) != ccpacket_OK) { + if (perr == ccpacket_timeout) + return ccmessv_timeout; + return ccmessv_recv; + } + + msg = extensions__api__cast_channel__cast_message__unpack(NULL, len, buf); + if (msg == NULL) + return ccmessv_unpack; + + ccmes_init(mes); + + if ((mes->source_id = strdup(msg->source_id)) == NULL) + return ccmessv_malloc; + if ((mes->destination_id = strdup(msg->destination_id)) == NULL) + return ccmessv_malloc; + if ((mes->namespace = strdup(msg->namespace_)) == NULL) + return ccmessv_malloc; + + if (msg->payload_type == EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__PAYLOAD_TYPE__BINARY) { + mes->binary = 1; + if ((mes->data = malloc(msg->payload_binary.len)) == NULL) + return ccmessv_malloc; + memcpy(mes->data, msg->payload_binary.data, msg->payload_binary.len); + mes->bin_len = msg->payload_binary.len; + } else { + mes->binary = 0; + if ((mes->data = (ORD8 *)strdup(msg->payload_utf8)) == NULL) + return ccmessv_malloc; + } + extensions__api__cast_channel__cast_message__free_unpacked(msg, NULL); + +#if defined(LOWVERBTRACE) || defined(DEBUG) + mes_dump(mes, "Recv"); +#endif + + /* Parse out some information if we can */ + mes->mtype = NULL; + mes->rqid = 0; + if (!mes->binary && mes->tnode == NULL) { + yajl_val tyn, idn; + char errbuf[1024]; + + if ((mes->tnode = yajl_tree_parse((char *)mes->data, errbuf, sizeof(errbuf))) == NULL) { + DBG((dbgo,"ccthread: yajl_tree_parse failed with '%s'\n",errbuf)) + } else if ((tyn = yajl_tree_get_first(mes->tnode, "type", yajl_t_string)) == NULL) { + DBG((dbgo,"ccthread: no type\n")) + } else { + mes->mtype = YAJL_GET_STRING(tyn); + if ((idn = yajl_tree_get_first(mes->tnode, "requestId", yajl_t_number)) != NULL) + mes->rqid = YAJL_GET_INTEGER(idn); + else { + DBG((dbgo,"ccthread: no id\n")) + } + } + } + + return ccmessv_OK; +} + +/* Delete the ccmessv */ +void del_ccmessv(ccmessv *p) { + if (p != NULL) { + amutex_del(p->slock); + if (p->pk != NULL) + p->pk->del(p->pk); + free(p); + } +} + +/* Create an ccmessv. We take ownership of pk */ +/* Return NULL on error */ +ccmessv *new_ccmessv(ccpacket *pk) { + ccmessv *p = NULL; + + if ((p = (ccmessv *)calloc(1, sizeof(ccmessv))) == NULL) { + DBG((dbgo, "calloc failed\n")) + return NULL; + } + + amutex_init(p->slock); + + p->pk = pk; + + /* Init method pointers */ + p->del = del_ccmessv; + p->send = send_ccmessv; + p->receive = receive_ccmessv; + + return p; +} + diff --git a/ccast/ccmes.h b/ccast/ccmes.h new file mode 100644 index 0000000..5eae02a --- /dev/null +++ b/ccast/ccmes.h @@ -0,0 +1,103 @@ + +#ifndef CCMES + +/* + * Class to deal with protobuf messages + * to/from ChromCast. + * + * Author: Graeme W. Gill + * Date: 3/9/2014 + * + * Copyright 2014 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License2.txt file for licencing details. + * + */ + +#ifdef __cplusplus + extern "C" { +#endif + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ +/* A single message */ +typedef struct _ccmes { + struct _ccmes *next; /* Linked list */ + yajl_val tnode; /* If not NULL, top node of parsed data */ + char *mtype; /* If not NULL, "type". Points into tnode data */ + int rqid; /* "requestId", 0 by default */ + + char *source_id; /* Source name */ + char *destination_id; /* Destination name */ + char *namespace; /* Channel id */ + int binary; /* Binary data flag */ + ORD8 *data; /* String or binary */ + ORD32 bin_len; /* Binary data length */ +} ccmes; + +/* Transfer the data from one message to another */ +void ccmes_transfer(ccmes *dst, ccmes *src); + +/* Initialise a message */ +void ccmes_init(ccmes *mes); + +/* Free up just the contents */ +void ccmes_empty(ccmes *mes); + +/* Free up the message and its contents */ +void ccmes_del(ccmes *mes); + +/* - - - - - - - - - - - - - - - - - - - - - - - - */ + +typedef enum { + ccmessv_OK = 0, + ccmessv_malloc, /* malloc failed */ + ccmessv_context, /* Getting a ssl context failed */ + ccmessv_connect, /* Connecting to host failed */ + ccmessv_ssl, /* Establishing SSL connection to host failed */ + + ccmessv_send, /* Message body failed to send */ + ccmessv_recv, /* No body or failed to read */ + ccmessv_unpack, /* Failed to unpack protobufs */ + ccmessv_timeout, /* Failed due to timeout on i/o operation */ + ccmessv_closed /* Connection has been closed */ +} ccmessv_err; + +/* Error message from error number */ +char *ccmessv_emes(ccmessv_err rv); + + +/* The central facility to send and receive messages */ +typedef struct _ccmessv { + +/* Public: */ + + /* Delete the ccmessv */ + void (*del)(struct _ccmessv *p); + + /* Send a raw message */ + /* Return ccmessv_err on error */ + ccmessv_err (*send)(struct _ccmessv *p, ccmes *mes); + + /* Receive a message. mes->data should be free's after use */ + /* Return ccmessv_err on error */ + ccmessv_err (*receive)(struct _ccmessv *p, ccmes *mes); + + ccpacket *pk; + + amutex slock; /* Send lock protecting */ + +} ccmessv; + +/* Create a new ccmessv object, and hand it the working packet connection. */ +/* (ccmessv does not close it when deleted) */ +/* Return NULL on error */ +ccmessv *new_ccmessv(ccpacket *pk); + +#ifdef __cplusplus + } +#endif + +#define CCMES_H +#endif /* CCMES_H */ diff --git a/ccast/ccpacket.c b/ccast/ccpacket.c new file mode 100644 index 0000000..4c138f2 --- /dev/null +++ b/ccast/ccpacket.c @@ -0,0 +1,501 @@ + + +/* + * Class to deal with TLS connection to ChromCast, + * and send and recieve packat format data. + * + * Author: Graeme W. Gill + * Date: 3/9/2014 + * + * Copyright 2014 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License2.txt file for licencing details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#include +#ifndef SALONEINSTLIB +#include "numlib.h" +#else +#include "numsup.h" +#endif +#include "conv.h" +#include "ssl.h" /* axTLS header */ + +#undef DEBUG +#undef DUMPSDATA /* Send data */ +#undef DUMPRDATA /* Receive data */ + +#if defined(NT) // Windows specific +# if _WIN32_WINNT < 0x0400 +# undef _WIN32_WINNT +# define _WIN32_WINNT 0x0400 // To make it link in VS2005 +# endif +# define WIN32_LEAN_AND_MEAN +# include +# include +# include + +# include +# include +# include + +# define ERRNO GetLastError() +# ifndef ETIMEDOUT +# define ETIMEDOUT WSAETIMEDOUT +# endif + +#else /* !NT = Unix, OS X */ + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +# include +# include +# include + +#undef SYNC_SSL /* Use lock to make sure ssl_read & write don't clash. */ + +typedef int SOCKET; +# define ERRNO errno + +# define INVALID_SOCKET -1 +# define SOCKET_ERROR -1 + +#define closesocket(xxx) close(xxx); + +#endif + +#define CCPACKET_IMPL +#include "ccpacket.h" + +/* ------------------------------------------------------------------- */ + +#ifdef DEBUG +# define dbgo stdout +# define DBG(xxx) fprintf xxx ; +void cc_dump_bytes(FILE *fp, char *pfx, unsigned char *buf, int len); +#else +# define DBG(xxx) ; +#endif /* DEBUG */ + +/* Error message from error number */ +char *ccpacket_emes(ccpacket_err rv) { +#ifndef SERVER_ONLY + if (rv & 0x10000000) { + return ccpacket_emes(rv & 0x0fffffff); + } +#endif + + switch(rv) { + case ccpacket_OK: + return "Packet: OK"; + case ccpacket_context: + return "Packet: getting a ssl contextfailed"; + case ccpacket_malloc: + return "Packet: malloc failed"; + case ccpacket_connect: + return "Packet: connecting to host failed"; + case ccpacket_ssl: + return "Packet: ssl connect to host failed"; + + case ccpacket_timeout: + return "Packet:: i/o timed out"; + case ccpacket_send: + return "Packet: message failed to send"; + case ccpacket_recv: + return "Packet: failed to read message"; + } + + return "Uknown ccpacket error"; +} + +/* Establish an ccpacket connection - implementation */ +static ccpacket_err connect_ccpacket_imp( + ccpacket *p +) { + struct sockaddr_in server; /* Server address */ + uint8_t sesid[32] = { 0 }; + int rv; + +#ifdef NT + WSADATA data; + WSAStartup(MAKEWORD(2,2), &data); +#endif + + server.sin_family = AF_INET; + server.sin_addr.s_addr = inet_addr(p->dip); + server.sin_port = htons((short)p->dport); + + if ((p->ctx = ssl_ctx_new(SSL_SERVER_VERIFY_LATER, 1)) == NULL) { + DBG((dbgo, "connect ssl_ctx_new failed\n")) + return ccpacket_context; + } + + /* Open socket */ + if ((p->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { + DBG((dbgo, "socket() failed with errno %d\n",ERRNO)) + return ccpacket_connect; + } + + /* Make recieve timeout after 100 msec, and send timeout after 2 seconds */ + { + struct linger ling; +#ifdef NT + DWORD tv; +#ifdef SYNC_SSL + tv = 100; +#else + tv = 2000; +#endif + if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, + sizeof(tv))) < 0) { + DBG((dbgo,"setsockopt timout failed with %d, errno %d",rv,ERRNO)) + return ccpacket_connect; + } + tv = 2000; + if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, + sizeof(tv))) < 0) { + DBG((dbgo,"setsockopt timout failed with %d, errno %d",rv,ERRNO)) + return ccpacket_connect; + } +#else + struct timeval tv; +#ifdef SYNC_SSL + tv.tv_sec = 0; + tv.tv_usec = 100 * 1000; +#else + tv.tv_sec = 2; + tv.tv_usec = 0; +#endif + if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, + sizeof(tv))) < 0) { + DBG((dbgo,"setsockopt timout failed with %d, errno %d",rv,ERRNO)) + return ccpacket_connect; + } + tv.tv_sec = 2; + tv.tv_usec = 0; + if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, + sizeof(tv))) < 0) { + DBG((dbgo,"setsockopt timout failed with %d, errno %d",rv,ERRNO)) + return ccpacket_connect; + } +#endif + +#ifdef NEVER /* This stops send timeout working - why ? */ + ling.l_onoff = 1; + ling.l_linger = 2; /* Two seconds */ + if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_LINGER, (const char*)&ling, + sizeof(ling))) < 0) { + DBG((dbgo,"setsockopt timout failed with %d, errno %d",rv,ERRNO)) + return ccpacket_connect; + } +#endif /* NEVER */ + } + + /* Connect */ + if (rv = (connect(p->sock, (struct sockaddr *)&server, sizeof(server))) != 0) { + DBG((dbgo, "TCP connect IP '%s' port %d failed with %d, errno %d\n",p->dip, p->dport,rv,ERRNO)) + return ccpacket_connect; + } + DBG((dbgo, "socket connect IP '%s' port %d success\n",p->dip, p->dport)) + + /* Establish TLS */ + /* Return nz if we can send PNG directly as base64 + bg RGB, */ + /* else have to setup webserver and send URL */ + if ((p->ssl = ssl_client_new(p->ctx, p->sock, sesid, 32)) == NULL) { + DBG((dbgo, "connect IP '%s' port %d ssl_ctx_new failed\n",p->dip, p->dport)) + return ccpacket_ssl; + } + DBG((dbgo, "TLS connect IP '%s' port %d success\n",p->dip, p->dport)) + return ccpacket_OK; +} + +/* Establish an ccpacket connection */ +static ccpacket_err connect_ccpacket( + ccpacket *p, + char *dip, /* Destination IP address */ + int dport /* Destination Port number */ +) { + + if ((p->dip = strdup(dip)) == NULL) + return ccpacket_malloc; + p->dport = dport; + + return connect_ccpacket_imp(p); +} + +static void clear_ccpacket(ccpacket *p); + +/* Re-establish communication */ +static ccpacket_err re_connect_ccpacket( + ccpacket *p +) { + clear_ccpacket(p); + return connect_ccpacket_imp(p); +} + +/* It appears that the axTLS library can't deal with simultanous */ +/* send and recieve, due to the sharing of a single buffer and */ +/* intricate dependence on this in dealing with handshaking, so */ +/* rather than try and fix this limitation, we avoid the problem */ +/* with a lock. Note that we need to make sure that a receive */ +/* doesn't wait for too long, or it will block sends. */ + +/* Send a message */ +/* Return ccpacket_err on error */ +static ccpacket_err send_ccpacket(ccpacket *p, + ORD8 *buf, ORD32 len /* Message body to send */ +) { + int lens, rv; + ORD8 *sbuf; + ORD32 slen; + + if (p->ssl == NULL) + return ccpacket_ssl; + + if ((sbuf = malloc(4 + len)) == NULL) { + return ccpacket_malloc; + } + + write_ORD32_be(len, sbuf); + memcpy(sbuf+4, buf, len); + slen = len + 4; + +#if defined(DEBUG) && defined(DUMPSDATA) + printf("send_ccpacket sending packet:\n"); + cc_dump_bytes(dbgo," ", sbuf, slen); +#endif + + for (lens = 0; lens < slen; lens += rv) { + DBG((dbgo, "Sending packet %d bytes\n",slen - lens)) + if (p->ssl == NULL) { + return ccpacket_ssl; + } +#ifdef SYNC_SSL + amutex_lock(p->lock); +printf("~1 send locked\n"); +#endif + if ((rv = ssl_write(p->ssl, sbuf + lens, slen - lens)) < 0) { + DBG((dbgo, "TLS send body failed with '%s'\n",ssl_error_string(rv))) +#ifdef SYNC_SSL +printf("~1 send unlocked\n"); + amutex_unlock(p->lock); +#endif + free(sbuf); + if (rv == SSL_TIMEDOUT) + return ccpacket_timeout; + return ccpacket_send; + } +#ifdef SYNC_SSL +printf("~1 send unlocked\n"); + amutex_unlock(p->lock); +#endif + } + free(sbuf); + + return ccpacket_OK; +} + +/* Receive a message */ +/* Return ccpacket_err on error */ +static ccpacket_err receive_ccpacket(ccpacket *p, + ORD8 **pbuf, ORD32 *plen /* ccpacket received, free after use */ +) { + ORD8 *ibuf; /* Buffer from ssl_read() */ + int ioff, ilen; /* Remaining data at ioff lenght ilen in ibuf */ + ORD8 hbuf[4], *rbuf = hbuf; /* Header buffer, returned buffer */ + int tlen, clen, rlen; /* Target length, copy length, return length */ + + if (p->ssl == NULL) + return ccpacket_ssl; + +#ifdef SYNC_SSL + amutex_lock(p->lock); +printf("~1 receive locked\n"); +#endif + + /* Until we have 4 bytes for the header */ + for (tlen = 4, rlen = 0; rlen < tlen;) { + ioff = 0; + if ((ilen = ssl_read(p->ssl, &ibuf)) < 0) { + DBG((dbgo, "header recv failed with '%s'\n",ssl_error_string(ilen))) + if (ilen == SSL_OK) /* Hmm. */ + continue; +#ifdef SYNC_SSL +printf("~1 receive unlocked\n"); + amutex_unlock(p->lock); +#endif + if (ilen == SSL_TIMEDOUT) + return ccpacket_timeout; + return ccpacket_recv; + } + DBG((dbgo, "receive_ccpacket read %d bytes\n",ilen)) + if (ilen == 0) + continue; + if ((clen = ilen) > (tlen - rlen)) /* More than we need */ + clen = tlen - rlen; + memcpy(rbuf + rlen, ibuf + ioff, clen); + rlen += clen; + ioff += clen; + ilen -= clen; + } + /* We have ilen left in ibuf at offset ioff */ + DBG((dbgo, "receive_ccpacket %d bytes left over\n",ilen)) + + tlen = read_ORD32_be(ibuf); + DBG((dbgo, "receive_ccpacket expecting %d more bytes\n",tlen)) + + if (tlen < 0 || tlen > 64 * 2014) { +#ifdef SYNC_SSL +printf("~1 receive unlocked\n"); + amutex_unlock(p->lock); +#endif + DBG((dbgo, "receive_ccpacket got bad data length - returning error\n")) + return ccpacket_recv; + } + + if ((rbuf = malloc(tlen)) == NULL) { +#ifdef SYNC_SSL +printf("~1 receive unlocked\n"); + amutex_unlock(p->lock); +#endif + DBG((dbgo, "receive_ccpacket malloc failed\n")) + return ccpacket_malloc; + } + rlen = 0; + + /* Use any data we have left over from first read */ + if (ilen > 0) { + if ((clen = ilen) > (tlen - rlen)) + clen = tlen - rlen; + DBG((dbgo, "receive_ccpacket using %d spair bytesr\n",clen)) + memcpy(rbuf + rlen, ibuf + ioff, clen); + rlen += clen; + ioff += clen; + ilen -= clen; + } + + /* Get the remainder of the body if we need to */ + for (; rlen < tlen;) { + ioff = 0; + if ((ilen = ssl_read(p->ssl, &ibuf)) < 0) { + DBG((dbgo, "body recv failed with '%s'\n",ssl_error_string(ilen))) + if (ilen == SSL_OK) + continue; +#ifdef SYNC_SSL +printf("~1 receive unlocked\n"); + amutex_unlock(p->lock); +#endif + if (ilen == SSL_TIMEDOUT) + return ccpacket_timeout; + return ccpacket_recv; + } + DBG((dbgo, "receive_ccpacket read %d bytes\n",ilen)) + if (ilen == 0) + continue; + if ((clen = ilen) > (tlen - rlen)) + clen = tlen - rlen; + memcpy(rbuf + rlen, ibuf + ioff, clen); + rlen += clen; + ioff += clen; + ilen -= clen; + } +#ifdef SYNC_SSL +printf("~1 receive unlocked\n"); + amutex_unlock(p->lock); +#endif + if (ilen > 0) { /* Hmm. We should keep this for the next read ?*/ + DBG((dbgo, " ################## got %d byts left over ###########\n", ilen)) + } +#if defined(DEBUG) && defined(DUMPRDATA) + printf("receive_ccpacket got:\n"); + cc_dump_bytes(dbgo," ", rbuf, rlen); +#endif + *pbuf = rbuf; + *plen = rlen; + + return ccpacket_OK; +} + +/* Clear the ccpacket */ +static void clear_ccpacket(ccpacket *p) { + if (p != NULL) { + + if (p->ssl != NULL) { + ssl_free(p->ssl); + p->ssl = NULL; + } + if (p->ctx != NULL) { + ssl_ctx_free(p->ctx); + p->ctx = NULL; + } + if (p->sock != INVALID_SOCKET) { + closesocket(p->sock); + p->sock = 0; + } + } +} + +/* Delete the ccpacket */ +static void del_ccpacket(ccpacket *p) { + if (p != NULL) { + clear_ccpacket(p); + if (p->dip != NULL) { + free(p->dip); + p->dip = NULL; + } +#ifdef SYNC_SSL + amutex_del(p->lock); +#endif + free(p); + } +} + +/* Create an ccpacket. */ +/* Return NULL on error */ +ccpacket *new_ccpacket() { + ccpacket *p = NULL; + + if ((p = (ccpacket *)calloc(1, sizeof(ccpacket))) == NULL) { + DBG((dbgo, "calloc failed\n")) + return NULL; + } + +#ifdef SYNC_SSL + amutex_init(p->lock); +#endif + + /* Init method pointers */ + p->del = del_ccpacket; + p->connect = connect_ccpacket; + p->reconnect = re_connect_ccpacket; + p->send = send_ccpacket; + p->receive = receive_ccpacket; + + return p; +} + diff --git a/ccast/ccpacket.h b/ccast/ccpacket.h new file mode 100644 index 0000000..e4f6f76 --- /dev/null +++ b/ccast/ccpacket.h @@ -0,0 +1,89 @@ + +#ifndef PACKET_H + +/* + * Class to deal with TLS connection to ChromCast, + * and send and recieve packat format data. + * + * Author: Graeme W. Gill + * Date: 3/9/2014 + * + * Copyright 2014 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License2.txt file for licencing details. + * + */ + +#ifdef __cplusplus + extern "C" { +#endif + +typedef enum { + ccpacket_OK = 0, + ccpacket_malloc, /* malloc failed */ + ccpacket_context, /* Getting a ssl context failed */ + ccpacket_connect, /* Connecting to host failed */ + ccpacket_ssl, /* Establishing SSL connection to host failed */ + + ccpacket_timeout, /* i/o timed out */ + ccpacket_send, /* Message failed to send */ + ccpacket_recv /* Messafe failed to read */ +} ccpacket_err; + +/* Error message from error number */ +char *ccpacket_emes(ccpacket_err rv); + +typedef struct _ccpacket { + +/* Public: */ + + /* Delete the ccpacket */ + void (*del)(struct _ccpacket *p); + + /* Establish an ccpacket connection */ + /* Return ccpacket_err on error */ + ccpacket_err (*connect)(struct _ccpacket *p, + char *dip, /* Destination IP address */ + int dport /* Destination Port number */ + ); + + /* Clear the connection and then re-stablish it */ + /* Return ccpacket_err on error */ + ccpacket_err (*reconnect)(struct _ccpacket *p); + + /* Send a message */ + /* Return ccpacket_err on error */ + ccpacket_err (*send)(struct _ccpacket *p, + ORD8 *buf, ORD32 len /* Message body to send */ + ); + + /* Receive a message */ + /* Return ccpacket_err on error */ + ccpacket_err (*receive)(struct _ccpacket *p, + ORD8 **pbuf, ORD32 *plen /* ccpacket received, free after use */ + ); + +#ifdef CCPACKET_IMPL +/* Private */ + char *dip; /* Destination IP address */ + int dport; /* Destination Port number */ + SOCKET sock; + SSL_CTX *ctx; + SSL *ssl; + amutex lock; /* Lock to prevent simultanious send & receive */ +#endif + +} ccpacket; + +/* Create a new ccpacket object */ +/* Return NULL on error */ +ccpacket *new_ccpacket(); + +#ifdef __cplusplus + } +#endif + +#define CCPACKET_H +#endif /* CCPACKET_H */ diff --git a/ccast/cctest.c b/ccast/cctest.c new file mode 100644 index 0000000..2c95ae1 --- /dev/null +++ b/ccast/cctest.c @@ -0,0 +1,329 @@ + +/* + * Argyll Color Correction System + * ChromCast test harness + * + * Author: Graeme W. Gill + * Date: 28/8/2014 + * + * Copyright 2014 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License2.txt file for licencing details. + * + */ + +#include +#include +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#ifndef SALONEINSTLIB +#include "numlib.h" +#else +#include "numsup.h" +#endif +#include "ccmdns.h" +#include "ccpacket.h" +#include "ccmes.h" +#include "yajl.h" + +/* Check if JSON is invalid */ +/* Exits if invalid */ +static void check_json(char *mesbuf) { + yajl_val node; + char errbuf[1024]; + + if ((node = yajl_tree_parse(mesbuf, errbuf, sizeof(errbuf))) == NULL) { + printf("yajl_tree_parse of send message failed with '%s'\n",errbuf); + printf("JSON = '%s'\n",mesbuf); + exit(1); + } + yajl_tree_free(node); +} + +/* Return nz on error */ +static int get_a_reply(ccmessv *mes, ORD8 **pdata) { + ccmessv_err merr; + char *source_id; + char *destination_id; + char *namespace; + int binary; /* Flag */ + ORD8 *data; /* String or binary */ + ORD32 bin_len; /* Binary data length */ + + if ((merr = mes->receive(mes, &source_id, &destination_id, &namespace, + &binary, &data, &bin_len)) != ccmessv_OK) { + printf("mes->receive failed with '%s'\n",ccmessv_emes(merr)); + return -1; + } else { + printf("Got message:\n"); + printf(" source_id = '%s'\n",source_id); + printf(" destination_id = '%s'\n",destination_id); + printf(" namespace = '%s'\n",namespace); + printf(" binary = %d\n",binary); + if (binary) { + printf(" payload =\n"); + cc_dump_bytes(stdout, " ", data, bin_len); + } else { + printf(" payload = '%s'\n",data); + } + if (pdata != NULL) + *pdata = data; + else + free(data); + } + return 0; +} + +/* Get replies until we get one with the matching Id, then return */ +static int get_a_reply_id(ccmessv *mes, int tid, ORD8 **pdata) { + ORD8 *data; + int id, rv; + yajl_val node, v; + char errbuf[1024]; + +//printf("~~~ looking for id %d\n",tid); + for (;;) { + int id = -1; + + if ((rv = get_a_reply(mes, &data)) != 0) + return rv; + +// printf("\nGot JSON data to parse: '%s'\n",data); + if ((node = yajl_tree_parse(data, errbuf, sizeof(errbuf))) == NULL) + error("yajl_tree_parse failed with '%s'",errbuf); + + if ((v = yajl_tree_get_first(node, "requestId", yajl_t_number)) != NULL) { + printf("~~~~ Got id %d\n",id); + id = YAJL_GET_INTEGER(v); + } + yajl_tree_free(node); + + if (id == tid) { +//printf("~~~ got our message\n"); + break; + } + free(data); +//printf("~~~ round we go again\n"); + } + if (pdata != NULL) + *pdata = data; + else + free(data); + return 0; +} + +/* Do the authentication sequence */ +static void authenticate(ccmessv *mes) { + + /* (Not needed) */ +} + +int +main( + int argc, + char *argv[] +) { + ccast_id **ids; + int i; + ccpacket *pk; + ccpacket_err perr; + ccmessv *mes; + ccmessv_err merr; + char *sessionId = NULL; + char *transportId = NULL; + char mesbuf[1024]; + + if ((ids = get_ccids()) == NULL) { + error("get_ccids() failed"); + } + + if (ids[0] == NULL) { + error("Found no ChromCasts"); + } + + for (i = 0; ids[i] != NULL; i++) { + printf("Entry %d:\n",i); + printf(" Name: %s\n",ids[i]->name); + printf(" IP: %s\n",ids[i]->ip); + } + + if ((pk = new_ccpacket()) == NULL) { + error("new_ccpacket() failed"); + } + + if ((perr = pk->connect(pk, ids[0]->ip, 8009)) != ccpacket_OK) { + error("ccpacket connect failed with '%s",ccpacket_emes(perr)); + } + printf("Got TLS connection to '%s\n'",ids[0]->name); + + if ((mes = new_ccmessv(pk)) == NULL) { + error("new_ccmessv() failed"); + } + + /* Attempt a connection */ + if ((merr = mes->send(mes, "sender-0", "receiver-0", + "urn:x-cast:com.google.cast.tp.connection", 0, + "{ \"type\": \"CONNECT\" }", 0)) != ccmessv_OK) { + error("mes->send CONNECT failed with '%s'",ccmessv_emes(merr)); + } + + /* Send a ping */ + if ((merr = mes->send(mes, "sender-0", "receiver-0", + "urn:x-cast:com.google.cast.tp.heartbeat", 0, + "{ \"type\": \"PING\" }", 0)) != ccmessv_OK) { + error("mes->send PING failed with '%s'",ccmessv_emes(merr)); + } + + /* Wait for a PONG */ + get_a_reply(mes, NULL); + + authenticate(mes); + + /* Launch the default application */ + /* (Presumably we would use the com.google.cast.receiver channel */ + /* for monitoring and controlling the reciever) */ + if ((merr = mes->send(mes, "sender-0", "receiver-0", + "urn:x-cast:com.google.cast.receiver", 0, + "{ \"requestId\": 1, \"type\": \"LAUNCH\", \"appId\": \"CC1AD845\" }", 0)) != ccmessv_OK) { + error("mes->send LAUNCH failed with '%s'",ccmessv_emes(merr)); + } + + /* Receive the RECEIVER_STATUS status messages until it is ready to cast */ + /* and get the sessionId and transportId */ + /* We get periodic notification messages (requestId=0) as well as */ + /* a response messages (requestId=1) */ + for (;sessionId == NULL || transportId == NULL;) { + ORD8 *data; + yajl_val node, v; + char errbuf[1024]; + + get_a_reply(mes, &data); + +// printf("\nGot JSON data to parse: '%s'\n",data); + if ((node = yajl_tree_parse(data, errbuf, sizeof(errbuf))) == NULL) + error("yajl_tree_parse failed with '%s'",errbuf); + + if ((v = yajl_tree_get_first(node, "sessionId", yajl_t_string)) != NULL) { + sessionId = strdup(YAJL_GET_STRING(v)); +// printf("~1 got sessionId = '%s'\n",sessionId); + } + + if ((v = yajl_tree_get_first(node, "transportId", yajl_t_string)) != NULL) { + transportId = strdup(YAJL_GET_STRING(v)); +// printf("~1 got transportId = '%s'\n",transportId); + } + free(data); + yajl_tree_free(node); + } + + printf("\ngot sessionId = '%s', transportId = '%s'\n",sessionId, transportId); + + printf("\nAbout to send URL\n"); + + /* Connect up to the reciever media channels */ + if ((merr = mes->send(mes, "med_send", transportId, + "urn:x-cast:com.google.cast.tp.connection", 0, + "{ \"type\": \"CONNECT\" }", 0)) != ccmessv_OK) { + error("mes->send CONNECT failed with '%s'",ccmessv_emes(merr)); + } + + // "urn:x-cast:com.google.cast.player.message" + // "urn:x-cast:com.google.cast.media"} + + sprintf(mesbuf, "{ \"requestId\": 2, \"type\": \"LOAD\", \"media\": " + "{ \"contentId\": \"http://www.argyllcms.com/ArgyllCMSLogo.gif\",\"streamType\": \"LIVE\"," + "\"contentType\": \"image/gif\" } }"); + +// "\"contentType\": \"image/gif\", \"duration\" : 0.1 }, \"autplay\": \"true\" }"); + + check_json(mesbuf); + + /* Send the LOAD command */ + if ((merr = mes->send(mes, "med_send", transportId, + "urn:x-cast:com.google.cast.media", 0, + mesbuf, 0 + )) != ccmessv_OK) { + error("mes->send LOAD failed with '%s'",ccmessv_emes(merr)); + } + + if (get_a_reply_id(mes, 2, NULL) != 0) + exit(1); + + msec_sleep(2000); + +#ifdef NEVER + /* Check the media status */ + if ((merr = mes->send(mes, "med_send", transportId, + "urn:x-cast:com.google.cast.media", 0, + "{ \"requestId\": 3, \"type\": \"GET_STATUS\" }", 0)) != ccmessv_OK) { + error("mes->send GET_STATUS failed with '%s'",ccmessv_emes(merr)); + } + + if (get_a_reply_id(mes, 3, NULL) != 0) + exit(1); +#endif + + sprintf(mesbuf, "{ \"requestId\": 4, \"type\": \"LOAD\", \"media\": { \"contentId\": \"http://www.argyllcms.com/testing.png\",\"streamType\": \"LIVE\",\"contentType\": \"image/png\" } }"); + +// sprintf(mesbuf, "{ \"requestId\": 4, \"type\": \"LOAD\", \"media\": { \"contentId\": \"http://www.argyllcms.com/testing.tif\",\"streamType\": \"LIVE\",\"contentType\": \"image/tiff\" } }"); +// sprintf(mesbuf, "{ \"requestId\": 4, \"type\": \"LOAD\", \"media\": { \"contentId\": \"http://www.argyllcms.com/doc/sl.jpg\",\"streamType\": \"LIVE\",\"contentType\": \"image/jpeg\" } }"); + check_json(mesbuf); + + /* Send the LOAD command */ + if ((merr = mes->send(mes, "med_send", transportId, + "urn:x-cast:com.google.cast.media", 0, + mesbuf, 0 + )) != ccmessv_OK) { + error("mes->send LOAD failed with '%s'",ccmessv_emes(merr)); + } + + if (get_a_reply_id(mes, 4, NULL) != 0) + exit(1); + + msec_sleep(2000); + +#ifdef NEVER + /* Check the media status */ + if ((merr = mes->send(mes, "med_send", transportId, + "urn:x-cast:com.google.cast.media", 0, + "{ \"requestId\": 5, \"type\": \"GET_STATUS\" }", 0)) != ccmessv_OK) { + error("mes->send GET_STATUS failed with '%s'",ccmessv_emes(merr)); + } + + if (get_a_reply_id(mes, 5, NULL) != 0) + exit(1); +#endif + +#ifdef NEVER + /* Try and send it an image URL */ + if ((merr = mes->send(mes, "med_send", transportId, + "urn:x-cast:com.google.cast.media", 0, +// "{ \"imageUrl\": \"http://www.argyllcms.com/ArgyllCMSLogo.gif\", \"requestId\": 6 }", 0)) != ccmessv_OK) { + "{ \"url\": \"http://www.argyllcms.com/ArgyllCMSLogo.gif\", \"requestId\": 2 }", 0)) != ccmessv_OK) { + error("mes->send imageUrl failed with '%s'",ccmessv_emes(merr)); + } + +#endif + /* Wait for any reply */ + for (;;) { + if (get_a_reply(mes, NULL) != 0) + break; + } + + // ~~999 + + mes->del(mes); + pk->del(pk); + + printf("Disconnected from '%s'\n",ids[0]->name); + + free_ccids(ids); + + return 0; +} + diff --git a/ccast/chan/cast_channel.pb-c.c b/ccast/chan/cast_channel.pb-c.c new file mode 100644 index 0000000..ec79608 --- /dev/null +++ b/ccast/chan/cast_channel.pb-c.c @@ -0,0 +1,593 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: cast_channel.proto */ + +/* Do not generate deprecated warnings for self */ +#ifndef PROTOBUF_C__NO_DEPRECATED +#define PROTOBUF_C__NO_DEPRECATED +#endif + +#include "cast_channel.pb-c.h" +void extensions__api__cast_channel__cast_message__init + (Extensions__Api__CastChannel__CastMessage *message) +{ + static Extensions__Api__CastChannel__CastMessage init_value = EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__INIT; + *message = init_value; +} +size_t extensions__api__cast_channel__cast_message__get_packed_size + (const Extensions__Api__CastChannel__CastMessage *message) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__cast_message__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t extensions__api__cast_channel__cast_message__pack + (const Extensions__Api__CastChannel__CastMessage *message, + uint8_t *out) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__cast_message__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t extensions__api__cast_channel__cast_message__pack_to_buffer + (const Extensions__Api__CastChannel__CastMessage *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__cast_message__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Extensions__Api__CastChannel__CastMessage * + extensions__api__cast_channel__cast_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Extensions__Api__CastChannel__CastMessage *) + protobuf_c_message_unpack (&extensions__api__cast_channel__cast_message__descriptor, + allocator, len, data); +} +void extensions__api__cast_channel__cast_message__free_unpacked + (Extensions__Api__CastChannel__CastMessage *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__cast_message__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void extensions__api__cast_channel__auth_challenge__init + (Extensions__Api__CastChannel__AuthChallenge *message) +{ + static Extensions__Api__CastChannel__AuthChallenge init_value = EXTENSIONS__API__CAST_CHANNEL__AUTH_CHALLENGE__INIT; + *message = init_value; +} +size_t extensions__api__cast_channel__auth_challenge__get_packed_size + (const Extensions__Api__CastChannel__AuthChallenge *message) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__auth_challenge__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t extensions__api__cast_channel__auth_challenge__pack + (const Extensions__Api__CastChannel__AuthChallenge *message, + uint8_t *out) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__auth_challenge__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t extensions__api__cast_channel__auth_challenge__pack_to_buffer + (const Extensions__Api__CastChannel__AuthChallenge *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__auth_challenge__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Extensions__Api__CastChannel__AuthChallenge * + extensions__api__cast_channel__auth_challenge__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Extensions__Api__CastChannel__AuthChallenge *) + protobuf_c_message_unpack (&extensions__api__cast_channel__auth_challenge__descriptor, + allocator, len, data); +} +void extensions__api__cast_channel__auth_challenge__free_unpacked + (Extensions__Api__CastChannel__AuthChallenge *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__auth_challenge__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void extensions__api__cast_channel__auth_response__init + (Extensions__Api__CastChannel__AuthResponse *message) +{ + static Extensions__Api__CastChannel__AuthResponse init_value = EXTENSIONS__API__CAST_CHANNEL__AUTH_RESPONSE__INIT; + *message = init_value; +} +size_t extensions__api__cast_channel__auth_response__get_packed_size + (const Extensions__Api__CastChannel__AuthResponse *message) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__auth_response__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t extensions__api__cast_channel__auth_response__pack + (const Extensions__Api__CastChannel__AuthResponse *message, + uint8_t *out) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__auth_response__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t extensions__api__cast_channel__auth_response__pack_to_buffer + (const Extensions__Api__CastChannel__AuthResponse *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__auth_response__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Extensions__Api__CastChannel__AuthResponse * + extensions__api__cast_channel__auth_response__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Extensions__Api__CastChannel__AuthResponse *) + protobuf_c_message_unpack (&extensions__api__cast_channel__auth_response__descriptor, + allocator, len, data); +} +void extensions__api__cast_channel__auth_response__free_unpacked + (Extensions__Api__CastChannel__AuthResponse *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__auth_response__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void extensions__api__cast_channel__auth_error__init + (Extensions__Api__CastChannel__AuthError *message) +{ + static Extensions__Api__CastChannel__AuthError init_value = EXTENSIONS__API__CAST_CHANNEL__AUTH_ERROR__INIT; + *message = init_value; +} +size_t extensions__api__cast_channel__auth_error__get_packed_size + (const Extensions__Api__CastChannel__AuthError *message) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__auth_error__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t extensions__api__cast_channel__auth_error__pack + (const Extensions__Api__CastChannel__AuthError *message, + uint8_t *out) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__auth_error__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t extensions__api__cast_channel__auth_error__pack_to_buffer + (const Extensions__Api__CastChannel__AuthError *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__auth_error__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Extensions__Api__CastChannel__AuthError * + extensions__api__cast_channel__auth_error__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Extensions__Api__CastChannel__AuthError *) + protobuf_c_message_unpack (&extensions__api__cast_channel__auth_error__descriptor, + allocator, len, data); +} +void extensions__api__cast_channel__auth_error__free_unpacked + (Extensions__Api__CastChannel__AuthError *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__auth_error__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void extensions__api__cast_channel__device_auth_message__init + (Extensions__Api__CastChannel__DeviceAuthMessage *message) +{ + static Extensions__Api__CastChannel__DeviceAuthMessage init_value = EXTENSIONS__API__CAST_CHANNEL__DEVICE_AUTH_MESSAGE__INIT; + *message = init_value; +} +size_t extensions__api__cast_channel__device_auth_message__get_packed_size + (const Extensions__Api__CastChannel__DeviceAuthMessage *message) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__device_auth_message__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t extensions__api__cast_channel__device_auth_message__pack + (const Extensions__Api__CastChannel__DeviceAuthMessage *message, + uint8_t *out) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__device_auth_message__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t extensions__api__cast_channel__device_auth_message__pack_to_buffer + (const Extensions__Api__CastChannel__DeviceAuthMessage *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__device_auth_message__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Extensions__Api__CastChannel__DeviceAuthMessage * + extensions__api__cast_channel__device_auth_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Extensions__Api__CastChannel__DeviceAuthMessage *) + protobuf_c_message_unpack (&extensions__api__cast_channel__device_auth_message__descriptor, + allocator, len, data); +} +void extensions__api__cast_channel__device_auth_message__free_unpacked + (Extensions__Api__CastChannel__DeviceAuthMessage *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &extensions__api__cast_channel__device_auth_message__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +const ProtobufCEnumValue extensions__api__cast_channel__cast_message__protocol_version__enum_values_by_number[1] = +{ + { "CASTV2_1_0", "EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__PROTOCOL_VERSION__CASTV2_1_0", 0 }, +}; +static const ProtobufCIntRange extensions__api__cast_channel__cast_message__protocol_version__value_ranges[] = { +{0, 0},{0, 1} +}; +const ProtobufCEnumValueIndex extensions__api__cast_channel__cast_message__protocol_version__enum_values_by_name[1] = +{ + { "CASTV2_1_0", 0 }, +}; +const ProtobufCEnumDescriptor extensions__api__cast_channel__cast_message__protocol_version__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "extensions.api.cast_channel.CastMessage.ProtocolVersion", + "ProtocolVersion", + "Extensions__Api__CastChannel__CastMessage__ProtocolVersion", + "extensions.api.cast_channel", + 1, + extensions__api__cast_channel__cast_message__protocol_version__enum_values_by_number, + 1, + extensions__api__cast_channel__cast_message__protocol_version__enum_values_by_name, + 1, + extensions__api__cast_channel__cast_message__protocol_version__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; +const ProtobufCEnumValue extensions__api__cast_channel__cast_message__payload_type__enum_values_by_number[2] = +{ + { "STRING", "EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__PAYLOAD_TYPE__STRING", 0 }, + { "BINARY", "EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__PAYLOAD_TYPE__BINARY", 1 }, +}; +static const ProtobufCIntRange extensions__api__cast_channel__cast_message__payload_type__value_ranges[] = { +{0, 0},{0, 2} +}; +const ProtobufCEnumValueIndex extensions__api__cast_channel__cast_message__payload_type__enum_values_by_name[2] = +{ + { "BINARY", 1 }, + { "STRING", 0 }, +}; +const ProtobufCEnumDescriptor extensions__api__cast_channel__cast_message__payload_type__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "extensions.api.cast_channel.CastMessage.PayloadType", + "PayloadType", + "Extensions__Api__CastChannel__CastMessage__PayloadType", + "extensions.api.cast_channel", + 2, + extensions__api__cast_channel__cast_message__payload_type__enum_values_by_number, + 2, + extensions__api__cast_channel__cast_message__payload_type__enum_values_by_name, + 1, + extensions__api__cast_channel__cast_message__payload_type__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; +static const ProtobufCFieldDescriptor extensions__api__cast_channel__cast_message__field_descriptors[7] = +{ + { + "protocol_version", + 1, + PROTOBUF_C_LABEL_REQUIRED, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(Extensions__Api__CastChannel__CastMessage, protocol_version), + &extensions__api__cast_channel__cast_message__protocol_version__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "source_id", + 2, + PROTOBUF_C_LABEL_REQUIRED, + PROTOBUF_C_TYPE_STRING, + 0, /* quantifier_offset */ + offsetof(Extensions__Api__CastChannel__CastMessage, source_id), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "destination_id", + 3, + PROTOBUF_C_LABEL_REQUIRED, + PROTOBUF_C_TYPE_STRING, + 0, /* quantifier_offset */ + offsetof(Extensions__Api__CastChannel__CastMessage, destination_id), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "namespace", + 4, + PROTOBUF_C_LABEL_REQUIRED, + PROTOBUF_C_TYPE_STRING, + 0, /* quantifier_offset */ + offsetof(Extensions__Api__CastChannel__CastMessage, namespace_), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "payload_type", + 5, + PROTOBUF_C_LABEL_REQUIRED, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(Extensions__Api__CastChannel__CastMessage, payload_type), + &extensions__api__cast_channel__cast_message__payload_type__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "payload_utf8", + 6, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_STRING, + 0, /* quantifier_offset */ + offsetof(Extensions__Api__CastChannel__CastMessage, payload_utf8), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "payload_binary", + 7, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Extensions__Api__CastChannel__CastMessage, has_payload_binary), + offsetof(Extensions__Api__CastChannel__CastMessage, payload_binary), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned extensions__api__cast_channel__cast_message__field_indices_by_name[] = { + 2, /* field[2] = destination_id */ + 3, /* field[3] = namespace */ + 6, /* field[6] = payload_binary */ + 4, /* field[4] = payload_type */ + 5, /* field[5] = payload_utf8 */ + 0, /* field[0] = protocol_version */ + 1, /* field[1] = source_id */ +}; +static const ProtobufCIntRange extensions__api__cast_channel__cast_message__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 7 } +}; +const ProtobufCMessageDescriptor extensions__api__cast_channel__cast_message__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "extensions.api.cast_channel.CastMessage", + "CastMessage", + "Extensions__Api__CastChannel__CastMessage", + "extensions.api.cast_channel", + sizeof(Extensions__Api__CastChannel__CastMessage), + 7, + extensions__api__cast_channel__cast_message__field_descriptors, + extensions__api__cast_channel__cast_message__field_indices_by_name, + 1, extensions__api__cast_channel__cast_message__number_ranges, + (ProtobufCMessageInit) extensions__api__cast_channel__cast_message__init, + NULL,NULL,NULL /* reserved[123] */ +}; +#define extensions__api__cast_channel__auth_challenge__field_descriptors NULL +#define extensions__api__cast_channel__auth_challenge__field_indices_by_name NULL +#define extensions__api__cast_channel__auth_challenge__number_ranges NULL +const ProtobufCMessageDescriptor extensions__api__cast_channel__auth_challenge__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "extensions.api.cast_channel.AuthChallenge", + "AuthChallenge", + "Extensions__Api__CastChannel__AuthChallenge", + "extensions.api.cast_channel", + sizeof(Extensions__Api__CastChannel__AuthChallenge), + 0, + extensions__api__cast_channel__auth_challenge__field_descriptors, + extensions__api__cast_channel__auth_challenge__field_indices_by_name, + 0, extensions__api__cast_channel__auth_challenge__number_ranges, + (ProtobufCMessageInit) extensions__api__cast_channel__auth_challenge__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor extensions__api__cast_channel__auth_response__field_descriptors[2] = +{ + { + "signature", + 1, + PROTOBUF_C_LABEL_REQUIRED, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(Extensions__Api__CastChannel__AuthResponse, signature), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "client_auth_certificate", + 2, + PROTOBUF_C_LABEL_REQUIRED, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(Extensions__Api__CastChannel__AuthResponse, client_auth_certificate), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned extensions__api__cast_channel__auth_response__field_indices_by_name[] = { + 1, /* field[1] = client_auth_certificate */ + 0, /* field[0] = signature */ +}; +static const ProtobufCIntRange extensions__api__cast_channel__auth_response__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor extensions__api__cast_channel__auth_response__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "extensions.api.cast_channel.AuthResponse", + "AuthResponse", + "Extensions__Api__CastChannel__AuthResponse", + "extensions.api.cast_channel", + sizeof(Extensions__Api__CastChannel__AuthResponse), + 2, + extensions__api__cast_channel__auth_response__field_descriptors, + extensions__api__cast_channel__auth_response__field_indices_by_name, + 1, extensions__api__cast_channel__auth_response__number_ranges, + (ProtobufCMessageInit) extensions__api__cast_channel__auth_response__init, + NULL,NULL,NULL /* reserved[123] */ +}; +const ProtobufCEnumValue extensions__api__cast_channel__auth_error__error_type__enum_values_by_number[2] = +{ + { "INTERNAL_ERROR", "EXTENSIONS__API__CAST_CHANNEL__AUTH_ERROR__ERROR_TYPE__INTERNAL_ERROR", 0 }, + { "NO_TLS", "EXTENSIONS__API__CAST_CHANNEL__AUTH_ERROR__ERROR_TYPE__NO_TLS", 1 }, +}; +static const ProtobufCIntRange extensions__api__cast_channel__auth_error__error_type__value_ranges[] = { +{0, 0},{0, 2} +}; +const ProtobufCEnumValueIndex extensions__api__cast_channel__auth_error__error_type__enum_values_by_name[2] = +{ + { "INTERNAL_ERROR", 0 }, + { "NO_TLS", 1 }, +}; +const ProtobufCEnumDescriptor extensions__api__cast_channel__auth_error__error_type__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "extensions.api.cast_channel.AuthError.ErrorType", + "ErrorType", + "Extensions__Api__CastChannel__AuthError__ErrorType", + "extensions.api.cast_channel", + 2, + extensions__api__cast_channel__auth_error__error_type__enum_values_by_number, + 2, + extensions__api__cast_channel__auth_error__error_type__enum_values_by_name, + 1, + extensions__api__cast_channel__auth_error__error_type__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; +static const ProtobufCFieldDescriptor extensions__api__cast_channel__auth_error__field_descriptors[1] = +{ + { + "error_type", + 1, + PROTOBUF_C_LABEL_REQUIRED, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(Extensions__Api__CastChannel__AuthError, error_type), + &extensions__api__cast_channel__auth_error__error_type__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned extensions__api__cast_channel__auth_error__field_indices_by_name[] = { + 0, /* field[0] = error_type */ +}; +static const ProtobufCIntRange extensions__api__cast_channel__auth_error__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor extensions__api__cast_channel__auth_error__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "extensions.api.cast_channel.AuthError", + "AuthError", + "Extensions__Api__CastChannel__AuthError", + "extensions.api.cast_channel", + sizeof(Extensions__Api__CastChannel__AuthError), + 1, + extensions__api__cast_channel__auth_error__field_descriptors, + extensions__api__cast_channel__auth_error__field_indices_by_name, + 1, extensions__api__cast_channel__auth_error__number_ranges, + (ProtobufCMessageInit) extensions__api__cast_channel__auth_error__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor extensions__api__cast_channel__device_auth_message__field_descriptors[3] = +{ + { + "challenge", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Extensions__Api__CastChannel__DeviceAuthMessage, challenge), + &extensions__api__cast_channel__auth_challenge__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "response", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Extensions__Api__CastChannel__DeviceAuthMessage, response), + &extensions__api__cast_channel__auth_response__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "error", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Extensions__Api__CastChannel__DeviceAuthMessage, error), + &extensions__api__cast_channel__auth_error__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned extensions__api__cast_channel__device_auth_message__field_indices_by_name[] = { + 0, /* field[0] = challenge */ + 2, /* field[2] = error */ + 1, /* field[1] = response */ +}; +static const ProtobufCIntRange extensions__api__cast_channel__device_auth_message__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 3 } +}; +const ProtobufCMessageDescriptor extensions__api__cast_channel__device_auth_message__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "extensions.api.cast_channel.DeviceAuthMessage", + "DeviceAuthMessage", + "Extensions__Api__CastChannel__DeviceAuthMessage", + "extensions.api.cast_channel", + sizeof(Extensions__Api__CastChannel__DeviceAuthMessage), + 3, + extensions__api__cast_channel__device_auth_message__field_descriptors, + extensions__api__cast_channel__device_auth_message__field_indices_by_name, + 1, extensions__api__cast_channel__device_auth_message__number_ranges, + (ProtobufCMessageInit) extensions__api__cast_channel__device_auth_message__init, + NULL,NULL,NULL /* reserved[123] */ +}; diff --git a/ccast/chan/cast_channel.pb-c.h b/ccast/chan/cast_channel.pb-c.h new file mode 100644 index 0000000..982e420 --- /dev/null +++ b/ccast/chan/cast_channel.pb-c.h @@ -0,0 +1,233 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: cast_channel.proto */ + +#ifndef PROTOBUF_C_cast_5fchannel_2eproto__INCLUDED +#define PROTOBUF_C_cast_5fchannel_2eproto__INCLUDED + +#include "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 1000001 < 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 _Extensions__Api__CastChannel__CastMessage Extensions__Api__CastChannel__CastMessage; +typedef struct _Extensions__Api__CastChannel__AuthChallenge Extensions__Api__CastChannel__AuthChallenge; +typedef struct _Extensions__Api__CastChannel__AuthResponse Extensions__Api__CastChannel__AuthResponse; +typedef struct _Extensions__Api__CastChannel__AuthError Extensions__Api__CastChannel__AuthError; +typedef struct _Extensions__Api__CastChannel__DeviceAuthMessage Extensions__Api__CastChannel__DeviceAuthMessage; + + +/* --- enums --- */ + +typedef enum _Extensions__Api__CastChannel__CastMessage__ProtocolVersion { + EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__PROTOCOL_VERSION__CASTV2_1_0 = 0 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__PROTOCOL_VERSION) +} Extensions__Api__CastChannel__CastMessage__ProtocolVersion; +typedef enum _Extensions__Api__CastChannel__CastMessage__PayloadType { + EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__PAYLOAD_TYPE__STRING = 0, + EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__PAYLOAD_TYPE__BINARY = 1 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__PAYLOAD_TYPE) +} Extensions__Api__CastChannel__CastMessage__PayloadType; +typedef enum _Extensions__Api__CastChannel__AuthError__ErrorType { + EXTENSIONS__API__CAST_CHANNEL__AUTH_ERROR__ERROR_TYPE__INTERNAL_ERROR = 0, + EXTENSIONS__API__CAST_CHANNEL__AUTH_ERROR__ERROR_TYPE__NO_TLS = 1 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(EXTENSIONS__API__CAST_CHANNEL__AUTH_ERROR__ERROR_TYPE) +} Extensions__Api__CastChannel__AuthError__ErrorType; + +/* --- messages --- */ + +struct _Extensions__Api__CastChannel__CastMessage +{ + ProtobufCMessage base; + Extensions__Api__CastChannel__CastMessage__ProtocolVersion protocol_version; + char *source_id; + char *destination_id; + char *namespace_; + Extensions__Api__CastChannel__CastMessage__PayloadType payload_type; + char *payload_utf8; + protobuf_c_boolean has_payload_binary; + ProtobufCBinaryData payload_binary; +}; +#define EXTENSIONS__API__CAST_CHANNEL__CAST_MESSAGE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&extensions__api__cast_channel__cast_message__descriptor) \ + , 0, NULL, NULL, NULL, 0, NULL, 0,{0,NULL} } + + +struct _Extensions__Api__CastChannel__AuthChallenge +{ + ProtobufCMessage base; +}; +#define EXTENSIONS__API__CAST_CHANNEL__AUTH_CHALLENGE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&extensions__api__cast_channel__auth_challenge__descriptor) \ + } + + +struct _Extensions__Api__CastChannel__AuthResponse +{ + ProtobufCMessage base; + ProtobufCBinaryData signature; + ProtobufCBinaryData client_auth_certificate; +}; +#define EXTENSIONS__API__CAST_CHANNEL__AUTH_RESPONSE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&extensions__api__cast_channel__auth_response__descriptor) \ + , {0,NULL}, {0,NULL} } + + +struct _Extensions__Api__CastChannel__AuthError +{ + ProtobufCMessage base; + Extensions__Api__CastChannel__AuthError__ErrorType error_type; +}; +#define EXTENSIONS__API__CAST_CHANNEL__AUTH_ERROR__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&extensions__api__cast_channel__auth_error__descriptor) \ + , 0 } + + +struct _Extensions__Api__CastChannel__DeviceAuthMessage +{ + ProtobufCMessage base; + Extensions__Api__CastChannel__AuthChallenge *challenge; + Extensions__Api__CastChannel__AuthResponse *response; + Extensions__Api__CastChannel__AuthError *error; +}; +#define EXTENSIONS__API__CAST_CHANNEL__DEVICE_AUTH_MESSAGE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&extensions__api__cast_channel__device_auth_message__descriptor) \ + , NULL, NULL, NULL } + + +/* Extensions__Api__CastChannel__CastMessage methods */ +void extensions__api__cast_channel__cast_message__init + (Extensions__Api__CastChannel__CastMessage *message); +size_t extensions__api__cast_channel__cast_message__get_packed_size + (const Extensions__Api__CastChannel__CastMessage *message); +size_t extensions__api__cast_channel__cast_message__pack + (const Extensions__Api__CastChannel__CastMessage *message, + uint8_t *out); +size_t extensions__api__cast_channel__cast_message__pack_to_buffer + (const Extensions__Api__CastChannel__CastMessage *message, + ProtobufCBuffer *buffer); +Extensions__Api__CastChannel__CastMessage * + extensions__api__cast_channel__cast_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void extensions__api__cast_channel__cast_message__free_unpacked + (Extensions__Api__CastChannel__CastMessage *message, + ProtobufCAllocator *allocator); +/* Extensions__Api__CastChannel__AuthChallenge methods */ +void extensions__api__cast_channel__auth_challenge__init + (Extensions__Api__CastChannel__AuthChallenge *message); +size_t extensions__api__cast_channel__auth_challenge__get_packed_size + (const Extensions__Api__CastChannel__AuthChallenge *message); +size_t extensions__api__cast_channel__auth_challenge__pack + (const Extensions__Api__CastChannel__AuthChallenge *message, + uint8_t *out); +size_t extensions__api__cast_channel__auth_challenge__pack_to_buffer + (const Extensions__Api__CastChannel__AuthChallenge *message, + ProtobufCBuffer *buffer); +Extensions__Api__CastChannel__AuthChallenge * + extensions__api__cast_channel__auth_challenge__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void extensions__api__cast_channel__auth_challenge__free_unpacked + (Extensions__Api__CastChannel__AuthChallenge *message, + ProtobufCAllocator *allocator); +/* Extensions__Api__CastChannel__AuthResponse methods */ +void extensions__api__cast_channel__auth_response__init + (Extensions__Api__CastChannel__AuthResponse *message); +size_t extensions__api__cast_channel__auth_response__get_packed_size + (const Extensions__Api__CastChannel__AuthResponse *message); +size_t extensions__api__cast_channel__auth_response__pack + (const Extensions__Api__CastChannel__AuthResponse *message, + uint8_t *out); +size_t extensions__api__cast_channel__auth_response__pack_to_buffer + (const Extensions__Api__CastChannel__AuthResponse *message, + ProtobufCBuffer *buffer); +Extensions__Api__CastChannel__AuthResponse * + extensions__api__cast_channel__auth_response__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void extensions__api__cast_channel__auth_response__free_unpacked + (Extensions__Api__CastChannel__AuthResponse *message, + ProtobufCAllocator *allocator); +/* Extensions__Api__CastChannel__AuthError methods */ +void extensions__api__cast_channel__auth_error__init + (Extensions__Api__CastChannel__AuthError *message); +size_t extensions__api__cast_channel__auth_error__get_packed_size + (const Extensions__Api__CastChannel__AuthError *message); +size_t extensions__api__cast_channel__auth_error__pack + (const Extensions__Api__CastChannel__AuthError *message, + uint8_t *out); +size_t extensions__api__cast_channel__auth_error__pack_to_buffer + (const Extensions__Api__CastChannel__AuthError *message, + ProtobufCBuffer *buffer); +Extensions__Api__CastChannel__AuthError * + extensions__api__cast_channel__auth_error__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void extensions__api__cast_channel__auth_error__free_unpacked + (Extensions__Api__CastChannel__AuthError *message, + ProtobufCAllocator *allocator); +/* Extensions__Api__CastChannel__DeviceAuthMessage methods */ +void extensions__api__cast_channel__device_auth_message__init + (Extensions__Api__CastChannel__DeviceAuthMessage *message); +size_t extensions__api__cast_channel__device_auth_message__get_packed_size + (const Extensions__Api__CastChannel__DeviceAuthMessage *message); +size_t extensions__api__cast_channel__device_auth_message__pack + (const Extensions__Api__CastChannel__DeviceAuthMessage *message, + uint8_t *out); +size_t extensions__api__cast_channel__device_auth_message__pack_to_buffer + (const Extensions__Api__CastChannel__DeviceAuthMessage *message, + ProtobufCBuffer *buffer); +Extensions__Api__CastChannel__DeviceAuthMessage * + extensions__api__cast_channel__device_auth_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void extensions__api__cast_channel__device_auth_message__free_unpacked + (Extensions__Api__CastChannel__DeviceAuthMessage *message, + ProtobufCAllocator *allocator); +/* --- per-message closures --- */ + +typedef void (*Extensions__Api__CastChannel__CastMessage_Closure) + (const Extensions__Api__CastChannel__CastMessage *message, + void *closure_data); +typedef void (*Extensions__Api__CastChannel__AuthChallenge_Closure) + (const Extensions__Api__CastChannel__AuthChallenge *message, + void *closure_data); +typedef void (*Extensions__Api__CastChannel__AuthResponse_Closure) + (const Extensions__Api__CastChannel__AuthResponse *message, + void *closure_data); +typedef void (*Extensions__Api__CastChannel__AuthError_Closure) + (const Extensions__Api__CastChannel__AuthError *message, + void *closure_data); +typedef void (*Extensions__Api__CastChannel__DeviceAuthMessage_Closure) + (const Extensions__Api__CastChannel__DeviceAuthMessage *message, + void *closure_data); + +/* --- services --- */ + + +/* --- descriptors --- */ + +extern const ProtobufCMessageDescriptor extensions__api__cast_channel__cast_message__descriptor; +extern const ProtobufCEnumDescriptor extensions__api__cast_channel__cast_message__protocol_version__descriptor; +extern const ProtobufCEnumDescriptor extensions__api__cast_channel__cast_message__payload_type__descriptor; +extern const ProtobufCMessageDescriptor extensions__api__cast_channel__auth_challenge__descriptor; +extern const ProtobufCMessageDescriptor extensions__api__cast_channel__auth_response__descriptor; +extern const ProtobufCMessageDescriptor extensions__api__cast_channel__auth_error__descriptor; +extern const ProtobufCEnumDescriptor extensions__api__cast_channel__auth_error__error_type__descriptor; +extern const ProtobufCMessageDescriptor extensions__api__cast_channel__device_auth_message__descriptor; + +PROTOBUF_C__END_DECLS + + +#endif /* PROTOBUF_C_cast_5fchannel_2eproto__INCLUDED */ diff --git a/ccast/chan/protobuf-c.c b/ccast/chan/protobuf-c.c new file mode 100644 index 0000000..1157d8b --- /dev/null +++ b/ccast/chan/protobuf-c.c @@ -0,0 +1,3287 @@ +/* + * 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 /* for malloc, free */ +#include /* for strcmp, strlen, memcpy, memmove, memset */ + +#include "protobuf-c.h" + +#define TRUE 1 +#define FALSE 0 + +#define PROTOBUF_C__ASSERT_NOT_REACHED() assert(0) + +/* VC6 doesn't support inline */ +#define inline + +/** + * \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(xxx) +#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 = { +#ifdef NEVER + .alloc = &system_alloc, + .free = &system_free, + .allocator_data = NULL, +#else + &system_alloc, + &system_free, + NULL +#endif +}; + +/* === 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< 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, *l_data; + const ProtobufCBinaryData *d_bd; + + e_data = ((ProtobufCBinaryData *) earlier_elem)->data; + l_data = ((ProtobufCBinaryData *) latter_elem)->data; + d_bd = (ProtobufCBinaryData *) def_val; + + 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, *l_str; + const char *d_str; + + el_size = sizeof(char *); + e_str = *(char **) earlier_elem; + l_str = *(char **) latter_elem; + d_str = def_val; + + 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 = *(uint8_t **) 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) { + unsigned n = *n_ptr; + void *a; + *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/ccast/chan/protobuf-c.h b/ccast/chan/protobuf-c.h new file mode 100644 index 0000000..8c4ba1d --- /dev/null +++ b/ccast/chan/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 +#include +#include +#include "os_int.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 uint8_t *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/ccast/dpat.c b/ccast/dpat.c new file mode 100644 index 0000000..4962ae1 --- /dev/null +++ b/ccast/dpat.c @@ -0,0 +1,952 @@ + +/* + * Argyll Color Correction System + * ChromCast dither pattern code + * + * Author: Graeme W. Gill + * Date: 11/12/2014 + * + * Copyright 2014 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License2.txt file for licencing details. + * + */ + +/* + * Locates minimal list of surrounders + * Find initial weight for them using minimizer + Itterates over + * Locate cell changes that match desired correction + with increasing randomness of choosing a poor match + * Reset to starting pattern when accumulated error over + best current is exceeded. + + + Problem is this is unreliable is locating good matches + for some cases. + + Problem is that it doesn't seem to work - ChromeCast doesn't + decode the way we model it :-( + + */ + +#include +#include +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#include "counters.h" +#ifndef SALONEINSTLIB +#include "numlib.h" +#else +#include "numsup.h" +#endif +#include "conv.h" +#include "base64.h" +#include "yajl.h" +#include "ccmdns.h" +#include "ccpacket.h" +#include "ccmes.h" +#include "ccast.h" + +#ifdef STANDALONE_TEST +# define DIAGNOSTICS +#endif + +#undef TEST_POINT + +/* Even/Odd filter filter index ranges (inclusive) and total size */ +#define EV_NEG -4 +#define EV_POS 4 +#define EV_WIDTH (-(EV_NEG) + 1 + EV_POS) +#define OD_NEG -4 +#define OD_POS 3 +#define OD_WIDTH (-(OD_NEG) + 1 + OD_POS) + +/* Weightings [horiz/vert] */ +double filt_v_ev[2][EV_WIDTH] = { +{ -0.003467, -0.048341, 0.028688, 0.418873, 0.704674, 0.427551, 0.032480, -0.050146, -0.003793 }, +{ -0.013477, 0.000081, -0.087119, 0.357627, 1.000252, 0.378473, -0.083496, -0.000155, -0.009850 } +}; + +/* Weightings [horiz/vert] */ +double filt_v_od[2][OD_WIDTH] = { +{ -0.026810, -0.045190, 0.186825, 0.614579, 0.623721, 0.206987, -0.040217, -0.026419 }, +{ 0.008396, -0.078987, -0.013733, 0.796594, 0.813578, 0.013555, -0.086466, 0.004781 } +}; + + +/* [horiz/vert][phase] */ +double *filt[2][2] = { { &filt_v_ev[0][-(EV_NEG)], &filt_v_od[0][-(OD_NEG)] }, + { &filt_v_ev[1][-(EV_NEG)], &filt_v_od[1][-(OD_NEG)] } }; + +/* [phase] */ +int fneg[2] = { EV_NEG, OD_NEG }; +int fpos[2] = { EV_POS, OD_POS }; + +/* Dither input size */ +#ifdef TEST_POINT +# define DISIZE 8 +#else +# define DISIZE CCDITHSIZE +#endif +#define DOSIZE ((DISIZE * 3)/2) + +#ifdef DIAGNOSTICS +int vv = 0; +#endif + +/* Upsample ipat to opat */ +/* And return the average RGB value */ +static void upsample(double ret[3], double iipat[DISIZE][DISIZE][3]) { + double ipat[DISIZE][DISIZE][3]; + double tpat[DOSIZE][DISIZE][3]; /* Intermediate pattern - horizontal up-sampled */ + double opat[DOSIZE][DOSIZE][3]; /* Output pattern */ + int x, y; + int i, j; + +//printf("~1 doing conversion\n"); + /* Convert from RGB to YCbCr */ + for (x = 0; x < DISIZE; x++) { + for (y = 0; y < DISIZE; y++) { + ccast2YCbCr(NULL, ipat[x][y], iipat[x][y]); +// ipat[x][y][0] = iipat[x][y][0]; +// ipat[x][y][1] = iipat[x][y][1]; +// ipat[x][y][2] = iipat[x][y][2]; +//printf("[%d][%d] %f %f %f -> %f %f %f\n",x,y, iipat[x][y][0], iipat[x][y][1], iipat[x][y][2], ipat[x][y][0], ipat[x][y][1], ipat[x][y][2]); + } + } + +//printf("~1 doing horiz\n"); + /* Up-sample in horizontal direction */ + for (y = 0; y < DISIZE; y++) { + + /* Zero the intermediate pattern */ + for (i = 0; i < DOSIZE; i++) { + tpat[i][y][0] = 0.0; + tpat[i][y][1] = 0.0; + tpat[i][y][2] = 0.0; + } + + /* Distribute the input according to the filter */ + for (x = 0; x < DISIZE; x++) { + int ph, ii, k; + + ph = x & 1; + ii = (int)floor(x * 1.5 + 0.5); + + /* For all the filter cooeficients */ + for (k = fneg[ph]; k <= fpos[ph]; k++) { + i = (ii + k); + while (i < 0) + i += DOSIZE; + while (i >= DOSIZE) + i -= DOSIZE; + tpat[i][y][0] += filt[0][ph][k] * ipat[x][y][0]; + tpat[i][y][1] += filt[0][ph][k] * ipat[x][y][1]; + tpat[i][y][2] += filt[0][ph][k] * ipat[x][y][2]; + } + } + } + +//printf("~1 doing vert\n"); + /* Up-sample in vertical direction */ + for (i = 0; i < DOSIZE; i++) { + + /* Zero the output pattern */ + for (j = 0; j < DOSIZE; j++) { + opat[i][j][0] = 0.0; + opat[i][j][1] = 0.0; + opat[i][j][2] = 0.0; + } + + /* Distribute the input according to the filter */ + for (y = 0; y < DISIZE; y++) { + int ph, jj, k; + + ph = y & 1; + jj = (int)floor(y * 1.5 + 0.5); + + /* For all the filter cooeficients */ + for (k = fneg[ph]; k <= fpos[ph]; k++) { + j = (jj + k); + while (j < 0) + j += DOSIZE; + while (j >= DOSIZE) + j -= DOSIZE; + opat[i][j][0] += filt[1][ph][k] * tpat[i][y][0]; + opat[i][j][1] += filt[1][ph][k] * tpat[i][y][1]; + opat[i][j][2] += filt[1][ph][k] * tpat[i][y][2]; + } + } + } +//printf("~1 doing conversion\n"); + + /* Convert from YCbCr to RGB */ + for (i = 0; i < DOSIZE; i++) { + for (j = 0; j < DOSIZE; j++) { + YCbCr2ccast(NULL, opat[i][j], opat[i][j]); + } + } +//printf("~1 done\n"); + + if (ret != NULL) { + ret[0] = ret[1] = ret[2] = 0.0; + for (j = 0; j < DOSIZE; j++) { + for (i = 0; i < DOSIZE; i++) { + ret[0] += opat[i][j][0]; + ret[1] += opat[i][j][1]; + ret[2] += opat[i][j][2]; + } + } + ret[0] /= (double)(DOSIZE * DOSIZE); + ret[1] /= (double)(DOSIZE * DOSIZE); + ret[2] /= (double)(DOSIZE * DOSIZE); + } + +#ifdef DIAGNOSTICS + if (vv) { + printf("Result\n"); + for (j = 0; j < DOSIZE; j++) { + for (i = 0; i < DOSIZE; i++) { + if (i > 0) + printf(", "); + printf("% 5.1f % 5.1f % 5.1f",opat[i][j][0],opat[i][j][1], opat[i][j][2]); + } + printf("\n"); + } + } +#endif +} + +/* ------------------------------------------------------------- */ + +/* Given a quantized RGB target, return a quantized RGB that either exactly */ +/* maps to it through YCbCr conversion, or is the closest in the */ +/* direction away from the target value, or the closest clipped value */ +static void quant_rgb(int n, double out[3], double rgb[3]) { + double ycc[3], base[3]; + double tmp[3], chval[3]; + double dist, bdist = 1e6; + double brgb[3], borgb[3]; + int ix, k; + +//printf("Quant RGB %f %f %f n %d\n", rgb[0], rgb[1], rgb[2], n); + + /* Search current rgb and surround in the same direction */ + /* it is from the target value, for a quantized rgb */ + /* that is in that direction */ + for (k = 0; k < 3; k++) + base[k] = rgb[k]; + + for (ix = 0; ix < 8; ix++) { + double dist; + + /* Comp trial RGB */ + for (k = 0; k < 3; k++) { + tmp[k] = base[k]; + if (ix & (1 << k)) { + if (n & (1 << k)) /* Move in direction base point is in */ + tmp[k] += 1.0; + else + tmp[k] -= 1.0; + if (tmp[k] < 0.0 || tmp[k] > 255.0) + break; + } + } + if (k < 3) /* Trial is out of gamut */ + continue; + + /* Quantize it */ + ccast2YCbCr(NULL, ycc, tmp); + YCbCr2ccast(NULL, chval, ycc); +//printf("Trial RGB %f %f %f\n", tmp[0], tmp[1], tmp[2]); +//printf(" result %f %f %f\n", chval[0], chval[1], chval[2]); + + /* It's OK if it is eual or greater in the desired */ + /* direction than the input rgb. */ + /* Best would be closest to input. */ + /* Least worst would be what ? */ + + dist = 0.0; + for (k = 0; k < 3; k++) { + double tt; + if (n & (1 << k)) { + tt = chval[k] - base[k]; + } else { + tt = base[k] - chval[k]; + } + if (tt >= 0.0) + dist += 0.1 * tt; + else + dist += 2.0 * -tt; + } +//printf(" dist %f\n", dist); + + /* Pick it if it is at least in the right direction */ + if (dist < bdist) { + for (k = 0; k < 3; k++) { + rgb[k] = tmp[k]; + out[k] = chval[k]; + } + bdist = dist; + } + } + +#ifdef NEVER + if (bdist > 0.0 + && rgb[0] != 0.0 && rgb[0] != 1.0 && rgb[0] != 254.0 && rgb[0] != 255.0 + && rgb[1] != 0.0 && rgb[1] != 1.0 && rgb[1] != 254.0 && rgb[1] != 255.0 + && rgb[2] != 0.0 && rgb[2] != 1.0 && rgb[2] != 254.0 && rgb[2] != 255.0) { + printf("quant_rgb failed with bdist %f\n",bdist); + printf(" rgb in %f %f %f\n",base[0],base[1],base[2]); + printf(" returning rgb %f %f %f\n",rgb[0],rgb[1],rgb[2]); + printf(" resrgb %f %f %f\n",out[0],out[1],out[2]); + } +#endif /* NEVER */ +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - */ + +typedef struct optcntx { + int di; + double *val; /* Target */ + double (*ressur)[8][3]; /* resulting RGB surrounding values */ +} optcntx; + +static double optfunc(void *fdata, double tp[]) { + optcntx *cntx = (optcntx *)fdata; + int i, k; + double iw, tmp[3]; + double err = 0.0; + + /* Compute interpolated result */ + for (k = 0; k < 3; k++) + tmp[k] = 0.0; + + iw = 1.0; + for (i = 0; i < cntx->di; i++) { + if (tp[i] < 0.0) + err += 1000.0 * tp[i] * tp[i]; + else if (tp[i] > 1.0) + err += 1000.0 * (tp[i] - 1.0) * (tp[i] - 1.0); + + for (k = 0; k < 3; k++) + tmp[k] += tp[i] * (*cntx->ressur)[i][k]; + iw -= tp[i]; + } + for (k = 0; k < 3; k++) + tmp[k] += iw * (*cntx->ressur)[i][k]; + + /* Compute error */ + for (k = 0; k < 3; k++) { + double tt = tmp[k] - cntx->val[k]; + err += tt * tt; + } +//printf("Returning %f from %f %f\n",err,tp[0],tp[1]); + return err; +} + +/* Compute pattern */ +/* return the delta to the target */ +double get_ccast_dith(double ipat[DISIZE][DISIZE][3], double val[3]) { + double itpat[DISIZE][DISIZE][3], tpat[DISIZE][DISIZE][3]; + double irtpat[DISIZE][DISIZE][3], rtpat[DISIZE][DISIZE][3]; /* resulting for tpat */ + double berr = 0.0; + int n, k; + int x, y; + int i, j; + int ii; + struct { + int x, y; + } order[16] = { +// Dispersed: + { 3, 1 },{ 1, 3 },{ 1, 1 },{ 3, 0 },{ 3, 3 },{ 1, 2 },{ 3, 2 },{ 2, 0 }, + { 1, 0 },{ 0, 3 },{ 0, 1 },{ 2, 1 },{ 2, 2 },{ 0, 0 },{ 0, 2 },{ 2, 3 } +// Clustered: +// { 0, 0 },{ 0, 1 },{ 1, 1 },{ 1, 0 },{ 2, 0 },{ 3, 0 },{ 3, 1 },{ 2, 1 }, +// { 2, 2 },{ 3, 2 },{ 3, 3 },{ 2, 3 },{ 1, 3 },{ 1, 2 },{ 0, 2 },{ 0, 3 } + }; + int cix = 0; + int nsur = 8, ncomb = 4; + double sur[8][3]; /* RGB surrounding values to use */ + double ressur[8][3]; /* resulting RGB surrounding values */ + int bcc[8]; /* Best combination */ + double bw[8]; /* Best weight */ + int biw[8]; /* Best integer weight/16 */ + double dval[3]; /* Dithered value */ + double err[3], werr; + double errxs; + /* Tuning params */ + int nitters = 300; /* No itters */ + int rand_count = 150; + double rand_start = 4.5; /* Ramp with itters */ + double rand_end = 0.2; + double rand_pow = 1.5; + double mxerrxs = 2.5; /* accumulated error reset threshold */ + unsigned int randv = 0x1234; + + /* 32 bit pseudo random sequencer based on XOR feedback */ + /* generates number between 1 and 4294967295 */ +#define PSRAND32(S) (((S) & 0x80000000) ? (((S) << 1) ^ 0xa398655d) : ((S) << 1)) + + /* Locate the 8 surrounding RGB verticies */ + for (n = 0; n < 8; n++) { + for (k = 0; k < 3; k++) { + if (n & (1 << k)) + sur[n][k] = ceil(val[k]); + else + sur[n][k] = floor(val[k]); + } + +//printf("Input sur %d: %f %f %f\n",n,sur[n][0], sur[n][1], sur[n][2], sur[n][3]); + /* Figure out what RGB values to use to surround the point, */ + /* and what actual RGB values to expect from using them. */ + quant_rgb(n, ressur[n], sur[n]); +//printf("Quant sur %d: %f %f %f\n",n,sur[n][0], sur[n][1], sur[n][2], sur[n][3]); +//printf(" ressur %d: %f %f %f\n",n,ressur[n][0], ressur[n][1], ressur[n][2], ressur[n][3]); +// printf("\n"); + } + + /* Reduce this to unique surrounders */ + for (nsur = 0, i = 0; i < 8; i++) { + + /* Check if the i'th entry is already in the list */ + for (j = 0; j < nsur; j++) { + for (k = 0; k < 3; k++) { + if (ressur[i][k] != ressur[j][k]) + break; + } + if (k < 3) + continue; /* Unique */ + break; /* Duplicate */ + } + if (j < nsur) /* Duplicate */ + continue; + + /* Copy i'th to nsur */ + for (k = 0; k < 3; k++) { + sur[nsur][k] = sur[i][k]; + ressur[nsur][k] = ressur[i][k]; + } + nsur++; + } + +#ifdef DIAGNOSTICS + if (vv) { + printf("There are %d unique surrounders:\n",nsur); + for (n = 0; n < nsur; n++) { + printf("sur %f %f %f\n",ressur[n][0], ressur[n][1], ressur[n][2], ressur[n][3]); + } + } +#endif + + /* Use an optimzer to set the initial values using all the unique */ + /* surrounders. */ + { + double s[8]; + optcntx cntx; + + ncomb = nsur; + for (n = 0; n < nsur; n++) { + bcc[n] = n; + bw[n] = 1.0/nsur; + s[n] = 0.1; + } + + cntx.di = nsur-1; + cntx.val = val; + cntx.ressur = &ressur; + + powell(NULL, cntx.di, bw, s, 1e-4, 1000, optfunc, &cntx, NULL, NULL); + + /* Compute baricentric values */ + bw[nsur-1] = 0.0; + for (n = 0; n < (nsur-1); n++) { + if (bw[n] < 0.0) + bw[n] = 0.0; + else if (bw[n] > 1.0) + bw[n] = 1.0; + bw[nsur-1] += bw[n]; + } + if (bw[nsur-1] > 1.0) { /* They summed to over 1.0 */ + for (n = 0; n < (nsur-1); n++) + bw[n] *= 1.0/bw[nsur-1]; /* Scale them down */ + bw[nsur-1] = 1.0; + } + bw[nsur-1] = 1.0 - bw[nsur-1]; /* Remainder */ + } + + /* Check the result */ +#ifdef DIAGNOSTICS + if (vv) { + double tmp[3], err; + + /* Compute interpolated result */ + for (k = 0; k < 3; k++) + tmp[k] = 0.0; + for (n = 0; n < ncomb; n++) { + for (k = 0; k < 3; k++) + tmp[k] += bw[n] * ressur[bcc[n]][k]; + } + /* Compute error */ + err = 0.0; + for (k = 0; k < 3; k++) { + tmp[k] -= val[k]; + err += tmp[k] * tmp[k]; + } + err = sqrt(err); + for (n = 0; n < ncomb; n++) + printf("Comb %d weight %f rgb %f %f %f\n",bcc[n],bw[n],ressur[bcc[n]][0],ressur[bcc[n]][1],ressur[bcc[n]][2]); + printf("Error %f %f %f rms %f\n",tmp[0], tmp[1], tmp[2], err); + printf("\n"); + } +#endif + + /* Compute the number of pixels for each surounder value */ + { + int sw[8], rem; + + /* Sort the weightings from smallest to largest */ + for (n = 0; n < 8; n++) + sw[n] = n; + + for (i = 0; i < (ncomb-1); i++) { + for (j = i+1; j < ncomb; j++) { + if (bw[sw[j]] < bw[sw[i]]) { + int tt = sw[i]; /* Swap them */ + sw[i] = sw[j]; + sw[j] = tt; + } + } + } + + /* Compute the nearest integer weighting out of 16 */ + rem = 16; + for (i = 0; i < (ncomb-1) && rem > 0; i++) { + n = sw[i]; + biw[n] = (int)(16.0 * bw[n] + 0.5); + rem -= biw[n]; + if (rem <= 0) + rem = 0; + } + for (; i < ncomb; i++) { + n = sw[i]; + biw[n] = rem; + } +#ifdef DIAGNOSTICS + if (vv) { + for (n = 0; n < ncomb; n++) + printf("Comb %d iweight %i rgb %f %f %f\n",bcc[n],biw[n],ressur[bcc[n]][0],ressur[bcc[n]][1],ressur[bcc[n]][2]); + } +#endif + } + + /* Set the initial pattern according to the integer weighting */ + for (cix = 0, n = 0; n < ncomb; n++) { + for (i = 0; i < biw[n]; i++) { + x = order[cix].x; + y = order[cix].y; + cix++; + for (k = 0; k < 3; k++) { + tpat[x][y][k] = itpat[x][y][k] = sur[bcc[n]][k]; + rtpat[x][y][k] = irtpat[x][y][k] = ressur[bcc[n]][k]; + } + } + } + +#ifdef DIAGNOSTICS + /* Check initial pattern error */ + if (vv) { + printf("Input pat:\n"); + for (x = 0; x < DISIZE; x++) { + for (y = 0; y < DISIZE; y++) { + if (y > 0) + printf(", "); + printf("%3.0f %3.0f %3.0f",rtpat[x][y][0], rtpat[x][y][1], rtpat[x][y][2]); + } + printf("\n"); + } + } +#endif + + upsample(dval, rtpat); + + werr = 0.0; + for (k = 0; k < 3; k++) { + err[k] = dval[k] - val[k]; + if (fabs(err[k]) > werr) + werr = fabs(err[k]); + } + +#ifdef DIAGNOSTICS + if (vv) { + printf("Target %f %f %f -> %f %f %f\n", val[0], val[1], val[2], dval[0], dval[1], dval[2]); + printf("Error %f %f %f werr %f\n", err[0], err[1], err[2], werr); + } +#endif + + berr = werr; + for (x = 0; x < DISIZE; x++) { + for (y = 0; y < DISIZE; y++) { + for (k = 0; k < 3; k++) + ipat[x][y][k] = tpat[x][y][k]; + } + } + + /* Improve fit if needed */ + /* This is a bit stocastic */ + errxs = 0.0; + for (ii = 0; ii < nitters; ii++) { /* Until we give up */ + double corr[3]; /* Correction direction needed */ + double bdot; + int bx, by; + + double wycc, mm; + double cell[3]; /* Cell being modified value */ + double ccell[3]; /* Corrected cell */ + double pcell[3]; /* Proposed new cell value */ + + for (k = 0; k < 3; k++) + corr[k] = val[k] - dval[k]; +#ifdef DIAGNOSTICS + if (vv) + printf("corr needed %f %f %f\n", corr[0], corr[1], corr[2]); +#endif + + /* Scale it and limit it */ + for (k = 0; k < 3; k++) { + double dd = 16.0 * corr[k]; + if (dd >= 1.0) + dd = 1.0; + else if (dd <= -1.0) + dd = -1.0; + else + dd = 0.0; + corr[k] = dd; + } + + if (corr[0] == 0.0 && corr[1] == 0 && corr[2] == 0.0) { +#ifdef DIAGNOSTICS + if (vv) + printf("No correction possible - done\n"); +#endif + break; + } + +#ifdef DIAGNOSTICS + if (vv) + printf("scaled corr %f %f %f\n", corr[0], corr[1], corr[2]); +#endif + + /* Search dither cell and surrounder for a combination */ + /* that is closest to the change we want to make. */ + bdot = 1e6; + bx = by = n = 0; + for (x = 0; x < DISIZE; x++) { + double rlevel = rand_start + (rand_end - rand_start) + * pow((ii % rand_count)/rand_count, rand_pow); + + for (y = 0; y < DISIZE; y++) { + for (i = 0; i < ncomb; i++) { + double dot = 0.0; + for (k = 0; k < 3; k++) + dot += (ressur[bcc[i]][k] - rtpat[x][y][k]) * corr[k]; + + /* Ramp the randomness up */ +// dot += d_rand(0.0, 0.1 + (2.5-0.1) * ii/nitters); +// dot += d_rand(-rlevel, rlevel); + /* use a deterministic random element, so that */ + /* the dither patterns are repeatable. */ + randv = PSRAND32(randv); + dot += rlevel * 2.0 * ((randv - 1)/4294967294.0 - 0.5); + + if (dot <= 0.0) + dot = 1e7; + else { + dot = (dot - 1.0) * (dot - 1.0); + } +//printf("dot %f from sur %f %f %f to pat %f %f %f\n", dot, ressur[bcc[i]][0], ressur[bcc[i]][1], ressur[bcc[i]][2], rtpat[x][y][0], rtpat[x][y][1], rtpat[x][y][2]); + + if (dot < bdot) { + bdot = dot; + bx = x; + by = y; + n = i; + } + } + } + } + +#ifdef DIAGNOSTICS + if (vv) { + printf("Changing cell [%d][%d] %f %f %f with dot %f\n",bx,by,rtpat[bx][by][0],rtpat[bx][by][1],rtpat[bx][by][2],bdot); + printf(" to sur %d: %f %f %f\n",n, ressur[bcc[n]][0], ressur[bcc[n]][1], ressur[bcc[n]][2]); + } +#endif + + /* Substitute the best correction for this cell */ + for (k = 0; k < 3; k++) { + tpat[bx][by][k] = sur[bcc[n]][k]; + rtpat[bx][by][k] = ressur[bcc[n]][k]; + } + +#ifdef DIAGNOSTICS + if (vv) { + printf("Input pat:\n"); + for (x = 0; x < DISIZE; x++) { + for (y = 0; y < DISIZE; y++) { + if (y > 0) + printf(", "); + printf("%3.0f %3.0f %3.0f",rtpat[x][y][0], rtpat[x][y][1], rtpat[x][y][2]); + } + printf("\n"); + } + } +#endif + + upsample(dval, rtpat); + + werr = 0.0; + for (k = 0; k < 3; k++) { + err[k] = dval[k] - val[k]; + if (fabs(err[k]) > werr) + werr = fabs(err[k]); + } + + if (werr > berr) { + errxs += werr - berr; + } + + /* New best */ + if (werr < berr) { + berr = werr; + errxs = 0.0; + for (x = 0; x < DISIZE; x++) { + for (y = 0; y < DISIZE; y++) { + for (k = 0; k < 3; k++) { + ipat[x][y][k] = tpat[x][y][k]; + + itpat[x][y][k] = tpat[x][y][k]; + irtpat[x][y][k] = rtpat[x][y][k]; + } + } + } + } + +#ifdef DIAGNOSTICS + if (vv) { + printf("Target %f %f %f -> %f %f %f\n", val[0], val[1], val[2], dval[0], dval[1], dval[2]); + printf("Error %f %f %f werr %f\n", err[0], err[1], err[2], werr); + } +#endif + + if (berr < 0.11) { +#ifdef DIAGNOSTICS + if (vv) + printf("best error %f < 0.11 - give up\n",berr); +#endif + break; + } + + /* If we're not making progress, reset to the last best */ + if (errxs > mxerrxs) { +#ifdef DIAGNOSTICS + if (vv) + printf("Restarting at ii %d \n",ii); +#endif + errxs = 0.0; + for (x = 0; x < DISIZE; x++) { + for (y = 0; y < DISIZE; y++) { + for (k = 0; k < 3; k++) { + tpat[x][y][k] = itpat[x][y][k]; + rtpat[x][y][k] = irtpat[x][y][k]; + } + } + } + } + } + +#ifdef DIAGNOSTICS + if (vv) { + printf("Returning best error %f pat:\n",berr); + for (y = 0; y < DISIZE; y++) { + for (x = 0; x < DISIZE; x++) { + if (x > 0) + printf(", "); + printf("%3.0f %3.0f %3.0f",ipat[x][y][0], ipat[x][y][1], ipat[x][y][2]); + } + printf("\n"); + } + } +#endif + + return berr; +} + +/* ====================================================================================== */ + +#ifdef STANDALONE_TEST + +int +main(int argc, + char *argv[] +) { + double val[3], out[3], err; + double ipat[DISIZE][DISIZE][3]; + double aerr, acount, xerr; + int x, y; + int i, j; + int k, nn; + + printf("Hi there\n"); + + rand32(time(NULL)); + +#ifdef TEST_POINT + for (x = 0; x < DISIZE; x++) { + for (y = 0; y < DISIZE; y++) { + ipat[x][y][0] = 0.0; + ipat[x][y][1] = 0.0; + ipat[x][y][2] = 0.0; + } + } + + ipat[5][4][0] = 255.0; + ipat[5][4][1] = 255.0; + ipat[5][4][2] = 255.0; + + upsample(NULL, ipat); + +#else + +#ifdef NEVER + +// val[0] = 201.500000; +// val[1] = 115.533403; +// val[2] = 76.300000; + +// val[0] = 255.000000; +// val[1] = 115.533403; +// val[2] = 255.000000; + +// val[0] = 221.689875; +// val[1] = 29.593255; +// val[2] = 140.820878; + +// val[0] = 212.377797; +// val[1] = 228.338903; +// val[2] = 70.153296; + +// val[0] = 231.554511; +// val[1] = 0.000000; +// val[2] = 51.958048; + +// val[0] = 255.000000; +// val[1] = 144.768052; +// val[2] = 179.737212; + +// val[0] = 194.854956; +// val[1] = 41.901887; +// val[2] = 20.434793; + +// val[0] = 250.100121; +// val[1] = 83.484217; +// val[2] = 42.867603; + +// val[0] = 255.000000; +// val[1] = 255.000000; +// val[2] = 228.534759; + + // 1.71 -> 1.58, rand 2.2 +// val[0] = 255.000000; +// val[1] = 176.894769; +// val[2] = 8.932806; + + // 1.54 -> 0.762592, rand 0.3 +// val[0] = 216.873703; +// val[1] = 250.908094; + + // 1.05 +// val[0] = 167.284458; +// val[1] = 248.945210; +// val[2] = 199.023452; + + // 1.07 +// val[0] = 211.045184; +// val[1] = 27.825141; +// val[2] = 63.883148; + + // 1.928 +// val[0] = 255.000000; +// val[1] = 0.439284; +// val[2] = 210.928135; + + // 1.278 +// val[0] = 218.693614; +// val[1] = 222.890101; +// val[2] = 174.779727; + + // 1.334501 +// val[0] = 253.931573; +// val[1] = 230.278945; +// val[2] = 185.677389; + +// printf("In RGB %f %f %f\n",val[0],val[1],val[2]); +// ccast2YCbCr(NULL, out, val); +// printf("YCbCr %f %f %f\n",out[0],out[1],out[2]); +// YCbCr2ccast(NULL, out, out); +// printf("RGB %f %f %f\n",out[0],out[1],out[2]); + + + vv = 1; + err = get_ccast_dith(ipat, val); + + printf("Got pat with err %f\n",err); + +#else + aerr = 0.0; + acount = 0.0; + xerr = 0.0; + for (nn = 0; nn < 200000; nn++) { + + for (k = 0; k < 3; k++) { + val[k] = d_rand(-5.0, 255.0 + 5.0); + if (val[k] < 0.0) + val[k] = 0.0; + else if (val[k] > 255.0) + val[k] = 255.0; + } + + err = get_ccast_dith(ipat, val); + + if (err >= 1.5 || + ( err >= 1.0 + && val[0] != 0.0 && val[0] != 255.0 + && val[1] != 0.0 && val[1] != 255.0 + && val[2] != 0.0 && val[2] != 255.0)) { + printf("Target RGB %f %f %f, err %f\n", val[0], val[1], val[2],err); +// vv = 1; +// comput_pat(ipat, val); +// break; + } + + aerr += err; + acount++; + if (err > xerr) + xerr = err; + } + + aerr /= acount; + printf("After %d trials, aerr = %f, maxerr = %f\n",nn,aerr,xerr); +#endif +#endif + + return 0; +} + +#endif /* STANDALONE_TEST */ diff --git a/ccast/filt.c b/ccast/filt.c new file mode 100644 index 0000000..ab075ae --- /dev/null +++ b/ccast/filt.c @@ -0,0 +1,531 @@ + +/* + * Argyll Color Correction System + * ChromCast up filter test code. + * + * Author: Graeme W. Gill + * Date: 28/8/2014 + * + * Copyright 2014 Graeme W. Gill + * All rights reserved. + * + * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- + * see the License2.txt file for licencing details. + * + */ + +#include +#include +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#ifndef SALONEINSTLIB +#include "numlib.h" +#else +#include "numsup.h" +#endif +#include "yajl.h" +#include "conv.h" +#include "base64.h" +#include "ccmdns.h" +#include "ccpacket.h" +#include "ccmes.h" +#include "yajl.h" + +#define DO_WEIGHTING +#define SUM_CONSTRAINT + +//#define VERT 1 /* 1 for vertical */ + +#ifndef DBL_PI +# define DBL_PI 3.1415926535897932384626433832795 +#endif + +double lanczos3(double wi, double x) { + double y; + + x = fabs(1.0 * x/wi); + if (x >= 3.0) + return 0.0; + if (x < 1e-6) + return 1.0; + y = sin(DBL_PI * x)/(DBL_PI * x) * sin(DBL_PI * x/3.0)/(DBL_PI * x/3.0); + + return y; +} + +double lanczos2(double wi, double x) { + double y; + + x = fabs(1.0 * x/wi); + if (x >= 2.0) + return 0.0; + if (x < 1e-6) + return 1.0; + y = sin(DBL_PI * x)/(DBL_PI * x) * sin(DBL_PI * x/2.0)/(DBL_PI * x/2.0); + + return y; +} + +double in[2][72] = { +{ // Horizontal +255, 0, 0, 0, 0, 0, 0, 0, 0, +255, 0, 0, 0, 0, 0, 0, 0, 0, +254, 0, 0, 0, 0, 0, 0, 0, 0, +254, 0, 0, 0, 0, 0, 0, 0, 0, +253, 0, 0, 0, 0, 0, 0, 0, 0, +253, 0, 0, 0, 0, 0, 0, 0, 0, +252, 0, 0, 0, 0, 0, 0, 0, 0, +252, 0, 0, 0, 0, 0, 0, 0, 0 +}, { // Vertical slice input target +255, 0, 0, 0, 0, 0, 0, 0, 0, +255, 0, 0, 0, 0, 0, 0, 0, 0, +254, 0, 0, 0, 0, 0, 0, 0, 0, +254, 0, 0, 0, 0, 0, 0, 0, 0, +253, 0, 0, 0, 0, 0, 0, 0, 0, +253, 0, 0, 0, 0, 0, 0, 0, 0, +252, 0, 0, 0, 0, 0, 0, 0, 0, +252, 0, 0, 0, 0, 0, 0, 0, 0 +} }; + +#ifdef NEVER +double out[2][] = { +{ // Horizontal +170, 110, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 57, +151, 153, 61, 7, 10, 16, 16, 16, 16, 17, 15, 5, 22, +107, 169, 109, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 57, +150, 152, 61, 7, 10, 16, 16, 16, 16, 17, 15, 5, 22, +107, 168, 109, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 56, +149, 151, 61, 7, 10, 16, 16, 16, 16, 17, 15, 6, 22, +106, 167, 108, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 56, +148, 150 +}, +{ // Vertical slice target output +235, 100, 0, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13, +191, 194, 19, 0, 20, 16, 16, 16, 16, 16, 16, 16, 0, 95, +234, 99, 0, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13, +190, 194, 19, 0, 20, 16, 16, 16, 16, 16, 16, 16, 0, 94, +233, 99, 0, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13, +189, 193, 19, 0, 20, 16, 16, 16, 16, 16, 16, 16, 0, 94, +232, 99, 0, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13, +188, 192 +} +}; +#else +//#define N1 -9 +//#define N2 -8 +#define N1 0 +#define N2 0 + +//#define N3 1 +#define N4 0 + +double out[2][96] = { +{ // Horozontal +170, 110, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 57, +151, 153, 61, 7, 10, 16, 16, 16, 16, 17, 15, 5, 22, +107, 169, 109, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 57, +150, 152, 61, 7, 10, 16, 16, 16, 16, 17, 15, 5, 22, +107, 168, 109, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 56, +149, 151, 61, 7, 10, 16, 16, 16, 16, 17, 15, 6, 22, +106, 167, 108, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 56, +148, 150 +}, +{ // Vertical slice target output +235, 100, N2, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13, +191, 194, 19, N4, 20, 16, 16, 16, 16, 16, 16, 16, N1, 95, +234, 99, N2, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13, +190, 194, 19, N4, 20, 16, 16, 16, 16, 16, 16, 16, N1, 94, +233, 99, N2, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13, +189, 193, 19, N4, 20, 16, 16, 16, 16, 16, 16, 16, N1, 94, +232, 99, N2, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13, +188, 192 +} +}; +#endif + +// Computed the input to output filters. +// There will be two phases, depending on whether +// the input pixel has an even or odd address. +// Although in this case they seem like they +// almost interleave, they are actually mirror +// images with offset, so it's easier to keep them +// separate, rather than trying to figure the offset out. +// The index is the output pixel around the closest one +// to the scaled input pixel - ie. floor(in * 1.5 + 0.5) +#define FWIDTH 6 +#define NWIDTH (2 * FWIDTH + 1) + +double filt_v[2][2][NWIDTH]; +double filt_vx[2][2][NWIDTH]; /* max */ +double filt_vn[2][2][NWIDTH]; /* min */ + +double *filt[2][2] = { { &filt_v[0][0][FWIDTH], &filt_v[0][1][FWIDTH] }, + { &filt_v[1][0][FWIDTH], &filt_v[1][1][FWIDTH] } }; + +double *filtx[2][2] = { { &filt_vx[0][0][FWIDTH], &filt_vx[0][1][FWIDTH] }, + { &filt_vx[1][0][FWIDTH], &filt_vx[1][1][FWIDTH] } }; + +double *filtn[2][2] = { { &filt_vn[0][0][FWIDTH], &filt_vn[0][1][FWIDTH] }, + { &filt_vn[1][0][FWIDTH], &filt_vn[1][1][FWIDTH] } }; + +//int fneg[2] = { -5, -4 }; /* Negative index range (inclusive) */ +//int fpos[2] = { 5, 3 }; /* Positive index range (inclusive) */ +int fneg[2][2] = { { -4, -4 }, /* Negative index range (inclusive) */ + { -4, -4 } }; +int fpos[2][2] = { { 4, 3 }, /* Positive index range (inclusive) */ + { 4, 3 } }; + +/* Weightings [horiz/vert][phase] */ +double filtw_v[2][2][NWIDTH] = { + /* -6, -5, -4, -3, -2, -1, 0, +1, +2, +3, +4, +5, +6 */ +// { { 1.0, 1.0, 1.0, 5.0, 3.0, 42.0, 80.0, 43.0, 3.0, 5.0, 1.0, 1.0, 1.0 }, +// { 1.0, 1.0, 3.0, 4.0, 19.0, 62.0, 62.0, 21.0, 4.0, 3.0, 1.0, 1.0, 1.0 } }, + { { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }, + { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 } }, + /* -6, -5, -4, -3, -2, -1, 0, +1, +2, +3, +4, +5, +6 */ +// { { 1.0, 1.0, 1.0, 3.0, 17.0, 36.0, 100.0, 40.0, 15.0, 1.0, 3.0, 1.0, 1.0 }, +// { 1.0, 1.0, 1.0, 8.0, 2.0, 80.0, 81.0, 2.0, 8.0, 2.0, 1.0, 1.0, 1.0 } } + { { 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 4.0, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0 }, + { 1.0, 1.0, 1.0, 1.0, 1.0, 3.0, 3.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 } }, +}; +double *filtw[2][2] = { { &filtw_v[0][0][FWIDTH], &filtw_v[0][1][FWIDTH] }, + { &filtw_v[1][0][FWIDTH], &filtw_v[1][1][FWIDTH] } }; + +#define MX 2.0 +#define MN -0.5 + +// Compute the filter shapes +// vv = 0 for horizontal, 1 for vertical +static void compute(int vv) { + int ph, ii, i, jj, j; + double iv, ov; + int fcount[2]; + int niv = sizeof(in[vv])/sizeof(double); + int nov = sizeof(out[vv])/sizeof(double); + + // Clear the filters + for (ph = 0; ph < 2; ph++) { + for (j = -FWIDTH; j <= FWIDTH; j++) { + filt[vv][ph][j] = 0.0; + filtx[vv][ph][j] = MX; + filtn[vv][ph][j] = MN; + } + fcount[ph] = 0; + } + + // Discover an input value + for (ii = 0; ii < niv; ii++) { + double rgb[3], ycc[3]; + double prop, propx, propn; + + if (in[ii] == 0) + continue; + + iv = in[vv][ii]; + rgb[0] = rgb[1] = rgb[2] = iv; + ccast2YCbCr(NULL, ycc, rgb); + iv = ycc[0]; + + ph = ii & 1; + jj = (int)floor(ii * 1.5 + 0.5); + + for (j = -FWIDTH; j <= FWIDTH; j++) { + int k = jj + j; + if (k < 0 || k >= nov) + continue; + ov = out[vv][k]; + + prop = ((ov - 16.0)/219.0)/((iv - 16.0)/219.0); + propx = ((ov - 16.0)/219.0)/((iv - 0.5 - 16.0)/219.0); + propn = ((ov - 16.0)/219.0)/((iv + 0.5 - 16.0)/219.0); + + if (propx < propn) { + double tt = propn; + propn = propx; + propx = tt; + } + +//printf("~1 phase %d, off %d, iv %f ov %f, prop %f\n",ph,j,iv,ov,prop); + filt[vv][ph][j] += prop; + if (propx < filtx[vv][ph][j]) + filtx[vv][ph][j] = propx; + if (propn > filtn[vv][ph][j]) + filtn[vv][ph][j] = propn; + } + fcount[ph]++; + } + + // Compute average values + for (ph = 0; ph < 2; ph++) { + for (j = -FWIDTH; j <= FWIDTH; j++) + filt[vv][ph][j] /= (double)fcount[ph]; + } +} + +#define FCO2IX(bank, off) (bank ? fpos[vv][0] - fneg[vv][0] + 1 + off - fneg[vv][1] : off - fneg[vv][0]) + +// Compute the filter shapes using SVD +// vv = 0 for horizontal, 1 for vertical +static void compute2(int vv) { + int niv = sizeof(in[vv])/sizeof(double); /* Number of input values */ + int nov = sizeof(out[vv])/sizeof(double); /* Number of output values */ + int novextra = 0; + int nfc = fpos[vv][0] - fneg[vv][0] + 1 + + fpos[vv][1] - fneg[vv][1] + 1; /* Number of filter coeficients */ + double **A, *b; + int oe; /* Even or odd */ + int j, i; + +#ifdef SUM_CONSTRAINT + novextra = 3; +#endif + + nov += novextra; /* Extra constraint of sum */ + + /* We assume nov > nfc */ + A = dmatrixz(0, nov-1, 0, nfc-1); + b = dvectorz(0, nov-1); + + /* For each output value */ + for (j = 0; j < (nov-novextra); j++) { + double ww = 1.0; + +#ifdef DO_WEIGHTING + /* Figure out the weighting */ + for (oe = 0; oe < 2; oe++) { + int fix; /* Filter index */ + + /* For offset range of filter */ + for (fix = fneg[vv][oe]; fix <= fpos[vv][oe]; fix++) { + int ocx, icx, ph; + + ocx = j - fix; /* Output center of filter */ + if (ocx < 0 || ocx >= nov) + continue; /* Filter would never get applied */ + if (((2 * ocx) % 3) == 2) + continue; /* Would never get applied */ + icx = (int)floor(ocx / 1.5); /* Input center index for this output */ + if (icx < 0 || icx >= niv) + continue; /* Filter would never get applied */ + ph = icx & 1; /* Phase of filter */ + if (ph != oe) /* Not a filter that would appear at this ouput */ + continue; + if (in[vv][icx] >= 200.0) + ww = filtw[vv][ph][fix]; + } + } +#endif /* DO_WEIGHTING */ + + /* For even and odd filters */ + for (oe = 0; oe < 2; oe++) { + int fix; /* Filter index */ + + /* For offset range of filter */ + for (fix = fneg[vv][oe]; fix <= fpos[vv][oe]; fix++) { + double rgb[3], ycc[3]; + int ocx, icx, ph; + + ocx = j - fix; /* Output center of filter */ + if (ocx < 0 || ocx >= nov) + continue; /* Filter would never get applied */ + if (((2 * ocx) % 3) == 2) + continue; /* Would never get applied */ + icx = (int)floor(ocx / 1.5); /* Input center index for this output */ + if (icx < 0 || icx >= niv) + continue; /* Filter would never get applied */ + ph = icx & 1; /* Phase of filter */ + if (ph != oe) /* Not a filter that would appear at this ouput */ + continue; +//printf("j = %d/%d, k = %d/%d, ix = %d/%d\n",j,nov,oe * NWIDTH + FWIDTH + fix,nfc,ix,niv); + rgb[0] = rgb[1] = rgb[2] = in[vv][icx]; + ccast2YCbCr(NULL, ycc, rgb); + A[j][FCO2IX(oe, fix)] += ww * ycc[0]; +//printf("A[%d][%d] = %f\n",j,FCO2IX(oe, fix),A[j][FCO2IX(oe, fix)]); + } + } + b[j] = ww * out[vv][j]; +//printf("b[%d] = %f\n",j,b[j]); + } + +#ifdef SUM_CONSTRAINT + /* Add sum constraints */ + /* For 3 repeating output slots */ + for (j = nov-novextra; j < nov; j++) { + double ww = 10000.0; + int jj = j - (nov-novextra); + + b[j] = ww; + + /* For even and odd filters */ + for (oe = 0; oe < 2; oe++) { + int fix; /* Filter index */ + + /* For offset range of filter */ + for (fix = fneg[vv][oe]; fix <= fpos[vv][oe]; fix++) { + double rgb[3], ycc[3]; + int ocx, ocx2, icx, ph; + + ocx = j - fix; /* Output center of filter */ + ocx2 = 2 * ocx; + + while (ocx2 < 0) + ocx2 += 3; + while (ocx2 >= 3) + ocx2 -= 3; + + if (ocx2 == 2) + continue; /* Would never get applied */ + + while (ocx < 0) + ocx += 3; + while (ocx >= 3) + ocx -= 3; + + icx = (int)floor(ocx / 1.5); /* Input center index for this output */ + ph = icx & 1; /* Phase of filter */ + if (ph != oe) /* Not a filter that would appear at this ouput */ + continue; + A[j][FCO2IX(oe, fix)] = ww; +printf("A[%d][%d] = %f\n",j,FCO2IX(oe, fix),A[j][FCO2IX(oe, fix)]); + } + } + } +#endif /* SUM_CONSTRAINT */ + + /* Solve the equation A.x = b using SVD */ + /* (The w[] values are thresholded for best accuracy) */ + /* Return non-zero if no solution found */ + if (svdsolve(A, b, nov, nfc)) + error("svdsolve failed"); + + /* Print the filter shape */ + /* and copy to the filter */ + printf("SVD computed for %s:\n", vv ? "vertical" : "horizontal"); + for (oe = 0; oe < 2; oe++) { + int fix; /* Filter index */ + double sum = 0.0; + + printf("Phase %d\n",oe); +// for (fix = -FWIDTH; fix <= FWIDTH; fix++) { + for (fix = fneg[vv][oe]; fix <= fpos[vv][oe]; fix++) { + printf(" %d -> %f\n",fix, b[FCO2IX(oe, fix)]); + sum += b[FCO2IX(oe, fix)]; + filt[vv][oe][fix] = b[FCO2IX(oe, fix)]; + } + printf("sum = %f\n",sum); + } +} + +void check(int vv) { + double *chout; + int niv = sizeof(in[vv])/sizeof(double); /* Number of input values */ + int nov = sizeof(out[vv])/sizeof(double); /* Number of output values */ + + int range, i, ii, j; + double xc, x, iv, tw, w, y; + double cout, terr = 0.0; + +printf("~1 nov = %d\n",nov); + if ((chout = (double *)malloc(sizeof(double) * nov)) == NULL) + error("Malloc failed"); + + // Clear the output + for (i = 0; i < nov; i++) + chout[i] = 0.0; + + // For all the input value + for (ii = 0; ii < niv; ii++) { + int ph, jj; + double rgb[3], ycc[3]; + double prop; + + iv = in[vv][ii]; + rgb[0] = rgb[1] = rgb[2] = iv; + ccast2YCbCr(NULL, ycc, rgb); + iv = ycc[0]; + + ph = ii & 1; + jj = (int)floor(ii * 1.5 + 0.5); + + for (j = -FWIDTH; j <= FWIDTH; j++) { + int k = jj + j; + if (k < 0 || k >= nov) + continue; + +//if ((jj + j) == 4) printf("[%d] += w %f * iv %f\n",jj + j, filt[ph][j], iv); + chout[k] += filt[vv][ph][j] * iv; + } + } + + for (i = 0; i < nov; i++) { + double ov, ee; + ov = chout[i]; + ov = floor(ov + 0.5); +#ifdef NEVER + if (ov < 0.0) + ov = 0.0; + else if (ov > 255.0) + ov = 255.0; +#endif + ee = ov - out[vv][i]; + terr += ee * ee; + printf("out %d = %f should be %f err %f\n",i,chout[i],out[vv][i],ee); + } + + printf("Total err = %f RMS\n",sqrt(terr)); +} + +int +main(int argc, + char *argv[] +) { + int ph, vv, j; + + double err; + double cp[2]; /* Initial starting point */ + double s[2]; /* Size of initial search area */ + + printf("Hi there\n"); + +#ifdef NEVER + compute(1); + + // Print filter shape + printf("Directly computed:\n"); + for (ph = 0; ph < 2; ph++) { + printf("Phase %d\n",ph); + for (j = -FWIDTH; j <= FWIDTH; j++) + printf(" %d -> min %f, avg %f, max %f\n",j,filtn[1][ph][j],filt[1][ph][j],filtx[1][ph][j]); + } +#endif + + for (vv = 0; vv < 2; vv++) { + compute2(vv); + check(vv); + } + + /* Output code to stdout */ + for (ph = 0; ph < 2; ph++) { + fprintf(stderr,"/* Weightings [horiz/vert] */\n"); + fprintf(stderr,"double filt_v_%s[2][%s_WIDTH] = {\n",ph ? "od" : "ev", ph ? "OD" : "EV"); + + for (vv = 0; vv < 2; vv++) { + int fix; /* Filter index */ + + fprintf(stderr,"{ "); + for (fix = fneg[vv][ph]; fix <= fpos[vv][ph]; fix++) { + if (fix > fneg[vv][ph]) + fprintf(stderr,", "); + fprintf(stderr,"%f", filt[vv][ph][fix]); + } + fprintf(stderr," }%s\n",vv == 0 ? "," : ""); + } + fprintf(stderr,"};\n\n"); + } + + return 0; +} + -- cgit v1.2.3