summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephane Glondu <steph@glondu.net>2019-08-03 10:15:24 +0200
committerStephane Glondu <steph@glondu.net>2019-08-03 10:15:24 +0200
commitcaf332912456c7af52ba76387bad03b460b3b2b8 (patch)
treefa60041ec6b62fc33137c74563f48b3375827767
parentbbb9402dfbab7214ced74c1a9c8d64c1fd6b16d3 (diff)
New upstream version 1.13
-rw-r--r--.gitignore5
-rw-r--r--Changes11
-rw-r--r--LICENSE501
-rw-r--r--README.md84
-rw-r--r--README.txt4
-rw-r--r--_oasis23
-rw-r--r--_tags12
-rw-r--r--myocamlbuild.ml7
-rw-r--r--setup.ml161
-rw-r--r--src/META4
-rw-r--r--src/chacha20.c162
-rw-r--r--src/chacha20.h23
-rw-r--r--src/cryptokit.ml113
-rw-r--r--src/cryptokit.mli161
-rw-r--r--src/libcryptokit_stubs.clib4
-rw-r--r--src/stubs-chacha20.c58
-rw-r--r--test/prngtest.ml49
-rw-r--r--test/speedtest.ml14
-rw-r--r--test/test.ml72
19 files changed, 1381 insertions, 87 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ffcec6b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+_build
+setup.data
+setup.log
+*.native
+*~
diff --git a/Changes b/Changes
index c9a5142..475c242 100644
--- a/Changes
+++ b/Changes
@@ -1,3 +1,14 @@
+Release 1.13:
+- Add the Chacha20 stream cipher.
+- Add the AES-CMAC (a.k.a. AES-OMAC1) message authentication code.
+- Pseudo-random number generator: replace the old AES-CBC-Fibonacci generator
+ with a faster, simpler generator based on Chacha20.
+- Add an alternate pseudo-random number generator based on AES in CTR mode.
+- Documentation: warn about known cryptographic weaknesses in Triple DES,
+ Blowfish, and ARCfour.
+- Documentation: warn about problems with variable-length messages in
+ MACs based on block ciphers in CBC mode.
+
Release 1.12:
- Fix x86-32 compilation error and improve detection of AES-NI for x86
processors (Jeremie Dimino, Etienne Millon)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..604c2ba
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,501 @@
+This Library is distributed under the terms of the GNU Library General
+Public License version 2 (included below).
+
+As a special exception to the GNU Library General Public License, you
+may link, statically or dynamically, a "work that uses the Library"
+with a publicly distributed version of the Library to produce an
+executable file containing portions of the Library, and distribute
+that executable file under terms of your choice, without any of the
+additional requirements listed in clause 6 of the GNU Library General
+Public License. By "a publicly distributed version of the Library",
+we mean either the unmodified Library as distributed by INRIA, or a
+modified version of the Library that is distributed under the
+conditions defined in clause 3 of the GNU Library General Public
+License. This exception does not however invalidate any other reasons
+why the executable file might be covered by the GNU Library General
+Public License.
+
+----------------------------------------------------------------------
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library 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 Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey 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 library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..fb23f8a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,84 @@
+# The Cryptokit library
+
+## Overview
+
+The Cryptokit library for OCaml provides a variety of cryptographic primitives that can be used to implement cryptographic protocols in security-sensitive applications. The primitives provided include:
+
+* Symmetric-key ciphers: AES, Chacha20, DES, Triple-DES, Blowfish, ARCfour, in ECB, CBC, CFB, OFB and counter modes.
+* Public-key cryptography: RSA encryption and signature, Diffie-Hellman key agreement.
+* Hash functions and MACs: SHA-3, SHA-1, SHA-2, RIPEMD-160, MD5, and MACs based on AES and DES.
+* Random number generation.
+* Encodings and compression: base 64, hexadecimal, Zlib compression.
+
+Additional ciphers and hashes can easily be used in conjunction with the library. In particular, basic mechanisms such as chaining modes, output buffering, and padding are provided by generic classes that can easily be composed with user-provided ciphers. More generally, the library promotes a "Lego"-like style of constructing and composing transformations over character streams.
+
+This library is distributed under the conditions of the GNU Library General Public license version 2, with the special OCaml exception on linking described in file LICENSE.
+
+## Requirements
+
+* OCaml 4.02 or more recent.
+* The findlib/ocamlfind tool.
+* The Zarith library, version 1.4 or more recent.
+* The Zlib C library, version 1.1.3 or up is recommended. If it is not installed on your system (look for libz.a or libz.so), get it from http://www.gzip.org/, or indicate in the Makefile that you do not have it. If you are running Linux or BSD or MacOS, your distribution provides precompiled binaries for this library.
+* If the operating system does not provide the `/dev/random` device for random number generation, consider installing the [EGD](http://egd.sourceforge.net/) entropy gathering daemon. Without `/dev/random` nor EGD, this library cannot generate cryptographically-strong random data nor RSA keys. The remainder of the library still works, though.
+
+## Installation
+
+```
+./configure --enable-tests
+make
+make test
+make install
+```
+
+## Documentation
+
+See the extensive documentation comments in file src/cryptokit.mli.
+
+Compilation options: `ocamlfind ocamlopt -package cryptokit`...
+
+Linking options: `ocamlfind ocamlopt -linkpkg -package cryptokit`...
+
+## Warnings and disclaimers
+
+Disclaimer 1: the author is not an expert in cryptography. While reasonable care has been taken to select good, widely-used implementations of the ciphers and hashes, and follow recommended practices found in reputable applied cryptography textbooks, you are advised to review thoroughly the implementation of this module before using it in a security-critical application.
+
+Disclaimer 2: some knowledge of cryptography is needed to use effectively this library. A recommended reading is the [Handbook of Applied Cryptography](http://www.cacr.math.uwaterloo.ca/hac/). Building secure applications out of cryptographic primitives also requires a general background in computer security.
+
+Disclaimer 3: in some countries, the use, distribution, import and/or export of cryptographic applications is restricted by law. The precise restrictions may depend on the strenght of the cryptography used (e.g. key size), but also on its purpose (e.g. confidentiality vs. authentication). It is up to the users of this library to comply with regulations applicable in their country.
+
+## Design notes and references
+
+The library is organized around the concept of "transforms". A transform is an object that accepts strings, sub-strings, characters and bytes as input, transforms them, and buffers the output. While it is possible to enter all input, then fetch the output, lower memory requirements can be achieved by purging the output periodically during data input.
+
+The AES implementation is the public-domain optimized reference implementation by Daemen, Rijmen and Barreto. On x86 processors that support the AES-NI extensions, hardware implementation is used instead.
+
+The Chacha20 implementation is due to D.J.Bernstein, https://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/regs/chacha.c . It is in the public domain.
+
+The DES implementation is based on Outerbridge's popular "d3des" implementation. This is not the fastest DES implementation available, but one of the cleanest. Outerbridge's code is marked as public domain.
+
+The Blowfish implementation is that of Paul Kocher with some performance improvements. It is under the LGPL. It passes the test vectors listed at http://www.schneier.com/code/vectors.txt
+
+ARCfour (``alleged RC4'') is implemented from scratch, based on the algorithm described in Schneier's _Applied Cryptography_
+
+SHA-1 is also implemented from scratch, based on the algorithm described in the _Handbook of Applied Cryptography_. It passes the FIPS test vectors.
+
+SHA-2 is implemented from scratch based on FIPS publication 180-2. It passes the FIPS test vectors.
+
+SHA-3 is based on the "readable" implementation of Keccak written by Markku-Juhani O. Saarinen <mjos@iki.fi>.
+
+RIPEMD-160 is based on the reference implementation by A.Bosselaers. It passes the test vectors listed at http://www.esat.kuleuven.ac.be/~bosselae/ripemd160.html
+
+MD5 uses the public-domain implementation by Colin Plumb that is also used in the OCaml runtime system for module Digest.
+
+RSA encryption and decryption was implemented from scratch, using the Zarith OCaml library for arbitrary-precision arithmetic, which itself uses GMP. Modular exponentiation is the constant-time implementation provided by GMP. The Chinese remainder theorem is exploited when possible, though. Like all ciphers in this library, the RSA implementation is *not* protected against timing attacks.
+
+RSA key generation uses GMP's `nextprime` function for probabilistic primality testing.
+
+The hardware RNG uses the RDRAND instruction of recent x86 processors, if supported. It is not available on other platforms.
+
+The seeded PRNG is just the Chacha20 stream cipher encrypting the all-zeroes message. The seed is used as the Chacha20 key. An alternate seeded PRNG is provided, based on AES encryption of a 128-bit counter. Both PRNGs pass the Dieharder statistical tests. Still, better use the system RNG or the hardware RNG if high-quality random numbers are needed.
+
+## Performance
+
+If you configure with the options `--enable-tests --enable-bench`, then do `make test`, a simple benchmark is performed and shows the speed of various operations from this library.
diff --git a/README.txt b/README.txt
index fa26694..406f45d 100644
--- a/README.txt
+++ b/README.txt
@@ -1,5 +1,5 @@
(* OASIS_START *)
-(* DO NOT EDIT (digest: 54340abf9f934bc8c8a02312afd5aa74) *)
+(* DO NOT EDIT (digest: 38dc311195a3981d90ae188e25b31bc8) *)
cryptokit - Cryptographic primitives
====================================
@@ -8,7 +8,7 @@ This library provides a variety of cryptographic primitives that can be used
to implement cryptographic protocols in security-sensitive applications. The
primitives provided include:
-- Symmetric-key ciphers: AES, DES, Triple-DES, ARCfour,
+- Symmetric-key ciphers: AES, Chacha20, Blowfish, DES, Triple-DES, ARCfour,
in ECB, CBC, CFB, OFB and counter modes.
- Public-key cryptography: RSA encryption, Diffie-Hellman key agreement. -
Hash functions and MACs: SHA-1, SHA-2, SHA-3, RIPEMD160, MD5,
diff --git a/_oasis b/_oasis
index 581940d..abebe23 100644
--- a/_oasis
+++ b/_oasis
@@ -1,6 +1,6 @@
OASISFormat: 0.3
Name: cryptokit
-Version: 1.12
+Version: 1.13
Authors: Xavier Leroy
License: LGPL-2 with OCaml linking exception
BuildTools: ocamlbuild, ocamldoc
@@ -12,7 +12,7 @@ Description:
to implement cryptographic protocols in security-sensitive applications. The
primitives provided include:
.
- - Symmetric-key ciphers: AES, DES, Triple-DES, ARCfour,
+ - Symmetric-key ciphers: AES, Chacha20, Blowfish, DES, Triple-DES, ARCfour,
in ECB, CBC, CFB, OFB and counter modes.
- Public-key cryptography: RSA encryption, Diffie-Hellman key agreement.
- Hash functions and MACs: SHA-1, SHA-2, SHA-3, RIPEMD160, MD5,
@@ -32,7 +32,7 @@ Flag zlib
Default$: !os_type(Win32)
Flag hardwaresupport
- Description: Enable hardware support for AES (needs GCC or Clang)
+ Description: Enable hardware support for AES and GCM (needs GCC or Clang)
Default$: (architecture(amd64) || architecture(i386)) && !os_type(Win32)
Library cryptokit
@@ -70,7 +70,10 @@ Library cryptokit
stubs-zlib.c,
keccak.h,
keccak.c,
- stubs-sha3.c
+ stubs-sha3.c,
+ chacha20.h,
+ chacha20.c,
+ stubs-chacha20.c
BuildDepends: unix, zarith
if flag(zlib)
CCOpt: -DHAVE_ZLIB
@@ -93,6 +96,14 @@ Executable test
Build$: flag(tests)
Install: false
+Executable prngtest
+ Path: test
+ MainIs: prngtest.ml
+ CompiledObject: native
+ BuildDepends: cryptokit
+ Build$: flag(tests)
+ Install: false
+
Test main
Command: $test
TestTools: test
@@ -129,5 +140,5 @@ SourceRepository head
SourceRepository this
Type: git
- Location: https://github.com/xavierleroy/cryptokit/releases/tag/release112
- Browser: https://github.com/xavierleroy/cryptokit/releases/tag/release112
+ Location: https://github.com/xavierleroy/cryptokit/releases/tag/release113
+ Browser: https://github.com/xavierleroy/cryptokit/releases/tag/release113
diff --git a/_tags b/_tags
index d626e50..e5e0082 100644
--- a/_tags
+++ b/_tags
@@ -1,5 +1,5 @@
# OASIS_START
-# DO NOT EDIT (digest: 0942acdef56237583b50126ab62c0ec8)
+# DO NOT EDIT (digest: 62ba61e1d8ad56a1e96795f7c6fb78e2)
# Ignore VCS directories, you can use the same kind of rule outside
# OASIS_START/STOP if you want to exclude directories that contains
# useless stuff for the build process
@@ -40,6 +40,8 @@ true: annot, bin_annot
"src/stubs-zlib.c": oasis_library_cryptokit_ccopt
"src/keccak.c": oasis_library_cryptokit_ccopt
"src/stubs-sha3.c": oasis_library_cryptokit_ccopt
+"src/chacha20.c": oasis_library_cryptokit_ccopt
+"src/stubs-chacha20.c": oasis_library_cryptokit_ccopt
<src/cryptokit.{cma,cmxa}>: oasis_library_cryptokit_cclib
"src/libcryptokit_stubs.lib": oasis_library_cryptokit_cclib
"src/dllcryptokit_stubs.dll": oasis_library_cryptokit_cclib
@@ -94,10 +96,18 @@ true: annot, bin_annot
"src/keccak.c": pkg_zarith
"src/stubs-sha3.c": pkg_unix
"src/stubs-sha3.c": pkg_zarith
+"src/chacha20.c": pkg_unix
+"src/chacha20.c": pkg_zarith
+"src/stubs-chacha20.c": pkg_unix
+"src/stubs-chacha20.c": pkg_zarith
# Executable test
"test/test.native": pkg_unix
"test/test.native": pkg_zarith
"test/test.native": use_cryptokit
+# Executable prngtest
+"test/prngtest.native": pkg_unix
+"test/prngtest.native": pkg_zarith
+"test/prngtest.native": use_cryptokit
# Executable speedtest
"test/speedtest.native": pkg_unix
"test/speedtest.native": pkg_zarith
diff --git a/myocamlbuild.ml b/myocamlbuild.ml
index 17a97e0..a07c579 100644
--- a/myocamlbuild.ml
+++ b/myocamlbuild.ml
@@ -1,5 +1,5 @@
(* OASIS_START *)
-(* DO NOT EDIT (digest: f9fc08b1c2e067f57d033cecfa00f2c0) *)
+(* DO NOT EDIT (digest: 7ab3acc49c3c9131310ec300b2562fe8) *)
module OASISGettext = struct
(* # 22 "src/oasis/OASISGettext.ml" *)
@@ -894,7 +894,8 @@ let package_default =
"src/sha1.h";
"src/sha256.h";
"src/sha512.h";
- "src/keccak.h"
+ "src/keccak.h";
+ "src/chacha20.h"
])
];
flags =
@@ -1201,6 +1202,6 @@ let conf = {MyOCamlbuildFindlib.no_automatic_syntax = false}
let dispatch_default = MyOCamlbuildBase.dispatch_default conf package_default;;
-# 1205 "myocamlbuild.ml"
+# 1206 "myocamlbuild.ml"
(* OASIS_STOP *)
Ocamlbuild_plugin.dispatch dispatch_default;;
diff --git a/setup.ml b/setup.ml
index 5290b8f..83da80d 100644
--- a/setup.ml
+++ b/setup.ml
@@ -1,7 +1,7 @@
(* setup.ml generated for the first time by OASIS v0.4.6 *)
(* OASIS_START *)
-(* DO NOT EDIT (digest: 924b717b9b76d9bf10d4cc12d81efc8c) *)
+(* DO NOT EDIT (digest: d4c571bd5629a18c3343ee6fabc026b5) *)
(*
Regenerated by OASIS v0.4.10
Visit http://oasis.forge.ocamlcore.org for more information and
@@ -7051,7 +7051,7 @@ let setup_t =
{
oasis_version = "0.3";
ocaml_version = None;
- version = "1.12";
+ version = "1.13";
license =
OASISLicense.DEP5License
(OASISLicense.DEP5Unit
@@ -7077,7 +7077,7 @@ let setup_t =
OASISText.Para
"This library provides a variety of cryptographic primitives that can be used to implement cryptographic protocols in security-sensitive applications. The primitives provided include:";
OASISText.Para
- "- Symmetric-key ciphers: AES, DES, Triple-DES, ARCfour,";
+ "- Symmetric-key ciphers: AES, Chacha20, Blowfish, DES, Triple-DES, ARCfour,";
OASISText.Verbatim
" in ECB, CBC, CFB, OFB and counter modes.";
OASISText.Para
@@ -7118,7 +7118,7 @@ let setup_t =
{
flag_description =
Some
- "Enable hardware support for AES (needs GCC or Clang)";
+ "Enable hardware support for AES and GCM (needs GCC or Clang)";
flag_default =
[
(OASISExpr.EBool true, false);
@@ -7293,7 +7293,10 @@ let setup_t =
"stubs-zlib.c";
"keccak.h";
"keccak.c";
- "stubs-sha3.c"
+ "stubs-sha3.c";
+ "chacha20.h";
+ "chacha20.c";
+ "stubs-chacha20.c"
];
bs_data_files = [];
bs_findlib_extra_files = [];
@@ -7596,6 +7599,145 @@ let setup_t =
bs_nativeopt = [(OASISExpr.EBool true, [])]
},
{exec_custom = false; exec_main_is = "test.ml"});
+ Executable
+ ({
+ cs_name = "prngtest";
+ cs_data = PropList.Data.create ();
+ cs_plugin_data = []
+ },
+ {
+ bs_build =
+ [
+ (OASISExpr.EBool true, false);
+ (OASISExpr.EFlag "tests", true)
+ ];
+ bs_install = [(OASISExpr.EBool true, false)];
+ bs_path = "test";
+ bs_compiled_object = Native;
+ bs_build_depends = [InternalLibrary "cryptokit"];
+ bs_build_tools =
+ [ExternalTool "ocamlbuild"; ExternalTool "ocamldoc"];
+ bs_interface_patterns =
+ [
+ {
+ OASISSourcePatterns.Templater.atoms =
+ [
+ OASISSourcePatterns.Templater.Text "";
+ OASISSourcePatterns.Templater.Expr
+ (OASISSourcePatterns.Templater.Call
+ ("capitalize_file",
+ OASISSourcePatterns.Templater.Ident
+ "module"));
+ OASISSourcePatterns.Templater.Text ".mli"
+ ];
+ origin = "${capitalize_file module}.mli"
+ };
+ {
+ OASISSourcePatterns.Templater.atoms =
+ [
+ OASISSourcePatterns.Templater.Text "";
+ OASISSourcePatterns.Templater.Expr
+ (OASISSourcePatterns.Templater.Call
+ ("uncapitalize_file",
+ OASISSourcePatterns.Templater.Ident
+ "module"));
+ OASISSourcePatterns.Templater.Text ".mli"
+ ];
+ origin = "${uncapitalize_file module}.mli"
+ }
+ ];
+ bs_implementation_patterns =
+ [
+ {
+ OASISSourcePatterns.Templater.atoms =
+ [
+ OASISSourcePatterns.Templater.Text "";
+ OASISSourcePatterns.Templater.Expr
+ (OASISSourcePatterns.Templater.Call
+ ("capitalize_file",
+ OASISSourcePatterns.Templater.Ident
+ "module"));
+ OASISSourcePatterns.Templater.Text ".ml"
+ ];
+ origin = "${capitalize_file module}.ml"
+ };
+ {
+ OASISSourcePatterns.Templater.atoms =
+ [
+ OASISSourcePatterns.Templater.Text "";
+ OASISSourcePatterns.Templater.Expr
+ (OASISSourcePatterns.Templater.Call
+ ("uncapitalize_file",
+ OASISSourcePatterns.Templater.Ident
+ "module"));
+ OASISSourcePatterns.Templater.Text ".ml"
+ ];
+ origin = "${uncapitalize_file module}.ml"
+ };
+ {
+ OASISSourcePatterns.Templater.atoms =
+ [
+ OASISSourcePatterns.Templater.Text "";
+ OASISSourcePatterns.Templater.Expr
+ (OASISSourcePatterns.Templater.Call
+ ("capitalize_file",
+ OASISSourcePatterns.Templater.Ident
+ "module"));
+ OASISSourcePatterns.Templater.Text ".mll"
+ ];
+ origin = "${capitalize_file module}.mll"
+ };
+ {
+ OASISSourcePatterns.Templater.atoms =
+ [
+ OASISSourcePatterns.Templater.Text "";
+ OASISSourcePatterns.Templater.Expr
+ (OASISSourcePatterns.Templater.Call
+ ("uncapitalize_file",
+ OASISSourcePatterns.Templater.Ident
+ "module"));
+ OASISSourcePatterns.Templater.Text ".mll"
+ ];
+ origin = "${uncapitalize_file module}.mll"
+ };
+ {
+ OASISSourcePatterns.Templater.atoms =
+ [
+ OASISSourcePatterns.Templater.Text "";
+ OASISSourcePatterns.Templater.Expr
+ (OASISSourcePatterns.Templater.Call
+ ("capitalize_file",
+ OASISSourcePatterns.Templater.Ident
+ "module"));
+ OASISSourcePatterns.Templater.Text ".mly"
+ ];
+ origin = "${capitalize_file module}.mly"
+ };
+ {
+ OASISSourcePatterns.Templater.atoms =
+ [
+ OASISSourcePatterns.Templater.Text "";
+ OASISSourcePatterns.Templater.Expr
+ (OASISSourcePatterns.Templater.Call
+ ("uncapitalize_file",
+ OASISSourcePatterns.Templater.Ident
+ "module"));
+ OASISSourcePatterns.Templater.Text ".mly"
+ ];
+ origin = "${uncapitalize_file module}.mly"
+ }
+ ];
+ bs_c_sources = [];
+ bs_data_files = [];
+ bs_findlib_extra_files = [];
+ bs_ccopt = [(OASISExpr.EBool true, [])];
+ bs_cclib = [(OASISExpr.EBool true, [])];
+ bs_dlllib = [(OASISExpr.EBool true, [])];
+ bs_dllpath = [(OASISExpr.EBool true, [])];
+ bs_byteopt = [(OASISExpr.EBool true, [])];
+ bs_nativeopt = [(OASISExpr.EBool true, [])]
+ },
+ {exec_custom = false; exec_main_is = "prngtest.ml"});
Test
({
cs_name = "main";
@@ -7862,10 +8004,10 @@ let setup_t =
{
src_repo_type = Git;
src_repo_location =
- "https://github.com/xavierleroy/cryptokit/releases/tag/release112";
+ "https://github.com/xavierleroy/cryptokit/releases/tag/release113";
src_repo_browser =
Some
- "https://github.com/xavierleroy/cryptokit/releases/tag/release112";
+ "https://github.com/xavierleroy/cryptokit/releases/tag/release113";
src_repo_module = None;
src_repo_branch = None;
src_repo_tag = None;
@@ -7917,7 +8059,8 @@ let setup_t =
};
oasis_fn = Some "_oasis";
oasis_version = "0.4.10";
- oasis_digest = Some "\244\194\194\027\015B\182\240M~r\143n\137\017\166";
+ oasis_digest =
+ Some "\212\1377\231\145\135\191\200=\177\220\134\230\157\203\214";
oasis_exec = None;
oasis_setup_args = [];
setup_update = false
@@ -7925,7 +8068,7 @@ let setup_t =
let setup () = BaseSetup.setup setup_t;;
-# 7929 "setup.ml"
+# 8072 "setup.ml"
let setup_t = BaseCompat.Compat_0_3.adapt_setup_t setup_t
open BaseCompat.Compat_0_3
(* OASIS_STOP *)
diff --git a/src/META b/src/META
index e2070ef..e4dc5ff 100644
--- a/src/META
+++ b/src/META
@@ -1,6 +1,6 @@
# OASIS_START
-# DO NOT EDIT (digest: 8c7dbd8eb6cf5ccf6ede6d77d6b00869)
-version = "1.12"
+# DO NOT EDIT (digest: 7cb4f4b2e0b5e77bb7a0ee261fd36f90)
+version = "1.13"
description = "Cryptographic primitives"
requires = "unix zarith"
archive(byte) = "cryptokit.cma"
diff --git a/src/chacha20.c b/src/chacha20.c
new file mode 100644
index 0000000..de56811
--- /dev/null
+++ b/src/chacha20.c
@@ -0,0 +1,162 @@
+/* Based on D. J. Bernstein's chacha-regs.c version 200801118,
+ https://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/regs/chacha.c
+ The initial code is in the public domain */
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <caml/config.h>
+#include "chacha20.h"
+
+static inline void U32TO8_LITTLE(uint8_t * dst, uint32_t val)
+{
+#ifdef ARCH_BIG_ENDIAN
+ dst[0] = val;
+ dst[1] = val >> 8;
+ dst[2] = val >> 16;
+ dst[3] = val >> 24;
+#else
+ *((uint32_t *) dst) = val;
+#endif
+}
+
+static inline uint32_t U8TO32_LITTLE(const uint8_t * src)
+{
+ return (uint32_t) src[0]
+ + ((uint32_t) src[1] << 8)
+ + ((uint32_t) src[2] << 16)
+ + ((uint32_t) src[3] << 24);
+}
+
+#define ROTATE(v,c) ((v) << (c) | (v) >> (32 - (c)))
+#define XOR(v,w) ((v) ^ (w))
+#define PLUS(v,w) ((v) + (w))
+#define PLUSONE(v) ((v) + 1)
+
+#define QUARTERROUND(a,b,c,d) \
+ a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \
+ c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \
+ a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \
+ c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
+
+static void chacha20_block(chacha20_ctx * ctx)
+{
+ uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
+ int i;
+
+ x0 = ctx->input[0];
+ x1 = ctx->input[1];
+ x2 = ctx->input[2];
+ x3 = ctx->input[3];
+ x4 = ctx->input[4];
+ x5 = ctx->input[5];
+ x6 = ctx->input[6];
+ x7 = ctx->input[7];
+ x8 = ctx->input[8];
+ x9 = ctx->input[9];
+ x10 = ctx->input[10];
+ x11 = ctx->input[11];
+ x12 = ctx->input[12];
+ x13 = ctx->input[13];
+ x14 = ctx->input[14];
+ x15 = ctx->input[15];
+ for (i = 10; i > 0; i --) {
+ QUARTERROUND( x0, x4, x8,x12)
+ QUARTERROUND( x1, x5, x9,x13)
+ QUARTERROUND( x2, x6,x10,x14)
+ QUARTERROUND( x3, x7,x11,x15)
+ QUARTERROUND( x0, x5,x10,x15)
+ QUARTERROUND( x1, x6,x11,x12)
+ QUARTERROUND( x2, x7, x8,x13)
+ QUARTERROUND( x3, x4, x9,x14)
+ }
+ x0 = PLUS(x0,ctx->input[0]);
+ x1 = PLUS(x1,ctx->input[1]);
+ x2 = PLUS(x2,ctx->input[2]);
+ x3 = PLUS(x3,ctx->input[3]);
+ x4 = PLUS(x4,ctx->input[4]);
+ x5 = PLUS(x5,ctx->input[5]);
+ x6 = PLUS(x6,ctx->input[6]);
+ x7 = PLUS(x7,ctx->input[7]);
+ x8 = PLUS(x8,ctx->input[8]);
+ x9 = PLUS(x9,ctx->input[9]);
+ x10 = PLUS(x10,ctx->input[10]);
+ x11 = PLUS(x11,ctx->input[11]);
+ x12 = PLUS(x12,ctx->input[12]);
+ x13 = PLUS(x13,ctx->input[13]);
+ x14 = PLUS(x14,ctx->input[14]);
+ x15 = PLUS(x15,ctx->input[15]);
+ U32TO8_LITTLE(ctx->output + 0,x0);
+ U32TO8_LITTLE(ctx->output + 4,x1);
+ U32TO8_LITTLE(ctx->output + 8,x2);
+ U32TO8_LITTLE(ctx->output + 12,x3);
+ U32TO8_LITTLE(ctx->output + 16,x4);
+ U32TO8_LITTLE(ctx->output + 20,x5);
+ U32TO8_LITTLE(ctx->output + 24,x6);
+ U32TO8_LITTLE(ctx->output + 28,x7);
+ U32TO8_LITTLE(ctx->output + 32,x8);
+ U32TO8_LITTLE(ctx->output + 36,x9);
+ U32TO8_LITTLE(ctx->output + 40,x10);
+ U32TO8_LITTLE(ctx->output + 44,x11);
+ U32TO8_LITTLE(ctx->output + 48,x12);
+ U32TO8_LITTLE(ctx->output + 52,x13);
+ U32TO8_LITTLE(ctx->output + 56,x14);
+ U32TO8_LITTLE(ctx->output + 60,x15);
+ /* Increment the 64-bit counter and, on overflow, the 64-bit nonce */
+ /* (Incrementing the nonce is not standard but a reasonable default.) */
+ if (++ ctx->input[12] == 0)
+ if (++ ctx->input[13] == 0)
+ if (++ ctx->input[14] == 0)
+ ++ ctx->input[15];
+}
+
+void chacha20_transform(chacha20_ctx * ctx,
+ const uint8_t * in, uint8_t * out, size_t len)
+{
+ int n = ctx->next;
+ for (/*nothing*/; len > 0; len--) {
+ if (n >= 64) { chacha20_block(ctx); n = 0; }
+ *out++ = *in++ ^ ctx->output[n++];
+ }
+ ctx->next = n;
+}
+
+void chacha20_extract(chacha20_ctx * ctx,
+ uint8_t * out, size_t len)
+{
+ int n = ctx->next;
+ for (/*nothing*/; len > 0; len--) {
+ if (n >= 64) { chacha20_block(ctx); n = 0; }
+ *out++ = ctx->output[n++];
+ }
+ ctx->next = n;
+}
+
+void chacha20_init(chacha20_ctx * ctx,
+ const uint8_t * key, size_t key_length,
+ const uint8_t iv[8],
+ uint64_t counter)
+{
+ const uint8_t *constants =
+ (uint8_t *) (key_length == 32 ? "expand 32-byte k" : "expand 16-byte k");
+ assert (key_length == 16 || key_length == 32);
+ ctx->input[0] = U8TO32_LITTLE(constants + 0);
+ ctx->input[1] = U8TO32_LITTLE(constants + 4);
+ ctx->input[2] = U8TO32_LITTLE(constants + 8);
+ ctx->input[3] = U8TO32_LITTLE(constants + 12);
+ ctx->input[4] = U8TO32_LITTLE(key + 0);
+ ctx->input[5] = U8TO32_LITTLE(key + 4);
+ ctx->input[6] = U8TO32_LITTLE(key + 8);
+ ctx->input[7] = U8TO32_LITTLE(key + 12);
+ if (key_length == 32) key += 16;
+ ctx->input[8] = U8TO32_LITTLE(key + 0);
+ ctx->input[9] = U8TO32_LITTLE(key + 4);
+ ctx->input[10] = U8TO32_LITTLE(key + 8);
+ ctx->input[11] = U8TO32_LITTLE(key + 12);
+ ctx->input[12] = (uint32_t) counter;
+ ctx->input[13] = (uint32_t) (counter >> 32);
+ ctx->input[14] = U8TO32_LITTLE(iv + 0);
+ ctx->input[15] = U8TO32_LITTLE(iv + 4);
+ ctx->next = 64;
+}
diff --git a/src/chacha20.h b/src/chacha20.h
new file mode 100644
index 0000000..26ba1fd
--- /dev/null
+++ b/src/chacha20.h
@@ -0,0 +1,23 @@
+/* Based on D. J. Bernstein's chacha-regs.c version 200801118,
+ https://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/regs/chacha.c
+ The initial code is in the public domain */
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+ uint32_t input[16]; /* The current state */
+ uint8_t output[64]; /* Output data for the current state */
+ int next; /* Index of next unused byte in output */
+} chacha20_ctx;
+
+void chacha20_init(chacha20_ctx * ctx,
+ const uint8_t * key, size_t key_length,
+ const uint8_t iv[8],
+ uint64_t ctr);
+
+void chacha20_extract(chacha20_ctx * ctx,
+ uint8_t * out, size_t len);
+
+void chacha20_transform(chacha20_ctx * ctx,
+ const uint8_t * in, uint8_t * out, size_t len);
diff --git a/src/cryptokit.ml b/src/cryptokit.ml
index 432343e..597139a 100644
--- a/src/cryptokit.ml
+++ b/src/cryptokit.ml
@@ -16,6 +16,15 @@
let wipe_bytes s = Bytes.fill s 0 (Bytes.length s) '\000'
let wipe_string s = wipe_bytes (Bytes.unsafe_of_string s)
+let shl1_bytes src soff dst doff len =
+ let rec shl1 carry i =
+ if i >= 0 then begin
+ let n = Char.code (Bytes.get src (soff + i)) in
+ Bytes.set dst (doff + i) (Char.unsafe_chr ((n lsl 1) lor carry));
+ shl1 (n lsr 7) (i - 1)
+ end
+ in shl1 0 (len - 1)
+
type error =
| Wrong_key_size
| Wrong_IV_size
@@ -53,6 +62,9 @@ external des_cook_key : string -> int -> dir -> bytes = "caml_des_cook_key"
external des_transform : bytes -> bytes -> int -> bytes -> int -> unit = "caml_des_transform"
external arcfour_cook_key : string -> bytes = "caml_arcfour_cook_key"
external arcfour_transform : bytes -> bytes -> int -> bytes -> int -> int -> unit = "caml_arcfour_transform_bytecode" "caml_arcfour_transform"
+external chacha20_cook_key : string -> bytes -> int64 -> bytes = "caml_chacha20_cook_key"
+external chacha20_transform : bytes -> bytes -> int -> bytes -> int -> int -> unit = "caml_chacha20_transform_bytecode" "caml_chacha20_transform"
+external chacha20_extract : bytes -> bytes -> int -> int -> unit = "caml_chacha20_extract"
external sha1_init: unit -> bytes = "caml_sha1_init"
external sha1_update: bytes -> bytes -> int -> int -> unit = "caml_sha1_update"
@@ -697,7 +709,7 @@ class cipher_padded_decrypt (padding : Padding.scheme)
oend <- oend + valid
end
-(* Wrapping of a block cipher as a MAC *)
+(* Wrapping of a block cipher as a MAC, using CBC mode *)
class mac ?iv:iv_init ?(pad: Padding.scheme option) (cipher : block_cipher) =
let blocksize = cipher#blocksize in
@@ -784,6 +796,27 @@ class mac_final_triple ?iv ?pad (cipher1 : block_cipher)
super#wipe; cipher2#wipe; cipher3#wipe
end
+(* Wrapping of a block ciper as a MAC, in CMAC mode (a.k.a. OMAC1) *)
+
+class cmac ?iv:iv_init (cipher : block_cipher) k1 k2 =
+ object (self)
+ inherit mac ?iv:iv_init cipher as super
+
+ method result =
+ let blocksize = cipher#blocksize in
+ let k' =
+ if used = blocksize then k1 else (Padding._8000#pad buffer used; k2) in
+ xor_bytes iv 0 buffer 0 blocksize;
+ xor_bytes k' 0 buffer 0 blocksize;
+ cipher#transform buffer 0 iv 0;
+ used <- 0; (* really useful? *)
+ Bytes.to_string iv
+
+ method wipe =
+ super#wipe;
+ wipe_bytes k1;
+ wipe_bytes k2
+ end
end
(* Stream ciphers *)
@@ -812,6 +845,23 @@ class arcfour key =
wipe_bytes ckey
end
+class chacha20 ?iv ?(ctr = 0L) key =
+ object
+ val ckey =
+ let iv = Block.make_initial_iv 8 iv in
+ if String.length key = 16 || String.length key = 32
+ then chacha20_cook_key key iv ctr
+ else raise(Error Wrong_key_size)
+ method transform src src_ofs dst dst_ofs len =
+ if len < 0
+ || src_ofs < 0 || src_ofs > Bytes.length src - len
+ || dst_ofs < 0 || dst_ofs > Bytes.length dst - len
+ then invalid_arg "chacha20#transform";
+ chacha20_transform ckey src src_ofs dst dst_ofs len
+ method wipe =
+ wipe_bytes ckey
+ end
+
(* Wrapping of a stream cipher as a cipher *)
class cipher (cipher : stream_cipher) =
@@ -1105,6 +1155,9 @@ let triple_des ?mode ?pad ?iv key dir =
let arcfour key dir = new Stream.cipher (new Stream.arcfour key)
+let chacha20 ?iv ?ctr key dir =
+ new Stream.cipher (new Stream.chacha20 key ?iv ?ctr)
+
end
(* The hmac construction *)
@@ -1177,6 +1230,20 @@ let des_final_triple_des ?iv ?pad key =
wipe_string k1; wipe_string k2; wipe_string k3;
new Block.mac_final_triple ?iv ?pad c1 c2 c3
+let aes_cmac ?iv key =
+ let cipher = new Block.aes_encrypt key in
+ let b = Bytes.make 16 '\000' in
+ let l = Bytes.create 16 in
+ cipher#transform b 0 l 0; (* l = AES-128(K, 000...000 *)
+ Bytes.set b 15 '\x87'; (* b = the Rb constant *)
+ let k1 = Bytes.create 16 in
+ shl1_bytes l 0 k1 0 16;
+ if Char.code (Bytes.get l 0) land 0x80 > 0 then xor_bytes b 0 k1 0 16;
+ let k2 = Bytes.create 16 in
+ shl1_bytes k1 0 k2 0 16;
+ if Char.code (Bytes.get k1 0) land 0x80 > 0 then xor_bytes b 0 k2 0 16;
+ wipe_bytes l;
+ new Block.cmac ?iv cipher k1 k2
end
(* Random number generation *)
@@ -1315,28 +1382,38 @@ let secure_rng =
class pseudo_rng seed =
let _ = if String.length seed < 16 then raise (Error Seed_too_short) in
object (self)
- val cipher =
- new Block.cbc_encrypt (new Block.aes_encrypt (String.sub seed 0 16))
- val state =
- let s = Bytes.make 71 '\001' in
- String.blit seed 0 s 0 (min 55 (String.length seed));
- s
+ val ckey =
+ let l = String.length seed in
+ chacha20_cook_key
+ (if l >= 32 then String.sub seed 0 32
+ else if l > 16 then seed ^ String.make (32 - l) '\000'
+ else seed)
+ (Bytes.make 8 '\000') 0L
+ method random_bytes buf ofs len =
+ if len < 0 || ofs < 0 || ofs > Bytes.length buf - len
+ then invalid_arg "pseudo_rng#random_bytes"
+ else chacha20_extract ckey buf ofs len
+ method wipe =
+ wipe_bytes ckey; wipe_string seed
+end
+
+let pseudo_rng seed = new pseudo_rng seed
+
+class pseudo_rng_aes_ctr seed =
+ let _ = if String.length seed < 16 then raise (Error Seed_too_short) in
+ object (self)
+ val cipher = new Block.aes_encrypt (String.sub seed 0 16)
+ val ctr = Bytes.make 16 '\000'
val obuf = Bytes.create 16
val mutable opos = 16
method random_bytes buf ofs len =
if len > 0 then begin
if opos >= 16 then begin
- (* Clock the lagged Fibonacci generator 16 times *)
- for i = 55 to 70 do
- Bytes.set state i
- (Char.unsafe_chr(Char.code (Bytes.get state (i-55)) +
- Char.code (Bytes.get state (i-24))))
- done;
- (* Encrypt resulting 16 bytes *)
- cipher#transform state 55 obuf 0;
- (* Shift Fibonacci generator by 16 bytes *)
- Bytes.blit state 16 state 0 55;
+ (* Encrypt the counter *)
+ cipher#transform ctr 0 obuf 0;
+ (* Increment the counter *)
+ Block.increment_counter ctr 0 15;
(* We have 16 fresh bytes of pseudo-random data *)
opos <- 0
end;
@@ -1350,7 +1427,7 @@ class pseudo_rng seed =
wipe_bytes obuf; wipe_string seed
end
- let pseudo_rng seed = new pseudo_rng seed
+let pseudo_rng_aes_ctr seed = new pseudo_rng_aes_ctr seed
end
diff --git a/src/cryptokit.mli b/src/cryptokit.mli
index 471a9f8..49d42de 100644
--- a/src/cryptokit.mli
+++ b/src/cryptokit.mli
@@ -30,7 +30,7 @@
[ocamlopt unix.cmxa nums.cmxa cryptokit.cmxa].
*)
-(** {6 General-purpose abstract interfaces} *)
+(** {1 General-purpose abstract interfaces} *)
(** A <I>transform</I> is an arbitrary mapping from sequences of characters
to sequences of characters. Examples of transforms include
@@ -207,7 +207,7 @@ val hash_channel: hash -> ?len:int -> in_channel -> string
The hash [h] is wiped before returning, hence can
no longer be used for further hash computations. *)
-(** {6 Utilities: random numbers and padding schemes} *)
+(** {1 Utilities: random numbers and padding schemes} *)
(** The [Random] module provides random and pseudo-random number generators
suitable for generating cryptographic keys, nonces, or challenges. *)
@@ -274,19 +274,30 @@ module Random : sig
(** [pseudo_rng seed] returns a pseudo-random number generator
seeded by the string [seed]. [seed] must contain at least
16 characters, and can be arbitrarily longer than this,
- except that only the first 55 characters are used.
- Technically, the first 16 characters of [seed] are used as
- a key for the AES cipher in CBC mode, which encrypts the output
- of a lagged Fibonacci generator [X(i) = (X(i-24) + X(i-55)) mod 256]
- seeded with the first 55 characters of [seed].
- While this generator is believed to have good statistical properties,
- it still does not generate ``true'' randomness: the entropy of
- the strings it creates cannot exceed the entropy contained in
- the seed. As a typical use,
+ except that only the first 32 characters are used.
+ The seed is used as a key for the Chacha20 stream cipher.
+ The generated pseudo-random data is the result of encrypting
+ the all-zero input with Chacha20.
+ While this generator is believed to have very good statistical
+ properties, it still does not generate ``true'' randomness:
+ the entropy of the byte strings it produces cannot exceed the
+ entropy contained in the seed. As a typical use,
[Random.pseudo_rng (Random.string Random.secure_rng 20)] returns a
generator that can generate arbitrarily long strings of pseudo-random
data without delays, and with a total entropy of approximately
160 bits. *)
+
+ val pseudo_rng_aes_ctr: string -> rng
+ (** This is another pseudo-random number generator, based on the AES
+ block cipher in counter mode. It is slightly slower than [pseudo_rng]
+ while having similar randomness characteristics.
+ The only reason to use it instead of [pseudo_rng] is that AES
+ has been cryptanalyzed even more than Chacha20.
+ The [seed] argument must contain at least 16 characters. Only the
+ first 16 characters are used, as an AES key. The generated
+ pseudo-random data is the result of encrypting the 128-bit integers
+ [0, 1, 2, ...] with this key. *)
+
end
(** The [Padding] module defines a generic interface
@@ -327,7 +338,7 @@ module Padding : sig
by as many [0] bytes as needed to fill the block. *)
end
-(** {6 Cryptographic primitives (simplified interface)} *)
+(** {1 Cryptographic primitives (simplified interface)} *)
(** The [Cipher] module implements the AES, DES, Triple-DES, ARCfour
and Blowfish symmetric ciphers. Symmetric ciphers are presented
@@ -372,6 +383,8 @@ module Cipher : sig
[n] must be between [1] and [blocksize] included.
[CTR] is equivalent to [CTR_N blocksize]. *)
+(** {2 Recommended ciphers} *)
+
val aes: ?mode:chaining_mode -> ?pad:Padding.scheme -> ?iv:string ->
string -> direction -> transform
(** AES is the Advanced Encryption Standard, also known as Rijndael.
@@ -399,6 +412,34 @@ module Cipher : sig
The [aes] function returns a transform that performs encryption
or decryption, depending on the direction argument. *)
+ val chacha20: ?iv:string -> ?ctr:int64 -> string -> direction -> transform
+ (** Chacha20 is a stream cipher proposed by D. J. Bernstein in 2008.
+
+ The Chacha20 cipher is a stream cipher, not a block cipher.
+ Hence, its natural block size is 1, and no padding is
+ required. Chaining modes do not apply. A feature of stream
+ ciphers is that the xor of two ciphertexts obtained with the
+ same key is the xor of the corresponding plaintexts, which
+ allows various attacks. Hence, the same key must never be
+ reused.
+
+ The string argument is the key; its length must be either 16
+ or (better) 32.
+
+ The optional [iv] argument is the initialization vector (also
+ called nonce) that can be used to diversify the key. If present,
+ it must be 8 characters long. If absent, it is taken to be
+ eight zero bytes.
+
+ The optional [ctr] argument is the initial value of the internal
+ counter. If absent, it defaults to 0.
+
+ The direction argument is present for consistency with the
+ other ciphers only, and is actually ignored: for all stream
+ ciphers, decryption is the same function as encryption. *)
+
+(** {2 Weaker, older ciphers, not recommended for new applications} *)
+
val des: ?mode:chaining_mode -> ?pad:Padding.scheme -> ?iv:string ->
string -> direction -> transform
(** DES is the Data Encryption Standard. Very popular in the past,
@@ -421,7 +462,10 @@ module Cipher : sig
This results in a 112-bit or 168-bit key length that resists
brute-force attacks. However, the three encryptions required
on each block make this cipher quite slow (4 times slower than
- AES). The arguments to the [triple_des] function have the
+ AES). Moreover, the small block size (64 bits) opens the way
+ to collision-based attacks. Triple DES should therefore be
+ considered as relatively weak encryption.
+ The arguments to the [triple_des] function have the
same meaning as for the {!Cryptokit.Cipher.aes} function. The
key argument is a string of length 16 or 24, representing the
concatenation of the key parts [k1], [k2], and optionally
@@ -436,7 +480,10 @@ module Cipher : sig
not to use ARCfour in a commercial product.
ARCfour is popular for its speed: approximately 2 times faster
- than AES. It accepts any key length up to 2048 bits.
+ than AES. It accepts any key length up to 2048 bits. However,
+ the security of ARCfour is being questioned owing to several
+ statistical biases in its output. It should not be used for
+ new applications.
The ARCfour cipher is a stream cipher, not a block cipher.
Hence, its natural block size is 1, and no padding is
@@ -457,8 +504,14 @@ module Cipher : sig
(** Blowfish is a fast block cipher proposed by B.Schneier in 1994.
It processes data by blocks of 64 bits (8 bytes),
and supports keys of 32 to 448 bits.
+
+ The small block size (64 bits) of Blowfish opens the way to
+ some collision-based attacks. Depending on the application,
+ ciphers with larger block size should be preferred.
+
The string argument is the key; its length must be between
4 and 56.
+
The direction argument specifies whether encryption or decryption
is to be performed.
@@ -491,16 +544,9 @@ end
hash of a text can be used as a compact replacement for this text
for the purposes of ensuring integrity of the text. *)
module Hash : sig
- val sha1: unit -> hash
- (** SHA-1 is the Secure Hash Algorithm revision 1. It is a NIST
- standard, is widely used, and produces 160-bit hashes (20 bytes).
- While popular in many legacy applications, it is now known
- to be insecure. In particular, it is not collision-resistant. *)
- val sha2: int -> hash
- (** SHA-2, another NIST standard for cryptographic hashing, produces
- hashes of 224, 256, 384, or 512 bits (24, 32, 48 or 64 bytes).
- The parameter is the desired size of the hash, in
- bits. It must be one of 224, 256, 384 or 512. *)
+
+(** {2 Recommended hashes} *)
+
val sha3: int -> hash
(** SHA-3, the latest NIST standard for cryptographic hashing,
produces hashes of 224, 256, 384 or 512 bits (24, 32, 48 or 64
@@ -510,6 +556,11 @@ module Hash : sig
(** The Keccak submission for the SHA-3 is very similar to [sha3] but
uses a slightly different padding. The parameter is the same as
that of [sha3]. *)
+ val sha2: int -> hash
+ (** SHA-2, another NIST standard for cryptographic hashing, produces
+ hashes of 224, 256, 384, or 512 bits (24, 32, 48 or 64 bytes).
+ The parameter is the desired size of the hash, in
+ bits. It must be one of 224, 256, 384 or 512. *)
val sha224: unit -> hash
(** SHA-224 is SHA-2 specialized to 224 bit hashes (24 bytes). *)
val sha256: unit -> hash
@@ -519,7 +570,15 @@ module Hash : sig
val sha512: unit -> hash
(** SHA-512 is SHA-2 specialized to 512 bit hashes (64 bytes). *)
val ripemd160: unit -> hash
- (** RIPEMD-160 produces 160-bit hashes (20 bytes). *)
+ (** RIPEMD-160 produces 160-bit hashes (20 bytes). *)
+
+(** {2 Weak hashes, not recommended for new applications} *)
+
+ val sha1: unit -> hash
+ (** SHA-1 is the Secure Hash Algorithm revision 1. It is a NIST
+ standard, is widely used, and produces 160-bit hashes (20 bytes).
+ While popular in many legacy applications, it is now known
+ to be insecure. In particular, it is not collision-resistant. *)
val md5: unit -> hash
(** MD5 is an older hash function, producing 128-bit hashes (16 bytes).
While popular in many legacy applications, it is now known
@@ -538,7 +597,7 @@ end
the text was authentified by someone who possesses the secret key.
The module [MAC] provides five MAC functions based on the hashes
- SHA-1, SHA256, SHA512, RIPEMD160 and MD5, and four MAC functions based on
+ SHA-1, SHA256, SHA512, RIPEMD160 and MD5, and five MAC functions based on
the block ciphers AES, DES, and Triple-DES. *)
module MAC: sig
val hmac_sha1: string -> hash
@@ -569,13 +628,25 @@ module MAC: sig
applied to MD5. The returned hash values are 128 bits (16 bytes)
long. The [key] argument is the MAC key; it can have any length,
but a minimal length of 16 bytes is recommended. *)
+ val aes_cmac: ?iv:string -> string -> hash
+ (** [aes_cmac key] returns a MAC based on AES encryption in CMAC mode,
+ also known as OMAC1 mode. The input data is encrypted using
+ AES in CBC mode, with a special treatment of the final block
+ that makes this MAC suitable for input data of variable length.
+ The final value of the initialization vector is the MAC value.
+ Thus, the returned hash values are 128 bit (16 bytes) long.
+ The [key] argument is the MAC key; it must have length 16, 24,
+ or 32. The optional [iv] argument is the first value of the
+ initialization vector, and defaults to 0. *)
val aes: ?iv:string -> ?pad:Padding.scheme -> string -> hash
(** [aes key] returns a MAC based on AES encryption in CBC mode.
- The ciphertext is discarded, and the final value of the
- initialization vector is the MAC value. Thus, the returned
- hash values are 128 bit (16 bytes) long. The [key] argument
- is the MAC key; it must have length 16, 24, or 32. The
- optional [iv] argument is the first value of the
+ Unlike [aes_cmac], there is no special treatment for the final
+ block, except padding it as per the optional [pad] argument.
+ This makes this MAC weak when used with input data of variable
+ length. (It is fine for data of fixed length, though.)
+ The returned hash values are 128 bit (16 bytes) long. The
+ [key] argument is the MAC key; it must have length 16, 24, or
+ 32. The optional [iv] argument is the first value of the
initialization vector, and defaults to 0. The optional [pad]
argument specifies a padding scheme to pad input to an
integral number of 16-byte blocks. *)
@@ -584,7 +655,7 @@ module MAC: sig
The construction is identical to that used for the [aes] MAC.
The key size is 64 bits (8 bytes), of which only 56 are used.
The returned hash value has length 8 bytes.
- Due to the small hash size and key size, this MAC is rather weak. *)
+ Due to the small hash size and key size, this MAC is weak. *)
val triple_des: ?iv:string -> ?pad:Padding.scheme -> string -> hash
(** [des key] returns a MAC based on triple DES encryption in CBC mode.
The construction is identical to that used for the [aes] MAC.
@@ -773,7 +844,7 @@ module DH: sig
counter until [numbytes] bytes have been obtained. *)
end
-(** {6 Advanced, compositional interface to block ciphers
+(** {1 Advanced, compositional interface to block ciphers
and stream ciphers} *)
(** The [Block] module provides classes that implements
@@ -799,7 +870,7 @@ module Block : sig
end
(** Abstract interface for a block cipher. *)
- (** {6 Deriving transforms and hashes from block ciphers} *)
+ (** {1 Deriving transforms and hashes from block ciphers} *)
class cipher: block_cipher -> transform
(** Wraps a block cipher as a general transform. The transform
@@ -839,7 +910,7 @@ module Block : sig
because of the additional final encryption through [c2] and
[c3]. *)
- (** {6 Some block ciphers: AES, DES, triple DES, Blowfish} *)
+ (** {1 Some block ciphers: AES, DES, triple DES, Blowfish} *)
class aes_encrypt: string -> block_cipher
(** The AES block cipher, in encryption mode. The string argument
@@ -865,7 +936,7 @@ module Block : sig
class blowfish_decrypt: string -> block_cipher
(** The Blowfish block cipher, in decryption mode. *)
- (** {6 Chaining modes} *)
+ (** {1 Chaining modes} *)
class cbc_encrypt: ?iv: string -> block_cipher -> block_cipher
(** Add Cipher Block Chaining (CBC) to the given block cipher
@@ -942,9 +1013,21 @@ module Stream : sig
This stream cipher works by xor-ing the input with the
output of a key-dependent pseudo random number generator.
Thus, decryption is the same function as encryption. *)
+
+ class chacha20: ?iv:string -> ?ctr:int64 -> string -> stream_cipher
+ (** The Chacha20 strea cipher.
+ The string argument is the key, and must be of length 16 or 32.
+ The optional [iv] argument is the initialization vector
+ (also known as the nonce). If present, it must be 8 bytes long.
+ If absent, it is taken to be eight zero bytes.
+ The optional [ctr] argument is the initial value of the internal
+ counter. If absent, it is taken to be 0.
+ This stream cipher works by xor-ing the input with the
+ output of a key-dependent pseudo random number generator.
+ Thus, decryption is the same function as encryption. *)
end
-(** {6 Encoding and compression of data} *)
+(** {1 Encoding and compression of data} *)
(** The [Base64] module supports the encoding and decoding of
binary data in base 64 format, using only alphanumeric
@@ -1005,7 +1088,7 @@ module Zlib: sig
(** Return a transform that decompresses its input. *)
end
-(** {6 Error reporting} *)
+(** {1 Error reporting} *)
(** Error codes for this library. *)
type error =
@@ -1050,7 +1133,7 @@ exception Error of error
(** Exception raised by functions in this library
to report error conditions. *)
-(** {6 Miscellaneous utilities} *)
+(** {1 Miscellaneous utilities} *)
val wipe_bytes : bytes -> unit
(** [wipe_bytes s] overwrites [s] with zeroes. Can be used
diff --git a/src/libcryptokit_stubs.clib b/src/libcryptokit_stubs.clib
index 6fd5022..94cc08d 100644
--- a/src/libcryptokit_stubs.clib
+++ b/src/libcryptokit_stubs.clib
@@ -1,5 +1,5 @@
# OASIS_START
-# DO NOT EDIT (digest: 8925748b522861580cf6f17b03f49c4b)
+# DO NOT EDIT (digest: c7ac7a160eaa5e93a581a4efbe9317de)
aesni.o
arcfour.o
stubs-arcfour.o
@@ -23,4 +23,6 @@ stubs-rng.o
stubs-zlib.o
keccak.o
stubs-sha3.o
+chacha20.o
+stubs-chacha20.o
# OASIS_STOP
diff --git a/src/stubs-chacha20.c b/src/stubs-chacha20.c
new file mode 100644
index 0000000..2d062aa
--- /dev/null
+++ b/src/stubs-chacha20.c
@@ -0,0 +1,58 @@
+/***********************************************************************/
+/* */
+/* The Cryptokit library */
+/* */
+/* Xavier Leroy, projet Cristal, INRIA Rocquencourt */
+/* */
+/* Copyright 2002 Institut National de Recherche en Informatique et */
+/* en Automatique. All rights reserved. This file is distributed */
+/* under the terms of the GNU Library General Public License, with */
+/* the special exception on linking described in file LICENSE. */
+/* */
+/***********************************************************************/
+
+/* Stub code for Chacha20 */
+
+#include "chacha20.h"
+#include <caml/mlvalues.h>
+#include <caml/alloc.h>
+#include <caml/memory.h>
+
+#define Cooked_key_size (sizeof(chacha20_ctx))
+#define Key_val(v) ((chacha20_ctx *) String_val(v))
+
+CAMLprim value caml_chacha20_cook_key(value key, value iv, value counter)
+{
+ CAMLparam2(key, iv);
+ value ckey = alloc_string(Cooked_key_size);
+ chacha20_init(Key_val(ckey),
+ (unsigned char *) String_val(key), caml_string_length(key),
+ (unsigned char *) String_val(iv), Int64_val(counter));
+ CAMLreturn(ckey);
+}
+
+CAMLprim value caml_chacha20_transform(value ckey, value src, value src_ofs,
+ value dst, value dst_ofs, value len)
+{
+ chacha20_transform(Key_val(ckey),
+ &Byte_u(src, Long_val(src_ofs)),
+ &Byte_u(dst, Long_val(dst_ofs)),
+ Long_val(len));
+ return Val_unit;
+}
+
+CAMLprim value caml_chacha20_transform_bytecode(value * argv, int argc)
+{
+ return caml_chacha20_transform(argv[0], argv[1], argv[2],
+ argv[3], argv[4], argv[5]);
+}
+
+CAMLprim value caml_chacha20_extract(value ckey,
+ value dst, value dst_ofs, value len)
+{
+ chacha20_extract(Key_val(ckey),
+ &Byte_u(dst, Long_val(dst_ofs)),
+ Long_val(len));
+ return Val_unit;
+}
+
diff --git a/test/prngtest.ml b/test/prngtest.ml
new file mode 100644
index 0000000..b6fb6a3
--- /dev/null
+++ b/test/prngtest.ml
@@ -0,0 +1,49 @@
+(***********************************************************************)
+(* *)
+(* The Cryptokit library *)
+(* *)
+(* Xavier Leroy, projet Gallium, INRIA Paris *)
+(* *)
+(* Copyright 2017 Institut National de Recherche en Informatique et *)
+(* en Automatique. All rights reserved. This file is distributed *)
+(* under the terms of the GNU Library General Public License, with *)
+(* the special exception on linking described in file LICENSE. *)
+(* *)
+(***********************************************************************)
+
+(* Generate pseudorandom data on stdout, for testing with "dieharder" *)
+
+open Cryptokit
+
+let output_pr_data rng =
+ let b = Bytes.create 64 in
+ while true do
+ rng#random_bytes b 0 64;
+ output stdout b 0 64
+ done
+
+let usage() =
+ prerr_string {|Usage:
+ ./prngtest.native aes-ctr | dieharder -a -g 200
+ ./prngtest.native chacha20 | dieharder -a -g 200
+ ./prngtest.native hardware | dieharder -a -g 200
+Warning: each dieharder run takes a long time.
+|};
+ exit 2
+
+let _ =
+ let seed =
+ if Array.length Sys.argv > 2
+ then Sys.argv.(2)
+ else "Supercalifragilistusexpialidolcius" in
+ let rng =
+ if Array.length Sys.argv > 1 then begin
+ match Sys.argv.(1) with
+ | "aes-ctr" -> Random.pseudo_rng_aes_ctr seed
+ | "chacha20" -> Random.pseudo_rng seed
+ | "hardware" -> Random.hardware_rng ()
+ | _ -> usage()
+ end else usage() in
+ output_pr_data rng
+
+
diff --git a/test/speedtest.ml b/test/speedtest.ml
index 64555a4..0552d90 100644
--- a/test/speedtest.ml
+++ b/test/speedtest.ml
@@ -74,6 +74,10 @@ let _ =
(raw_stream_cipher (new Stream.arcfour "0123456789ABCDEF") 4000000 16);
time_fn "Raw ARCfour, 64_000_000 bytes, 64-byte chunks"
(raw_stream_cipher (new Stream.arcfour "0123456789ABCDEF") 1000000 64);
+ time_fn "Raw Chacha20, 64_000_000 bytes, 16-byte chunks"
+ (raw_stream_cipher (new Stream.arcfour "0123456789ABCDEF") 4000000 16);
+ time_fn "Raw Chacha20, 64_000_000 bytes, 64-byte chunks"
+ (raw_stream_cipher (new Stream.arcfour "0123456789ABCDEF") 1000000 64);
time_fn "Raw Blowfish 128, 64_000_000 bytes"
(raw_block_cipher (new Block.blowfish_encrypt "0123456789ABCDEF") 8000000);
time_fn "Wrapped AES 128 CBC, 64_000_000 bytes"
@@ -88,6 +92,8 @@ let _ =
(transform (Cipher.triple_des "0123456789ABCDEF" Cipher.Encrypt) 1000000 16);
time_fn "Wrapped ARCfour, 64_000_000 bytes"
(transform (Cipher.arcfour "0123456789ABCDEF" Cipher.Encrypt) 4000000 16);
+ time_fn "Wrapped Chacha20, 64_000_000 bytes"
+ (transform (Cipher.chacha20 "0123456789ABCDEF" Cipher.Encrypt) 4000000 16);
time_fn "Wrapped Blowfish 128 CBC, 64_000_000 bytes"
(transform (Cipher.blowfish "0123456789ABCDEF" Cipher.Encrypt) 4000000 16);
time_fn "SHA-1, 64_000_000 bytes, 16-byte chunks"
@@ -106,8 +112,10 @@ let _ =
(hash (Hash.sha256()) 4000000 16);
time_fn "MD5, 64_000_000 bytes, 16-byte chunks"
(hash (Hash.md5()) 4000000 16);
- time_fn "AES MAC, 64_000_000 bytes, 16-byte chunks"
- (hash (MAC.aes "0123456789ABCDEF") 4000000 16);
+ time_fn "AES CMAC, 64_000_000 bytes, 16-byte chunks"
+ (hash (MAC.aes_cmac "0123456789ABCDEF") 4000000 16);
+ time_fn "HMAC-SHA1, 64_000_000 bytes, 16-byte chunks"
+ (hash (MAC.hmac_sha1 "0123456789ABCDEF") 4000000 16);
let prng = Random.pseudo_rng "supercalifragilistusexpialidolcius" in
let key =
time_fn "RSA key generation (2048 bits) x 10"
@@ -122,6 +130,8 @@ let _ =
(repeat 100 (fun () -> ignore(RSA.decrypt_CRT key ciphertext)));
time_fn "PRNG, 64_000_000 bytes"
(rng prng 1000000 64);
+ time_fn "PRNG AES CTR, 64_000_000 bytes"
+ (rng (Random.pseudo_rng_aes_ctr "supercalifragilistusexpialidolcius") 1000000 64);
begin try
let hr = Random.hardware_rng () in
time_fn "Hardware RNG, 64_000_000 bytes"
diff --git a/test/test.ml b/test/test.ml
index e4c6a5a..8e3eba6 100644
--- a/test/test.ml
+++ b/test/test.ml
@@ -205,6 +205,41 @@ let _ =
c2#put_string (String.make 1024 'x');
test 9 c2#available_output 1024
+(* Chacha20 *)
+
+let _ =
+ testing_function "Chacha20";
+ let do_test n1 n2 key nonce plain cipher counter =
+ let key = hex key
+ and nonce = hex nonce
+ and plain = hexbytes plain
+ and cipher = hexbytes cipher in
+ let c = new Stream.chacha20 ~iv:nonce ~ctr:counter key in
+ let d = new Stream.chacha20 ~iv:nonce ~ctr:counter key in
+ let res = Bytes.create (Bytes.length plain) in
+ c#transform plain 0 res 0 (Bytes.length plain);
+ test n1 res cipher;
+ d#transform cipher 0 res 0 (Bytes.length cipher);
+ test n2 res plain in
+ do_test 1 2
+ "0000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"
+ 0L;
+ do_test 3 4
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ "0000000000000002"
+ "416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e7472696275746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e20224945544620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c2073746174656d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c656374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207768696368206172652061646472657373656420746f"
+ "a3fbf07df3fa2fde4f376ca23e82737041605d9f4f4f57bd8cff2c1d4b7955ec2a97948bd3722915c8f3d337f7d370050e9e96d647b7c39f56e031ca5eb6250d4042e02785ececfa4b4bb5e8ead0440e20b6e8db09d881a7c6132f420e52795042bdfa7773d8a9051447b3291ce1411c680465552aa6c405b7764d5e87bea85ad00f8449ed8f72d0d662ab052691ca66424bc86d2df80ea41f43abf937d3259dc4b2d0dfb48a6c9139ddd7f76966e928e635553ba76c5c879d7b35d49eb2e62b0871cdac638939e25e8a1e0ef9d5280fa8ca328b351c3c765989cbcf3daa8b6ccc3aaf9f3979c92b3720fc88dc95ed84a1be059c6499b9fda236e7e818b04b0bc39c1e876b193bfe5569753f88128cc08aaa9b63d1a16f80ef2554d7189c411f5869ca52c5b83fa36ff216b9c1d30062bebcfd2dc5bce0911934fda79a86f6e698ced759c3ff9b6477338f3da4f9cd8514ea9982ccafb341b2384dd902f3d1ab7ac61dd29c6f21ba5b862f3730e37cfdc4fd806c22f221"
+ 1L;
+ do_test 5 6
+ "1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0"
+ "0000000000000002"
+ "2754776173206272696c6c69672c20616e642074686520736c6974687920746f7665730a446964206779726520616e642067696d626c6520696e2074686520776162653a0a416c6c206d696d737920776572652074686520626f726f676f7665732c0a416e6420746865206d6f6d65207261746873206f757467726162652e"
+ "62e6347f95ed87a45ffae7426f27a1df5fb69110044c0d73118effa95b01e5cf166d3df2d721caf9b21e5fb14c616871fd84c54f9d65b283196c7fe4f60553ebf39c6402c42234e32a356b3e764312a61a5532055716ead6962568f87d3f3f7704c6a8d1bcd1bf4d50d6154b6da731b187b58dfd728afa36757a797ac188d1"
+ 42L
+
(* Blowfish *)
let _ =
@@ -659,6 +694,32 @@ let _ =
(String.make 50 '\221'))
(hex "56be34521d144c88dbb8c733f0e8b3f6")
+(* AES-CMAC (from RFC4493) *)
+
+let _ =
+ testing_function "AES-CMAC";
+ let key = hex "2b7e1516 28aed2a6 abf71588 09cf4f3c" in
+ let msg = hex "6bc1bee2 2e409f96 e93d7e11 7393172a \
+ ae2d8a57 1e03ac9c 9eb76fac 45af8e51 \
+ 30c81c46 a35ce411 e5fbc119 1a0a52ef \
+ f69f2445 df4f9b17 ad2b417b e66c3710" in
+ test 1
+ (hash_string (MAC.aes_cmac key)
+ "")
+ (hex "bb1d6929 e9593728 7fa37d12 9b756746");
+ test 2
+ (hash_string (MAC.aes_cmac key)
+ (String.sub msg 0 16))
+ (hex "070a16b4 6b4d4144 f79bdd9d d04a287c");
+ test 3
+ (hash_string (MAC.aes_cmac key)
+ (String.sub msg 0 40))
+ (hex "dfa66747 de9ae630 30ca3261 1497c827");
+ test 4
+ (hash_string (MAC.aes_cmac key)
+ msg)
+ (hex "51f0bebf 7e3b9d92 fc497417 79363cfe")
+
(* RSA *)
let some_rsa_key = {
@@ -831,7 +892,7 @@ The quick brown fox jumps over the lazy dog.
(* Random numbers *)
(* This is not a serious statistical test of Cryptokit's RNGs
- (use Diehard or TestU01 for this). Rather, it's a simplistic
+ (use Dieharder or TestU01 for this). Rather, it's a simplistic
test intended to detect obvious bugs such as providing
fewer random bytes than requested. *)
@@ -862,25 +923,28 @@ let _ =
testing_function "Random number generation";
printf " 1. PRNG: ";
test_rng (Random.pseudo_rng "abcdefghijklmnopqrstuvwxyz");
- printf " 2. /dev/urandom: ";
+ printf " 2. PRNG based on AES CTR: ";
+ test_rng (Random.pseudo_rng_aes_ctr "abcdefghijklmnopqrstuvwxyz");
+ printf " 3. /dev/urandom: ";
begin try
test_rng (Random.device_rng "/dev/urandom")
with Unix.Unix_error _ ->
printf "not available\n"
end;
- printf " 3. Hardware RNG: ";
+ printf " 4. Hardware RNG: ";
begin try
test_rng (Random.hardware_rng ())
with Error No_entropy_source ->
printf "not available\n"
end;
- printf " 4. System RNG: ";
+ printf " 5. System RNG: ";
begin try
test_rng (Random.system_rng ())
with Error No_entropy_source ->
printf "not available\n"
end
+
(* End of tests *)
let _ =