diff options
Diffstat (limited to 'rand/rand_hc')
| -rw-r--r-- | rand/rand_hc/CHANGELOG.md | 8 | ||||
| -rw-r--r-- | rand/rand_hc/COPYRIGHT | 12 | ||||
| -rw-r--r-- | rand/rand_hc/Cargo.toml | 21 | ||||
| -rw-r--r-- | rand/rand_hc/LICENSE-APACHE | 201 | ||||
| -rw-r--r-- | rand/rand_hc/LICENSE-MIT | 25 | ||||
| -rw-r--r-- | rand/rand_hc/README.md | 45 | ||||
| -rw-r--r-- | rand/rand_hc/src/hc128.rs | 462 | ||||
| -rw-r--r-- | rand/rand_hc/src/lib.rs | 25 | 
8 files changed, 799 insertions, 0 deletions
diff --git a/rand/rand_hc/CHANGELOG.md b/rand/rand_hc/CHANGELOG.md new file mode 100644 index 0000000..d0c4a2f --- /dev/null +++ b/rand/rand_hc/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.1.0] - 2018-10-17 +- Pulled out of the Rand crate diff --git a/rand/rand_hc/COPYRIGHT b/rand/rand_hc/COPYRIGHT new file mode 100644 index 0000000..468d907 --- /dev/null +++ b/rand/rand_hc/COPYRIGHT @@ -0,0 +1,12 @@ +Copyrights in the Rand project are retained by their contributors. No +copyright assignment is required to contribute to the Rand project. + +For full authorship information, see the version control history. + +Except as otherwise noted (below and/or in individual files), Rand is +licensed under the Apache License, Version 2.0 <LICENSE-APACHE> or +<http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +<LICENSE-MIT> or <http://opensource.org/licenses/MIT>, at your option. + +The Rand project includes code from the Rust project +published under these same licenses. diff --git a/rand/rand_hc/Cargo.toml b/rand/rand_hc/Cargo.toml new file mode 100644 index 0000000..ed5dd5b --- /dev/null +++ b/rand/rand_hc/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "rand_hc" +version = "0.1.0" +authors = ["The Rand Project Developers"] +license = "MIT/Apache-2.0" +readme = "README.md" +repository = "https://github.com/rust-random/rand" +documentation = "https://rust-random.github.io/rand/rand_hc" +homepage = "https://crates.io/crates/rand_hc" +description = """ +HC128 random number generator +""" +keywords = ["random", "rng", "hc128"] +categories = ["algorithms", "no-std"] + +[badges] +travis-ci = { repository = "rust-random/rand" } +appveyor = { repository = "rust-random/rand" } + +[dependencies] +rand_core = { path = "../rand_core", version = ">=0.2, <0.4", default-features=false } diff --git a/rand/rand_hc/LICENSE-APACHE b/rand/rand_hc/LICENSE-APACHE new file mode 100644 index 0000000..17d7468 --- /dev/null +++ b/rand/rand_hc/LICENSE-APACHE @@ -0,0 +1,201 @@ +                              Apache License +                        Version 2.0, January 2004 +                     https://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +   "License" shall mean the terms and conditions for use, reproduction, +   and distribution as defined by Sections 1 through 9 of this document. + +   "Licensor" shall mean the copyright owner or entity authorized by +   the copyright owner that is granting the License. + +   "Legal Entity" shall mean the union of the acting entity and all +   other entities that control, are controlled by, or are under common +   control with that entity. For the purposes of this definition, +   "control" means (i) the power, direct or indirect, to cause the +   direction or management of such entity, whether by contract or +   otherwise, or (ii) ownership of fifty percent (50%) or more of the +   outstanding shares, or (iii) beneficial ownership of such entity. + +   "You" (or "Your") shall mean an individual or Legal Entity +   exercising permissions granted by this License. + +   "Source" form shall mean the preferred form for making modifications, +   including but not limited to software source code, documentation +   source, and configuration files. + +   "Object" form shall mean any form resulting from mechanical +   transformation or translation of a Source form, including but +   not limited to compiled object code, generated documentation, +   and conversions to other media types. + +   "Work" shall mean the work of authorship, whether in Source or +   Object form, made available under the License, as indicated by a +   copyright notice that is included in or attached to the work +   (an example is provided in the Appendix below). + +   "Derivative Works" shall mean any work, whether in Source or Object +   form, that is based on (or derived from) the Work and for which the +   editorial revisions, annotations, elaborations, or other modifications +   represent, as a whole, an original work of authorship. For the purposes +   of this License, Derivative Works shall not include works that remain +   separable from, or merely link (or bind by name) to the interfaces of, +   the Work and Derivative Works thereof. + +   "Contribution" shall mean any work of authorship, including +   the original version of the Work and any modifications or additions +   to that Work or Derivative Works thereof, that is intentionally +   submitted to Licensor for inclusion in the Work by the copyright owner +   or by an individual or Legal Entity authorized to submit on behalf of +   the copyright owner. For the purposes of this definition, "submitted" +   means any form of electronic, verbal, or written communication sent +   to the Licensor or its representatives, including but not limited to +   communication on electronic mailing lists, source code control systems, +   and issue tracking systems that are managed by, or on behalf of, the +   Licensor for the purpose of discussing and improving the Work, but +   excluding communication that is conspicuously marked or otherwise +   designated in writing by the copyright owner as "Not a Contribution." + +   "Contributor" shall mean Licensor and any individual or Legal Entity +   on behalf of whom a Contribution has been received by Licensor and +   subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +   this License, each Contributor hereby grants to You a perpetual, +   worldwide, non-exclusive, no-charge, royalty-free, irrevocable +   copyright license to reproduce, prepare Derivative Works of, +   publicly display, publicly perform, sublicense, and distribute the +   Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +   this License, each Contributor hereby grants to You a perpetual, +   worldwide, non-exclusive, no-charge, royalty-free, irrevocable +   (except as stated in this section) patent license to make, have made, +   use, offer to sell, sell, import, and otherwise transfer the Work, +   where such license applies only to those patent claims licensable +   by such Contributor that are necessarily infringed by their +   Contribution(s) alone or by combination of their Contribution(s) +   with the Work to which such Contribution(s) was submitted. If You +   institute patent litigation against any entity (including a +   cross-claim or counterclaim in a lawsuit) alleging that the Work +   or a Contribution incorporated within the Work constitutes direct +   or contributory patent infringement, then any patent licenses +   granted to You under this License for that Work shall terminate +   as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +   Work or Derivative Works thereof in any medium, with or without +   modifications, and in Source or Object form, provided that You +   meet the following conditions: + +   (a) You must give any other recipients of the Work or +       Derivative Works a copy of this License; and + +   (b) You must cause any modified files to carry prominent notices +       stating that You changed the files; and + +   (c) You must retain, in the Source form of any Derivative Works +       that You distribute, all copyright, patent, trademark, and +       attribution notices from the Source form of the Work, +       excluding those notices that do not pertain to any part of +       the Derivative Works; and + +   (d) If the Work includes a "NOTICE" text file as part of its +       distribution, then any Derivative Works that You distribute must +       include a readable copy of the attribution notices contained +       within such NOTICE file, excluding those notices that do not +       pertain to any part of the Derivative Works, in at least one +       of the following places: within a NOTICE text file distributed +       as part of the Derivative Works; within the Source form or +       documentation, if provided along with the Derivative Works; or, +       within a display generated by the Derivative Works, if and +       wherever such third-party notices normally appear. The contents +       of the NOTICE file are for informational purposes only and +       do not modify the License. You may add Your own attribution +       notices within Derivative Works that You distribute, alongside +       or as an addendum to the NOTICE text from the Work, provided +       that such additional attribution notices cannot be construed +       as modifying the License. + +   You may add Your own copyright statement to Your modifications and +   may provide additional or different license terms and conditions +   for use, reproduction, or distribution of Your modifications, or +   for any such Derivative Works as a whole, provided Your use, +   reproduction, and distribution of the Work otherwise complies with +   the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +   any Contribution intentionally submitted for inclusion in the Work +   by You to the Licensor shall be under the terms and conditions of +   this License, without any additional terms or conditions. +   Notwithstanding the above, nothing herein shall supersede or modify +   the terms of any separate license agreement you may have executed +   with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +   names, trademarks, service marks, or product names of the Licensor, +   except as required for reasonable and customary use in describing the +   origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +   agreed to in writing, Licensor provides the Work (and each +   Contributor provides its Contributions) on an "AS IS" BASIS, +   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +   implied, including, without limitation, any warranties or conditions +   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +   PARTICULAR PURPOSE. You are solely responsible for determining the +   appropriateness of using or redistributing the Work and assume any +   risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +   whether in tort (including negligence), contract, or otherwise, +   unless required by applicable law (such as deliberate and grossly +   negligent acts) or agreed to in writing, shall any Contributor be +   liable to You for damages, including any direct, indirect, special, +   incidental, or consequential damages of any character arising as a +   result of this License or out of the use or inability to use the +   Work (including but not limited to damages for loss of goodwill, +   work stoppage, computer failure or malfunction, or any and all +   other commercial damages or losses), even if such Contributor +   has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +   the Work or Derivative Works thereof, You may choose to offer, +   and charge a fee for, acceptance of support, warranty, indemnity, +   or other liability obligations and/or rights consistent with this +   License. However, in accepting such obligations, You may act only +   on Your own behalf and on Your sole responsibility, not on behalf +   of any other Contributor, and only if You agree to indemnify, +   defend, and hold each Contributor harmless for any liability +   incurred by, or claims asserted against, such Contributor by reason +   of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +   To apply the Apache License to your work, attach the following +   boilerplate notice, with the fields enclosed by brackets "[]" +   replaced with your own identifying information. (Don't include +   the brackets!)  The text should be enclosed in the appropriate +   comment syntax for the file format. We also recommend that a +   file or class name and description of purpose be included on the +   same "printed page" as the copyright notice for easier +   identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +	https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/rand/rand_hc/LICENSE-MIT b/rand/rand_hc/LICENSE-MIT new file mode 100644 index 0000000..cf65607 --- /dev/null +++ b/rand/rand_hc/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright 2018 Developers of the Rand project + +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. diff --git a/rand/rand_hc/README.md b/rand/rand_hc/README.md new file mode 100644 index 0000000..178548a --- /dev/null +++ b/rand/rand_hc/README.md @@ -0,0 +1,45 @@ +# rand_hc + +[](https://travis-ci.org/rust-random/rand) +[](https://ci.appveyor.com/project/rust-random/rand) +[](https://crates.io/crates/rand_hc) +[[](https://rust-random.github.io/book/) +[](https://rust-random.github.io/rand/rand_hc) +[](https://docs.rs/rand_hc) +[](https://github.com/rust-random/rand#rust-version-requirements) + +A cryptographically secure random number generator that uses the HC-128 +algorithm. + +HC-128 is a stream cipher designed by Hongjun Wu[^1], that we use as an +RNG. It is selected as one of the "stream ciphers suitable for widespread +adoption" by eSTREAM[^2]. + +Links: + +-   [API documentation (master)](https://rust-random.github.io/rand/rand_hc) +-   [API documentation (docs.rs)](https://docs.rs/rand_hc) +-   [Changelog](CHANGELOG.md) + +[rand]: https://crates.io/crates/rand +[^1]: Hongjun Wu (2008). ["The Stream Cipher HC-128"]( +      http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc128_p3.pdf). +      *The eSTREAM Finalists*, LNCS 4986, pp. 39–47, Springer-Verlag. + +[^2]: [eSTREAM: the ECRYPT Stream Cipher Project]( +      http://www.ecrypt.eu.org/stream/) + + +## Crate Features + +`rand_hc` is `no_std` compatible. It does not require any functionality +outside of the `core` lib, thus there are no features to configure. + + +# License + +`rand_hc` is distributed under the terms of both the MIT license and the +Apache License (Version 2.0). + +See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT), and +[COPYRIGHT](COPYRIGHT) for details. diff --git a/rand/rand_hc/src/hc128.rs b/rand/rand_hc/src/hc128.rs new file mode 100644 index 0000000..d1dadcc --- /dev/null +++ b/rand/rand_hc/src/hc128.rs @@ -0,0 +1,462 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The HC-128 random number generator. + +use core::fmt; +use rand_core::{CryptoRng, RngCore, SeedableRng, Error, le}; +use rand_core::block::{BlockRngCore, BlockRng}; + +const SEED_WORDS: usize = 8; // 128 bit key followed by 128 bit iv + +/// A cryptographically secure random number generator that uses the HC-128 +/// algorithm. +/// +/// HC-128 is a stream cipher designed by Hongjun Wu[^1], that we use as an +/// RNG. It is selected as one of the "stream ciphers suitable for widespread +/// adoption" by eSTREAM[^2]. +/// +/// HC-128 is an array based RNG. In this it is similar to RC-4 and ISAAC before +/// it, but those have never been proven cryptographically secure (or have even +/// been significantly compromised, as in the case of RC-4[^5]). +/// +/// Because HC-128 works with simple indexing into a large array and with a few +/// operations that parallelize well, it has very good performance. The size of +/// the array it needs, 4kb, can however be a disadvantage. +/// +/// This implementation is not based on the version of HC-128 submitted to the +/// eSTREAM contest, but on a later version by the author with a few small +/// improvements from December 15, 2009[^3]. +/// +/// HC-128 has no known weaknesses that are easier to exploit than doing a +/// brute-force search of 2<sup>128</sup>. A very comprehensive analysis of the +/// current state of known attacks / weaknesses of HC-128 is given in *Some +/// Results On Analysis And Implementation Of HC-128 Stream Cipher*[^4]. +/// +/// The average cycle length is expected to be +/// 2<sup>1024*32+10-1</sup> = 2<sup>32777</sup>. +/// We support seeding with a 256-bit array, which matches the 128-bit key +/// concatenated with a 128-bit IV from the stream cipher. +/// +/// This implementation uses an output buffer of sixteen `u32` words, and uses +/// [`BlockRng`] to implement the [`RngCore`] methods. +/// +/// ## References +/// [^1]: Hongjun Wu (2008). ["The Stream Cipher HC-128"]( +///       http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc128_p3.pdf). +///       *The eSTREAM Finalists*, LNCS 4986, pp. 39–47, Springer-Verlag. +/// +/// [^2]: [eSTREAM: the ECRYPT Stream Cipher Project]( +///       http://www.ecrypt.eu.org/stream/) +/// +/// [^3]: Hongjun Wu, [Stream Ciphers HC-128 and HC-256]( +///       https://www.ntu.edu.sg/home/wuhj/research/hc/index.html) +/// +/// [^4]: Shashwat Raizada (January 2015),["Some Results On Analysis And +///       Implementation Of HC-128 Stream Cipher"]( +///       http://library.isical.ac.in:8080/jspui/bitstream/123456789/6636/1/TH431.pdf). +/// +/// [^5]: Internet Engineering Task Force (February 2015), +///       ["Prohibiting RC4 Cipher Suites"](https://tools.ietf.org/html/rfc7465). +/// +/// [`BlockRng`]: ../rand_core/block/struct.BlockRng.html +/// [`RngCore`]: ../rand_core/trait.RngCore.html +#[derive(Clone, Debug)] +pub struct Hc128Rng(BlockRng<Hc128Core>); + +impl RngCore for Hc128Rng { +    #[inline(always)] +    fn next_u32(&mut self) -> u32 { +        self.0.next_u32() +    } + +    #[inline(always)] +    fn next_u64(&mut self) -> u64 { +        self.0.next_u64() +    } + +    fn fill_bytes(&mut self, dest: &mut [u8]) { +        self.0.fill_bytes(dest) +    } + +    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { +        self.0.try_fill_bytes(dest) +    } +} + +impl SeedableRng for Hc128Rng { +    type Seed = <Hc128Core as SeedableRng>::Seed; + +    fn from_seed(seed: Self::Seed) -> Self { +        Hc128Rng(BlockRng::<Hc128Core>::from_seed(seed)) +    } + +    fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> { +        BlockRng::<Hc128Core>::from_rng(rng).map(Hc128Rng) +    } +} + +impl CryptoRng for Hc128Rng {} + +/// The core of `Hc128Rng`, used with `BlockRng`. +#[derive(Clone)] +pub struct Hc128Core { +    t: [u32; 1024], +    counter1024: usize, +} + +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for Hc128Core { +    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +        write!(f, "Hc128Core {{}}") +    } +} + +impl BlockRngCore for Hc128Core { +    type Item = u32; +    type Results = [u32; 16]; + +    fn generate(&mut self, results: &mut Self::Results) { +        assert!(self.counter1024 % 16 == 0); + +        let cc = self.counter1024 % 512; +        let dd = (cc + 16) % 512; +        let ee = cc.wrapping_sub(16) % 512; + +        if self.counter1024 & 512 == 0 { +            // P block +            results[0]  = self.step_p(cc+0,  cc+1,  ee+13, ee+6,  ee+4); +            results[1]  = self.step_p(cc+1,  cc+2,  ee+14, ee+7,  ee+5); +            results[2]  = self.step_p(cc+2,  cc+3,  ee+15, ee+8,  ee+6); +            results[3]  = self.step_p(cc+3,  cc+4,  cc+0,  ee+9,  ee+7); +            results[4]  = self.step_p(cc+4,  cc+5,  cc+1,  ee+10, ee+8); +            results[5]  = self.step_p(cc+5,  cc+6,  cc+2,  ee+11, ee+9); +            results[6]  = self.step_p(cc+6,  cc+7,  cc+3,  ee+12, ee+10); +            results[7]  = self.step_p(cc+7,  cc+8,  cc+4,  ee+13, ee+11); +            results[8]  = self.step_p(cc+8,  cc+9,  cc+5,  ee+14, ee+12); +            results[9]  = self.step_p(cc+9,  cc+10, cc+6,  ee+15, ee+13); +            results[10] = self.step_p(cc+10, cc+11, cc+7,  cc+0,  ee+14); +            results[11] = self.step_p(cc+11, cc+12, cc+8,  cc+1,  ee+15); +            results[12] = self.step_p(cc+12, cc+13, cc+9,  cc+2,  cc+0); +            results[13] = self.step_p(cc+13, cc+14, cc+10, cc+3,  cc+1); +            results[14] = self.step_p(cc+14, cc+15, cc+11, cc+4,  cc+2); +            results[15] = self.step_p(cc+15, dd+0,  cc+12, cc+5,  cc+3); +        } else { +            // Q block +            results[0]  = self.step_q(cc+0,  cc+1,  ee+13, ee+6,  ee+4); +            results[1]  = self.step_q(cc+1,  cc+2,  ee+14, ee+7,  ee+5); +            results[2]  = self.step_q(cc+2,  cc+3,  ee+15, ee+8,  ee+6); +            results[3]  = self.step_q(cc+3,  cc+4,  cc+0,  ee+9,  ee+7); +            results[4]  = self.step_q(cc+4,  cc+5,  cc+1,  ee+10, ee+8); +            results[5]  = self.step_q(cc+5,  cc+6,  cc+2,  ee+11, ee+9); +            results[6]  = self.step_q(cc+6,  cc+7,  cc+3,  ee+12, ee+10); +            results[7]  = self.step_q(cc+7,  cc+8,  cc+4,  ee+13, ee+11); +            results[8]  = self.step_q(cc+8,  cc+9,  cc+5,  ee+14, ee+12); +            results[9]  = self.step_q(cc+9,  cc+10, cc+6,  ee+15, ee+13); +            results[10] = self.step_q(cc+10, cc+11, cc+7,  cc+0,  ee+14); +            results[11] = self.step_q(cc+11, cc+12, cc+8,  cc+1,  ee+15); +            results[12] = self.step_q(cc+12, cc+13, cc+9,  cc+2,  cc+0); +            results[13] = self.step_q(cc+13, cc+14, cc+10, cc+3,  cc+1); +            results[14] = self.step_q(cc+14, cc+15, cc+11, cc+4,  cc+2); +            results[15] = self.step_q(cc+15, dd+0,  cc+12, cc+5,  cc+3); +        } +        self.counter1024 = self.counter1024.wrapping_add(16); +    } +} + +impl Hc128Core { +    // One step of HC-128, update P and generate 32 bits keystream +    #[inline(always)] +    fn step_p(&mut self, i: usize, i511: usize, i3: usize, i10: usize, i12: usize) +         -> u32 +    { +        let (p, q) = self.t.split_at_mut(512); +        // FIXME: it would be great if we the bounds checks here could be +        // optimized out, and we would not need unsafe. +        // This improves performance by about 7%. +        unsafe { +            let temp0 = p.get_unchecked(i511).rotate_right(23); +            let temp1 = p.get_unchecked(i3).rotate_right(10); +            let temp2 = p.get_unchecked(i10).rotate_right(8); +            *p.get_unchecked_mut(i) = p.get_unchecked(i) +                                       .wrapping_add(temp2) +                                       .wrapping_add(temp0 ^ temp1); +            let temp3 = { +                // The h1 function in HC-128 +                let a = *p.get_unchecked(i12) as u8; +                let c = (p.get_unchecked(i12) >> 16) as u8; +                q[a as usize].wrapping_add(q[256 + c as usize]) +            }; +            temp3 ^ p.get_unchecked(i) +        } +    } + +    // One step of HC-128, update Q and generate 32 bits keystream +    // Similar to `step_p`, but `p` and `q` are swapped, and the rotates are to +    // the left instead of to the right. +    #[inline(always)] +    fn step_q(&mut self, i: usize, i511: usize, i3: usize, i10: usize, i12: usize) +         -> u32 +    { +        let (p, q) = self.t.split_at_mut(512); +        unsafe { +            let temp0 = q.get_unchecked(i511).rotate_left(23); +            let temp1 = q.get_unchecked(i3).rotate_left(10); +            let temp2 = q.get_unchecked(i10).rotate_left(8); +            *q.get_unchecked_mut(i) = q.get_unchecked(i) +                                       .wrapping_add(temp2) +                                       .wrapping_add(temp0 ^ temp1); +            let temp3 = { +                // The h2 function in HC-128 +                let a = *q.get_unchecked(i12) as u8; +                let c = (q.get_unchecked(i12) >> 16) as u8; +                p[a as usize].wrapping_add(p[256 + c as usize]) +            }; +            temp3 ^ q.get_unchecked(i) +        } +    } + +    fn sixteen_steps(&mut self) { +        assert!(self.counter1024 % 16 == 0); + +        let cc = self.counter1024 % 512; +        let dd = (cc + 16) % 512; +        let ee = cc.wrapping_sub(16) % 512; + +        if self.counter1024 < 512 { +            // P block +            self.t[cc+0]  = self.step_p(cc+0,  cc+1,  ee+13, ee+6,  ee+4); +            self.t[cc+1]  = self.step_p(cc+1,  cc+2,  ee+14, ee+7,  ee+5); +            self.t[cc+2]  = self.step_p(cc+2,  cc+3,  ee+15, ee+8,  ee+6); +            self.t[cc+3]  = self.step_p(cc+3,  cc+4,  cc+0,  ee+9,  ee+7); +            self.t[cc+4]  = self.step_p(cc+4,  cc+5,  cc+1,  ee+10, ee+8); +            self.t[cc+5]  = self.step_p(cc+5,  cc+6,  cc+2,  ee+11, ee+9); +            self.t[cc+6]  = self.step_p(cc+6,  cc+7,  cc+3,  ee+12, ee+10); +            self.t[cc+7]  = self.step_p(cc+7,  cc+8,  cc+4,  ee+13, ee+11); +            self.t[cc+8]  = self.step_p(cc+8,  cc+9,  cc+5,  ee+14, ee+12); +            self.t[cc+9]  = self.step_p(cc+9,  cc+10, cc+6,  ee+15, ee+13); +            self.t[cc+10] = self.step_p(cc+10, cc+11, cc+7,  cc+0,  ee+14); +            self.t[cc+11] = self.step_p(cc+11, cc+12, cc+8,  cc+1,  ee+15); +            self.t[cc+12] = self.step_p(cc+12, cc+13, cc+9,  cc+2,  cc+0); +            self.t[cc+13] = self.step_p(cc+13, cc+14, cc+10, cc+3,  cc+1); +            self.t[cc+14] = self.step_p(cc+14, cc+15, cc+11, cc+4,  cc+2); +            self.t[cc+15] = self.step_p(cc+15, dd+0,  cc+12, cc+5,  cc+3); +        } else { +            // Q block +            self.t[cc+512+0]  = self.step_q(cc+0,  cc+1,  ee+13, ee+6,  ee+4); +            self.t[cc+512+1]  = self.step_q(cc+1,  cc+2,  ee+14, ee+7,  ee+5); +            self.t[cc+512+2]  = self.step_q(cc+2,  cc+3,  ee+15, ee+8,  ee+6); +            self.t[cc+512+3]  = self.step_q(cc+3,  cc+4,  cc+0,  ee+9,  ee+7); +            self.t[cc+512+4]  = self.step_q(cc+4,  cc+5,  cc+1,  ee+10, ee+8); +            self.t[cc+512+5]  = self.step_q(cc+5,  cc+6,  cc+2,  ee+11, ee+9); +            self.t[cc+512+6]  = self.step_q(cc+6,  cc+7,  cc+3,  ee+12, ee+10); +            self.t[cc+512+7]  = self.step_q(cc+7,  cc+8,  cc+4,  ee+13, ee+11); +            self.t[cc+512+8]  = self.step_q(cc+8,  cc+9,  cc+5,  ee+14, ee+12); +            self.t[cc+512+9]  = self.step_q(cc+9,  cc+10, cc+6,  ee+15, ee+13); +            self.t[cc+512+10] = self.step_q(cc+10, cc+11, cc+7,  cc+0,  ee+14); +            self.t[cc+512+11] = self.step_q(cc+11, cc+12, cc+8,  cc+1,  ee+15); +            self.t[cc+512+12] = self.step_q(cc+12, cc+13, cc+9,  cc+2,  cc+0); +            self.t[cc+512+13] = self.step_q(cc+13, cc+14, cc+10, cc+3,  cc+1); +            self.t[cc+512+14] = self.step_q(cc+14, cc+15, cc+11, cc+4,  cc+2); +            self.t[cc+512+15] = self.step_q(cc+15, dd+0,  cc+12, cc+5,  cc+3); +        } +        self.counter1024 += 16; +    } + +    // Initialize an HC-128 random number generator. The seed has to be +    // 256 bits in length (`[u32; 8]`), matching the 128 bit `key` followed by +    // 128 bit `iv` when HC-128 where to be used as a stream cipher. +    fn init(seed: [u32; SEED_WORDS]) -> Self { +        #[inline] +        fn f1(x: u32) -> u32 { +            x.rotate_right(7) ^ x.rotate_right(18) ^ (x >> 3) +        } + +        #[inline] +        fn f2(x: u32) -> u32 { +            x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10) +        } + +        let mut t = [0u32; 1024]; + +        // Expand the key and iv into P and Q +        let (key, iv) = seed.split_at(4); +        t[..4].copy_from_slice(key); +        t[4..8].copy_from_slice(key); +        t[8..12].copy_from_slice(iv); +        t[12..16].copy_from_slice(iv); + +        // Generate the 256 intermediate values W[16] ... W[256+16-1], and +        // copy the last 16 generated values to the start op P. +        for i in 16..256+16 { +            t[i] = f2(t[i-2]).wrapping_add(t[i-7]).wrapping_add(f1(t[i-15])) +                   .wrapping_add(t[i-16]).wrapping_add(i as u32); +        } +        { +            let (p1, p2) = t.split_at_mut(256); +            p1[0..16].copy_from_slice(&p2[0..16]); +        } + +        // Generate both the P and Q tables +        for i in 16..1024 { +            t[i] = f2(t[i-2]).wrapping_add(t[i-7]).wrapping_add(f1(t[i-15])) +                   .wrapping_add(t[i-16]).wrapping_add(256 + i as u32); +        } + +        let mut core = Self { t, counter1024: 0 }; + +        // run the cipher 1024 steps +        for _ in 0..64 { core.sixteen_steps() }; +        core.counter1024 = 0; +        core +    } +} + +impl SeedableRng for Hc128Core { +    type Seed = [u8; SEED_WORDS*4]; + +    /// Create an HC-128 random number generator with a seed. The seed has to be +    /// 256 bits in length, matching the 128 bit `key` followed by 128 bit `iv` +    /// when HC-128 where to be used as a stream cipher. +    fn from_seed(seed: Self::Seed) -> Self { +        let mut seed_u32 = [0u32; SEED_WORDS]; +        le::read_u32_into(&seed, &mut seed_u32); +        Self::init(seed_u32) +    } +} + +impl CryptoRng for Hc128Core {} + +#[cfg(test)] +mod test { +    use ::rand_core::{RngCore, SeedableRng}; +    use super::Hc128Rng; + +    #[test] +    // Test vector 1 from the paper "The Stream Cipher HC-128" +    fn test_hc128_true_values_a() { +        let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key +                    0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv +        let mut rng = Hc128Rng::from_seed(seed); + +        let mut results = [0u32; 16]; +        for i in results.iter_mut() { *i = rng.next_u32(); } +        let expected = [0x73150082, 0x3bfd03a0, 0xfb2fd77f, 0xaa63af0e, +                        0xde122fc6, 0xa7dc29b6, 0x62a68527, 0x8b75ec68, +                        0x9036db1e, 0x81896005, 0x00ade078, 0x491fbf9a, +                        0x1cdc3013, 0x6c3d6e24, 0x90f664b2, 0x9cd57102]; +        assert_eq!(results, expected); +    } + +    #[test] +    // Test vector 2 from the paper "The Stream Cipher HC-128" +    fn test_hc128_true_values_b() { +        let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key +                    1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv +        let mut rng = Hc128Rng::from_seed(seed); + +        let mut results = [0u32; 16]; +        for i in results.iter_mut() { *i = rng.next_u32(); } +        let expected = [0xc01893d5, 0xb7dbe958, 0x8f65ec98, 0x64176604, +                        0x36fc6724, 0xc82c6eec, 0x1b1c38a7, 0xc9b42a95, +                        0x323ef123, 0x0a6a908b, 0xce757b68, 0x9f14f7bb, +                        0xe4cde011, 0xaeb5173f, 0x89608c94, 0xb5cf46ca]; +        assert_eq!(results, expected); +    } + +    #[test] +    // Test vector 3 from the paper "The Stream Cipher HC-128" +    fn test_hc128_true_values_c() { +        let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key +                    0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv +        let mut rng = Hc128Rng::from_seed(seed); + +        let mut results = [0u32; 16]; +        for i in results.iter_mut() { *i = rng.next_u32(); } +        let expected = [0x518251a4, 0x04b4930a, 0xb02af931, 0x0639f032, +                        0xbcb4a47a, 0x5722480b, 0x2bf99f72, 0xcdc0e566, +                        0x310f0c56, 0xd3cc83e8, 0x663db8ef, 0x62dfe07f, +                        0x593e1790, 0xc5ceaa9c, 0xab03806f, 0xc9a6e5a0]; +        assert_eq!(results, expected); +    } + +    #[test] +    fn test_hc128_true_values_u64() { +        let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key +                    0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv +        let mut rng = Hc128Rng::from_seed(seed); + +        let mut results = [0u64; 8]; +        for i in results.iter_mut() { *i = rng.next_u64(); } +        let expected = [0x3bfd03a073150082, 0xaa63af0efb2fd77f, +                        0xa7dc29b6de122fc6, 0x8b75ec6862a68527, +                        0x818960059036db1e, 0x491fbf9a00ade078, +                        0x6c3d6e241cdc3013, 0x9cd5710290f664b2]; +        assert_eq!(results, expected); + +        // The RNG operates in a P block of 512 results and next a Q block. +        // After skipping 2*800 u32 results we end up somewhere in the Q block +        // of the second round +        for _ in 0..800 { rng.next_u64(); } + +        for i in results.iter_mut() { *i = rng.next_u64(); } +        let expected = [0xd8c4d6ca84d0fc10, 0xf16a5d91dc66e8e7, +                        0xd800de5bc37a8653, 0x7bae1f88c0dfbb4c, +                        0x3bfe1f374e6d4d14, 0x424b55676be3fa06, +                        0xe3a1e8758cbff579, 0x417f7198c5652bcd]; +        assert_eq!(results, expected); +    } + +    #[test] +    fn test_hc128_true_values_bytes() { +        let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key +                    0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv +        let mut rng = Hc128Rng::from_seed(seed); +        let expected = [0x31, 0xf9, 0x2a, 0xb0, 0x32, 0xf0, 0x39, 0x06, +                 0x7a, 0xa4, 0xb4, 0xbc, 0x0b, 0x48, 0x22, 0x57, +                 0x72, 0x9f, 0xf9, 0x2b, 0x66, 0xe5, 0xc0, 0xcd, +                 0x56, 0x0c, 0x0f, 0x31, 0xe8, 0x83, 0xcc, 0xd3, +                 0xef, 0xb8, 0x3d, 0x66, 0x7f, 0xe0, 0xdf, 0x62, +                 0x90, 0x17, 0x3e, 0x59, 0x9c, 0xaa, 0xce, 0xc5, +                 0x6f, 0x80, 0x03, 0xab, 0xa0, 0xe5, 0xa6, 0xc9, +                 0x60, 0x95, 0x84, 0x7a, 0xa5, 0x68, 0x5a, 0x84, +                 0xea, 0xd5, 0xf3, 0xea, 0x73, 0xa9, 0xad, 0x01, +                 0x79, 0x7d, 0xbe, 0x9f, 0xea, 0xe3, 0xf9, 0x74, +                 0x0e, 0xda, 0x2f, 0xa0, 0xe4, 0x7b, 0x4b, 0x1b, +                 0xdd, 0x17, 0x69, 0x4a, 0xfe, 0x9f, 0x56, 0x95, +                 0xad, 0x83, 0x6b, 0x9d, 0x60, 0xa1, 0x99, 0x96, +                 0x90, 0x00, 0x66, 0x7f, 0xfa, 0x7e, 0x65, 0xe9, +                 0xac, 0x8b, 0x92, 0x34, 0x77, 0xb4, 0x23, 0xd0, +                 0xb9, 0xab, 0xb1, 0x47, 0x7d, 0x4a, 0x13, 0x0a]; + +        // Pick a somewhat large buffer so we can test filling with the +        // remainder from `state.results`, directly filling the buffer, and +        // filling the remainder of the buffer. +        let mut buffer = [0u8; 16*4*2]; +        // Consume a value so that we have a remainder. +        assert!(rng.next_u64() == 0x04b4930a518251a4); +        rng.fill_bytes(&mut buffer); + +        // [u8; 128] doesn't implement PartialEq +        assert_eq!(buffer.len(), expected.len()); +        for (b, e) in buffer.iter().zip(expected.iter()) { +            assert_eq!(b, e); +        } +    } + +    #[test] +    fn test_hc128_clone() { +        let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key +                    0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv +        let mut rng1 = Hc128Rng::from_seed(seed); +        let mut rng2 = rng1.clone(); +        for _ in 0..16 { +            assert_eq!(rng1.next_u32(), rng2.next_u32()); +        } +    } +} diff --git a/rand/rand_hc/src/lib.rs b/rand/rand_hc/src/lib.rs new file mode 100644 index 0000000..f1d772d --- /dev/null +++ b/rand/rand_hc/src/lib.rs @@ -0,0 +1,25 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The HC128 random number generator. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", +       html_favicon_url = "https://www.rust-lang.org/favicon.ico", +       html_root_url = "https://rust-random.github.io/rand/")] + +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] +#![doc(test(attr(allow(unused_variables), deny(warnings))))] + +#![no_std] + +extern crate rand_core; + +mod hc128; + +pub use hc128::{Hc128Rng, Hc128Core};  | 
