summaryrefslogtreecommitdiff
path: root/ccast
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2015-05-01 16:13:57 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2015-05-01 16:13:57 +0200
commit094535c010320967639e8e86f974d878e80baa72 (patch)
treeefc3094b20355dcbeebb2c4ece4fcfc69bffedb5 /ccast
parentc07d0c2d2f6f7b0eb6e92cc6204bf05037957e82 (diff)
Imported Upstream version 1.7.0upstream/1.7.0
Diffstat (limited to 'ccast')
-rw-r--r--ccast/Jamfile34
-rw-r--r--ccast/License.txt662
-rw-r--r--ccast/Readme.txt12
-rw-r--r--ccast/afiles54
-rw-r--r--ccast/axTLS/Jamfile42
-rw-r--r--ccast/axTLS/Readme.txt7
-rw-r--r--ccast/axTLS/aes.c457
-rw-r--r--ccast/axTLS/asn1.c566
-rw-r--r--ccast/axTLS/bigint.c1512
-rw-r--r--ccast/axTLS/bigint.h99
-rw-r--r--ccast/axTLS/bigint_impl.h131
-rw-r--r--ccast/axTLS/cert.h43
-rw-r--r--ccast/axTLS/config.h41
-rw-r--r--ccast/axTLS/crypto.h230
-rw-r--r--ccast/axTLS/crypto_misc.c367
-rw-r--r--ccast/axTLS/crypto_misc.h172
-rw-r--r--ccast/axTLS/gen_cert.c368
-rw-r--r--ccast/axTLS/hmac.c105
-rw-r--r--ccast/axTLS/loader.c483
-rw-r--r--ccast/axTLS/md2.c162
-rw-r--r--ccast/axTLS/md5.c294
-rw-r--r--ccast/axTLS/openssl.c323
-rw-r--r--ccast/axTLS/os_int.h74
-rw-r--r--ccast/axTLS/os_port.c160
-rw-r--r--ccast/axTLS/os_port.h194
-rw-r--r--ccast/axTLS/p12.c483
-rw-r--r--ccast/axTLS/private_key.h54
-rw-r--r--ccast/axTLS/rc4.c92
-rw-r--r--ccast/axTLS/rsa.c270
-rw-r--r--ccast/axTLS/sha1.c249
-rw-r--r--ccast/axTLS/ssl.h513
-rw-r--r--ccast/axTLS/temp2
-rw-r--r--ccast/axTLS/tls1.c2411
-rw-r--r--ccast/axTLS/tls1.h309
-rw-r--r--ccast/axTLS/tls1_clnt.c397
-rw-r--r--ccast/axTLS/tls1_svr.c478
-rw-r--r--ccast/axTLS/version.h1
-rw-r--r--ccast/axTLS/x509.c561
-rw-r--r--ccast/cast_channel.proto79
-rw-r--r--ccast/ccast.c1319
-rw-r--r--ccast/ccast.h108
-rw-r--r--ccast/ccmdns.c1096
-rw-r--r--ccast/ccmdns.h42
-rw-r--r--ccast/ccmes.c334
-rw-r--r--ccast/ccmes.h103
-rw-r--r--ccast/ccpacket.c501
-rw-r--r--ccast/ccpacket.h89
-rw-r--r--ccast/cctest.c329
-rw-r--r--ccast/chan/cast_channel.pb-c.c593
-rw-r--r--ccast/chan/cast_channel.pb-c.h233
-rw-r--r--ccast/chan/protobuf-c.c3287
-rw-r--r--ccast/chan/protobuf-c.h1079
-rw-r--r--ccast/dpat.c952
-rw-r--r--ccast/filt.c531
54 files changed, 23087 insertions, 0 deletions
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. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU 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 <http://www.gnu.org/licenses/>.
+
+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
+<http://www.gnu.org/licenses/>.
+
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 <string.h>
+#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 <brg@gladman.uk.net> 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<ii; i++)
+ {
+ tmp = W[i-1];
+
+ if ((i % words) == 0)
+ {
+ tmp2 =(uint32_t)aes_sbox[(tmp )&0xff]<< 8;
+ tmp2|=(uint32_t)aes_sbox[(tmp>> 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#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
+ * <http://cs.marlboro.edu/term/cs-fall02/algorithms/crypto/RSA/bigint>
+ * and GMP <http://www.swox.com/gmp>. It gets most of its implementation
+ * detail from "The Handbook of Applied Cryptography"
+ * <http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf>
+ * @{
+ */
+
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#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<<COMP_BIT_SIZE) + biR->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 <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#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 <string.h>
+#include <stdlib.h>
+#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 <string.h>
+#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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#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 <string.h>
+#include <stdio.h>
+#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 <string.h>
+#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 <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#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 <stdint.h>
+
+#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 <time.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#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 <stdio.h>
+
+#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 <basetsd.h>
+#define abort() exit(1)
+#else
+#include <io.h>
+#include <process.h>
+#include <sys/timeb.h>
+#include <fcntl.h>
+#endif /* _WIN32_WCE */
+
+#include <winsock.h>
+#include <direct.h>
+#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 <unistd.h>
+#include <pwd.h>
+#include <netdb.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#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 <pthread.h>
+#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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#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 <string.h>
+#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 <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#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 <string.h>
+#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 <time.h>
+
+/* 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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#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 <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <stdio.h>
+#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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#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 = "<Not Part Of Certificate>";
+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 <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <time.h>
+#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 <http://stackoverflow.com/questions/8659638/how-does-windows-know-how-to-resolve-mdns-queries>
+
+ 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 <time.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <math.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include "copyright.h"
+#include "aconfig.h"
+#include <sys/types.h>
+#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 <winsock2.h>
+# include <windows.h>
+# include <ws2tcpip.h>
+
+# include <process.h>
+# include <direct.h>
+# include <io.h>
+
+# define ERRNO GetLastError()
+# define UDP_SOCKET_TIMEOUT WSAETIMEDOUT
+
+#else /* !NT = Unix, OS X */
+
+# include <errno.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <sys/socket.h>
+# include <sys/select.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <sys/time.h>
+# include <stdint.h>
+# include <inttypes.h>
+# include <netdb.h>
+
+# include <pwd.h>
+# include <unistd.h>
+# include <dirent.h>
+
+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 <time.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <math.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include "copyright.h"
+#include "aconfig.h"
+#include <sys/types.h>
+#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 <time.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <math.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include "copyright.h"
+#include "aconfig.h"
+#include <sys/types.h>
+#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 <winsock2.h>
+# include <windows.h>
+# include <ws2tcpip.h>
+
+# include <process.h>
+# include <direct.h>
+# include <io.h>
+
+# define ERRNO GetLastError()
+# ifndef ETIMEDOUT
+# define ETIMEDOUT WSAETIMEDOUT
+# endif
+
+#else /* !NT = Unix, OS X */
+
+# include <errno.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <sys/socket.h>
+# include <sys/select.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <sys/time.h>
+# include <stdint.h>
+# include <inttypes.h>
+# include <netdb.h>
+
+# include <pwd.h>
+# include <unistd.h>
+# include <dirent.h>
+
+#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 <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/types.h>
+#include <time.h>
+#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 <stdlib.h> /* for malloc, free */
+#include <string.h> /* 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<<BOUND_SIZEOF_SCANNED_MEMBER_LOG2) */
+#define BOUND_SIZEOF_SCANNED_MEMBER_LOG2 5
+typedef struct _ScannedMember ScannedMember;
+/** Field as it's being read. */
+struct _ScannedMember {
+ uint32_t tag; /**< Field tag. */
+ uint8_t wire_type; /**< Field type. */
+ uint8_t length_prefix_len; /**< Prefix length. */
+ const ProtobufCFieldDescriptor *field; /**< Field descriptor. */
+ size_t len; /**< Field length. */
+ const uint8_t *data; /**< Pointer to field data. */
+};
+
+static inline uint32_t
+scan_length_prefixed_data(size_t len, const uint8_t *data,
+ size_t *prefix_len_out)
+{
+ unsigned hdr_max = len < 5 ? len : 5;
+ unsigned hdr_len;
+ uint32_t val = 0;
+ unsigned i;
+ unsigned shift = 0;
+
+ for (i = 0; i < hdr_max; i++) {
+ val |= (data[i] & 0x7f) << shift;
+ shift += 7;
+ if ((data[i] & 0x80) == 0)
+ break;
+ }
+ if (i == hdr_max) {
+ PROTOBUF_C_UNPACK_ERROR(("error parsing length for length-prefixed data"))
+ return 0;
+ }
+ hdr_len = i + 1;
+ *prefix_len_out = hdr_len;
+ if (hdr_len + val > len) {
+ PROTOBUF_C_UNPACK_ERROR(("data too short after length-prefix of %u", val))
+ return 0;
+ }
+ return hdr_len + val;
+}
+
+static size_t
+max_b128_numbers(size_t len, const uint8_t *data)
+{
+ size_t rv = 0;
+ while (len--)
+ if ((*data++ & 0x80) == 0)
+ ++rv;
+ return rv;
+}
+
+/**@}*/
+
+/**
+ * Merge earlier message into a latter message.
+ *
+ * For numeric types and strings, if the same value appears multiple
+ * times, the parser accepts the last value it sees. For embedded
+ * message fields, the parser merges multiple instances of the same
+ * field. That is, all singular scalar fields in the latter instance
+ * replace those in the former, singular embedded messages are merged,
+ * and repeated fields are concatenated.
+ *
+ * The earlier message should be freed after calling this function, as
+ * some of its fields may have been reused and changed to their default
+ * values during the merge.
+ */
+static protobuf_c_boolean
+merge_messages(ProtobufCMessage *earlier_msg,
+ ProtobufCMessage *latter_msg,
+ ProtobufCAllocator *allocator)
+{
+ unsigned i;
+ const ProtobufCFieldDescriptor *fields =
+ earlier_msg->descriptor->fields;
+ for (i = 0; i < latter_msg->descriptor->n_fields; i++) {
+ if (fields[i].label == PROTOBUF_C_LABEL_REPEATED) {
+ size_t *n_earlier =
+ STRUCT_MEMBER_PTR(size_t, earlier_msg,
+ fields[i].quantifier_offset);
+ uint8_t **p_earlier =
+ STRUCT_MEMBER_PTR(uint8_t *, earlier_msg,
+ fields[i].offset);
+ size_t *n_latter =
+ STRUCT_MEMBER_PTR(size_t, latter_msg,
+ fields[i].quantifier_offset);
+ uint8_t **p_latter =
+ STRUCT_MEMBER_PTR(uint8_t *, latter_msg,
+ fields[i].offset);
+
+ if (*n_earlier > 0) {
+ if (*n_latter > 0) {
+ /* Concatenate the repeated field */
+ size_t el_size =
+ sizeof_elt_in_repeated_array(fields[i].type);
+ uint8_t *new_field;
+
+ new_field = do_alloc(allocator,
+ (*n_earlier + *n_latter) * el_size);
+ if (!new_field)
+ return FALSE;
+
+ memcpy(new_field, *p_earlier,
+ *n_earlier * el_size);
+ memcpy(new_field +
+ *n_earlier * el_size,
+ *p_latter,
+ *n_latter * el_size);
+
+ do_free(allocator, *p_latter);
+ do_free(allocator, *p_earlier);
+ *p_latter = new_field;
+ *n_latter = *n_earlier + *n_latter;
+ } else {
+ /* Zero copy the repeated field from the earlier message */
+ *n_latter = *n_earlier;
+ *p_latter = *p_earlier;
+ }
+ /* Make sure the field does not get double freed */
+ *n_earlier = 0;
+ *p_earlier = 0;
+ }
+ } else if (fields[i].type == PROTOBUF_C_TYPE_MESSAGE) {
+ ProtobufCMessage **em =
+ STRUCT_MEMBER_PTR(ProtobufCMessage *,
+ earlier_msg,
+ fields[i].offset);
+ ProtobufCMessage **lm =
+ STRUCT_MEMBER_PTR(ProtobufCMessage *,
+ latter_msg,
+ fields[i].offset);
+ if (*em != NULL) {
+ if (*lm != NULL) {
+ if (!merge_messages
+ (*em, *lm, allocator))
+ return FALSE;
+ } else {
+ /* Zero copy the optional message */
+ assert(fields[i].label ==
+ PROTOBUF_C_LABEL_OPTIONAL);
+ *lm = *em;
+ *em = NULL;
+ }
+ }
+ } else if (fields[i].label == PROTOBUF_C_LABEL_OPTIONAL) {
+ size_t el_size = 0;
+ protobuf_c_boolean need_to_merge = FALSE;
+ void *earlier_elem =
+ STRUCT_MEMBER_P(earlier_msg, fields[i].offset);
+ void *latter_elem =
+ STRUCT_MEMBER_P(latter_msg, fields[i].offset);
+ const void *def_val = fields[i].default_value;
+
+ switch (fields[i].type) {
+ case PROTOBUF_C_TYPE_BYTES: {
+ uint8_t *e_data, *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 <assert.h>
+#include <limits.h>
+#include <stddef.h>
+#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 <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/types.h>
+#include <time.h>
+#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 <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/types.h>
+#include <time.h>
+#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;
+}
+