diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/c2-chacha | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/c2-chacha')
-rw-r--r-- | third_party/rust/c2-chacha/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/c2-chacha/Cargo.toml | 46 | ||||
-rw-r--r-- | third_party/rust/c2-chacha/LICENSE-APACHE | 201 | ||||
-rw-r--r-- | third_party/rust/c2-chacha/LICENSE-MIT | 25 | ||||
-rw-r--r-- | third_party/rust/c2-chacha/README.md | 24 | ||||
-rw-r--r-- | third_party/rust/c2-chacha/benches/chacha20.rs | 20 | ||||
-rw-r--r-- | third_party/rust/c2-chacha/src/guts.rs | 299 | ||||
-rw-r--r-- | third_party/rust/c2-chacha/src/lib.rs | 45 | ||||
-rw-r--r-- | third_party/rust/c2-chacha/src/rustcrypto_impl.rs | 548 |
9 files changed, 1209 insertions, 0 deletions
diff --git a/third_party/rust/c2-chacha/.cargo-checksum.json b/third_party/rust/c2-chacha/.cargo-checksum.json new file mode 100644 index 0000000000..051db01780 --- /dev/null +++ b/third_party/rust/c2-chacha/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"ca60f483e157aa3a4da8f2e81328a91085df95aac950af29b98ba00264670118","LICENSE-APACHE":"0218327e7a480793ffdd4eb792379a9709e5c135c7ba267f709d6f6d4d70af0a","LICENSE-MIT":"4cada0bd02ea3692eee6f16400d86c6508bbd3bafb2b65fed0419f36d4f83e8f","README.md":"e884c49fd9cf2d6316cb825e1cfc310ed8d9005ecf702506393f95330102a63b","benches/chacha20.rs":"e499e9d8607ad6ac89663258fb8ce4d37cebb5cd231bc8d9c1112fb7905eccb6","src/guts.rs":"ef6337688f5a3d5cbd848e882b53ef2b96824126783103c77709c5dd2856b660","src/lib.rs":"f963f2932409ff28a21a89d5dc4f44c1a9b320705cc474246a93067abcf4b528","src/rustcrypto_impl.rs":"805692d33643b40430559710184e52ac49c82d41a23e6c112e19251e2ea84656"},"package":"214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"}
\ No newline at end of file diff --git a/third_party/rust/c2-chacha/Cargo.toml b/third_party/rust/c2-chacha/Cargo.toml new file mode 100644 index 0000000000..d93ef9c7d5 --- /dev/null +++ b/third_party/rust/c2-chacha/Cargo.toml @@ -0,0 +1,46 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "c2-chacha" +version = "0.2.3" +authors = ["The CryptoCorrosion Contributors"] +description = "The ChaCha family of stream ciphers" +documentation = "https://docs.rs/c2-chacha" +readme = "README.md" +keywords = ["chacha", "chacha20", "xchacha20", "cipher", "crypto"] +categories = ["cryptography", "no-std"] +license = "MIT/Apache-2.0" +repository = "https://github.com/cryptocorrosion/cryptocorrosion" +[dependencies.byteorder] +version = "1.3" +optional = true + +[dependencies.ppv-lite86] +version = "0.2.6" +default-features = false +package = "ppv-lite86" + +[dependencies.stream-cipher] +version = "0.3" +optional = true +[dev-dependencies.hex-literal] +version = "0.2" + +[features] +default = ["std", "simd", "rustcrypto_api"] +rustcrypto_api = ["stream-cipher", "byteorder"] +simd = ["ppv-lite86/simd"] +std = ["ppv-lite86/std"] +[badges.travis-ci] +repository = "cryptocorrosion/cryptocorrosion" diff --git a/third_party/rust/c2-chacha/LICENSE-APACHE b/third_party/rust/c2-chacha/LICENSE-APACHE new file mode 100644 index 0000000000..1eb3215354 --- /dev/null +++ b/third_party/rust/c2-chacha/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://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 2019 The CryptoCorrosion Contributors + +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 + + http://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/third_party/rust/c2-chacha/LICENSE-MIT b/third_party/rust/c2-chacha/LICENSE-MIT new file mode 100644 index 0000000000..d78c961bca --- /dev/null +++ b/third_party/rust/c2-chacha/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2019 The CryptoCorrosion Contributors + +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/third_party/rust/c2-chacha/README.md b/third_party/rust/c2-chacha/README.md new file mode 100644 index 0000000000..a7f472c590 --- /dev/null +++ b/third_party/rust/c2-chacha/README.md @@ -0,0 +1,24 @@ +# The ChaCha family of stream ciphers + +## Features + +- pure Rust implementation +- supports the RustCrypto API +- builds on stable Rust +- portable +- fast: within 15% of throughput of a hand-optimized ASM SIMD implementation + (floodberry/chacha-opt) on my machine (a Xeon X5650, using ppv-lite86) +- no-std compatible (std required only for runtime algorithm selection) + +## Supported Variants + +ChaCha20: used in chacha20-poly1305 in TLS, OpenSSH; arc4random in the BSDs, +Linux /dev/urandom since 4.8. + +Ietf: IETF RFC 7539. Longer nonce, short block counter. + +XChaCha20: constructed analogously to XSalsa20; a mixing step during +initialization allows using a long nonce and along with a full-sized block +counter. + +ChaCha12, ChaCha8: faster; lower security margin of safety. diff --git a/third_party/rust/c2-chacha/benches/chacha20.rs b/third_party/rust/c2-chacha/benches/chacha20.rs new file mode 100644 index 0000000000..b8d7c228bd --- /dev/null +++ b/third_party/rust/c2-chacha/benches/chacha20.rs @@ -0,0 +1,20 @@ +#![feature(test)] +extern crate c2_chacha; +extern crate stream_cipher; +extern crate test; + +use c2_chacha::ChaCha20; +use stream_cipher::{NewStreamCipher, SyncStreamCipher}; +use test::Bencher; + +#[bench] +pub fn stream_10k(b: &mut Bencher) { + let mut state = ChaCha20::new_var(&[0; 32], &[0; 8]).unwrap(); + let mut result = [0; 1024]; + b.iter(|| { + for _ in 0..10 { + state.apply_keystream(&mut result) + } + }); + b.bytes = 10240; +} diff --git a/third_party/rust/c2-chacha/src/guts.rs b/third_party/rust/c2-chacha/src/guts.rs new file mode 100644 index 0000000000..394aab4833 --- /dev/null +++ b/third_party/rust/c2-chacha/src/guts.rs @@ -0,0 +1,299 @@ +#[cfg(feature = "rustcrypto_api")] +pub use stream_cipher::generic_array; + +pub use ppv_lite86::Machine; +use ppv_lite86::{vec128_storage, ArithOps, BitOps32, LaneWords4, MultiLane, StoreBytes, Vec4}; + +pub(crate) const BLOCK: usize = 64; +pub(crate) const BLOCK64: u64 = BLOCK as u64; +const LOG2_BUFBLOCKS: u64 = 2; +const BUFBLOCKS: u64 = 1 << LOG2_BUFBLOCKS; +pub(crate) const BUFSZ64: u64 = BLOCK64 * BUFBLOCKS; +pub(crate) const BUFSZ: usize = BUFSZ64 as usize; + +#[derive(Clone)] +pub struct ChaCha { + pub(crate) b: vec128_storage, + pub(crate) c: vec128_storage, + pub(crate) d: vec128_storage, +} + +#[derive(Clone)] +pub struct State<V> { + pub(crate) a: V, + pub(crate) b: V, + pub(crate) c: V, + pub(crate) d: V, +} + +#[inline(always)] +pub(crate) fn round<V: ArithOps + BitOps32>(mut x: State<V>) -> State<V> { + x.a += x.b; + x.d = (x.d ^ x.a).rotate_each_word_right16(); + x.c += x.d; + x.b = (x.b ^ x.c).rotate_each_word_right20(); + x.a += x.b; + x.d = (x.d ^ x.a).rotate_each_word_right24(); + x.c += x.d; + x.b = (x.b ^ x.c).rotate_each_word_right25(); + x +} + +#[inline(always)] +pub(crate) fn diagonalize<V: LaneWords4>(mut x: State<V>) -> State<V> { + x.b = x.b.shuffle_lane_words3012(); + x.c = x.c.shuffle_lane_words2301(); + x.d = x.d.shuffle_lane_words1230(); + x +} +#[inline(always)] +pub(crate) fn undiagonalize<V: LaneWords4>(mut x: State<V>) -> State<V> { + x.b = x.b.shuffle_lane_words1230(); + x.c = x.c.shuffle_lane_words2301(); + x.d = x.d.shuffle_lane_words3012(); + x +} + +impl ChaCha { + #[inline(always)] + pub fn new(key: &[u8; 32], nonce: &[u8]) -> Self { + init_chacha(key, nonce) + } + + #[inline(always)] + fn pos64<M: Machine>(&self, m: M) -> u64 { + let d: M::u32x4 = m.unpack(self.d); + ((d.extract(1) as u64) << 32) | d.extract(0) as u64 + } + + /// Set 64-bit block count, affecting next refill. + #[inline(always)] + pub(crate) fn seek64<M: Machine>(&mut self, m: M, blockct: u64) { + let d: M::u32x4 = m.unpack(self.d); + self.d = d + .insert((blockct >> 32) as u32, 1) + .insert(blockct as u32, 0) + .into(); + } + + /// Set 32-bit block count, affecting next refill. + #[inline(always)] + pub(crate) fn seek32<M: Machine>(&mut self, m: M, blockct: u32) { + let d: M::u32x4 = m.unpack(self.d); + self.d = d.insert(blockct, 0).into(); + } + + /// Produce output from the current state. + #[inline(always)] + fn output_narrow<M: Machine>(&mut self, m: M, x: State<M::u32x4>, out: &mut [u8; BLOCK]) { + let k = m.vec([0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574]); + (x.a + k).write_le(&mut out[0..16]); + (x.b + m.unpack(self.b)).write_le(&mut out[16..32]); + (x.c + m.unpack(self.c)).write_le(&mut out[32..48]); + (x.d + m.unpack(self.d)).write_le(&mut out[48..64]); + } + + /// Add one to the block counter (no overflow check). + #[inline(always)] + fn inc_block_ct<M: Machine>(&mut self, m: M) { + let mut pos = self.pos64(m); + let d0: M::u32x4 = m.unpack(self.d); + pos += 1; + let d1 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0); + self.d = d1.into(); + } + + /// Produce 4 blocks of output, advancing the state + #[inline(always)] + pub fn refill4(&mut self, drounds: u32, out: &mut [u8; BUFSZ]) { + refill_wide(self, drounds, out) + } + + /// Produce a block of output, advancing the state + #[inline(always)] + pub fn refill(&mut self, drounds: u32, out: &mut [u8; BLOCK]) { + refill_narrow(self, drounds, out) + } + + #[inline(always)] + pub(crate) fn refill_rounds(&mut self, drounds: u32) -> State<vec128_storage> { + refill_narrow_rounds(self, drounds) + } + + #[inline(always)] + pub fn set_stream_param(&mut self, param: u32, value: u64) { + set_stream_param(self, param, value) + } + + #[inline(always)] + pub fn get_stream_param(&self, param: u32) -> u64 { + get_stream_param(self, param) + } +} + +#[inline(always)] +fn refill_wide_impl<Mach: Machine>( + m: Mach, + state: &mut ChaCha, + drounds: u32, + out: &mut [u8; BUFSZ], +) { + let k = m.vec([0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574]); + let mut pos = state.pos64(m); + let d0: Mach::u32x4 = m.unpack(state.d); + pos += 1; + let d1 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0); + pos += 1; + let d2 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0); + pos += 1; + let d3 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0); + + let b = m.unpack(state.b); + let c = m.unpack(state.c); + let mut x = State { + a: Mach::u32x4x4::from_lanes([k, k, k, k]), + b: Mach::u32x4x4::from_lanes([b, b, b, b]), + c: Mach::u32x4x4::from_lanes([c, c, c, c]), + d: m.unpack(Mach::u32x4x4::from_lanes([d0, d1, d2, d3]).into()), + }; + for _ in 0..drounds { + x = round(x); + x = undiagonalize(round(diagonalize(x))); + } + let mut pos = state.pos64(m); + let d0: Mach::u32x4 = m.unpack(state.d); + pos += 1; + let d1 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0); + pos += 1; + let d2 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0); + pos += 1; + let d3 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0); + pos += 1; + let d4 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0); + + let (a, b, c, d) = ( + x.a.to_lanes(), + x.b.to_lanes(), + x.c.to_lanes(), + x.d.to_lanes(), + ); + let sb = m.unpack(state.b); + let sc = m.unpack(state.c); + let sd = [m.unpack(state.d), d1, d2, d3]; + state.d = d4.into(); + let mut words = out.chunks_exact_mut(16); + for ((((&a, &b), &c), &d), &sd) in a.iter().zip(&b).zip(&c).zip(&d).zip(&sd) { + (a + k).write_le(words.next().unwrap()); + (b + sb).write_le(words.next().unwrap()); + (c + sc).write_le(words.next().unwrap()); + (d + sd).write_le(words.next().unwrap()); + } +} + +dispatch!(m, Mach, { + fn refill_wide(state: &mut ChaCha, drounds: u32, out: &mut [u8; BUFSZ]) { + refill_wide_impl(m, state, drounds, out); + } +}); + +/// Refill the buffer from a single-block round, updating the block count. +dispatch_light128!(m, Mach, { + fn refill_narrow(state: &mut ChaCha, drounds: u32, out: &mut [u8; BLOCK]) { + let x = refill_narrow_rounds(state, drounds); + let x = State { + a: m.unpack(x.a), + b: m.unpack(x.b), + c: m.unpack(x.c), + d: m.unpack(x.d), + }; + state.output_narrow(m, x, out); + state.inc_block_ct(m); + } +}); + +/// Single-block, rounds-only; shared by try_apply_keystream for tails shorter than BUFSZ +/// and XChaCha's setup step. +dispatch!(m, Mach, { + fn refill_narrow_rounds(state: &mut ChaCha, drounds: u32) -> State<vec128_storage> { + let k: Mach::u32x4 = m.vec([0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574]); + let mut x = State { + a: k, + b: m.unpack(state.b), + c: m.unpack(state.c), + d: m.unpack(state.d), + }; + for _ in 0..drounds { + x = round(x); + x = undiagonalize(round(diagonalize(x))); + } + State { + a: x.a.into(), + b: x.b.into(), + c: x.c.into(), + d: x.d.into(), + } + } +}); + +dispatch_light128!(m, Mach, { + fn set_stream_param(state: &mut ChaCha, param: u32, value: u64) { + let d: Mach::u32x4 = m.unpack(state.d); + state.d = d + .insert((value >> 32) as u32, (param << 1) | 1) + .insert(value as u32, param << 1) + .into(); + } +}); + +dispatch_light128!(m, Mach, { + fn get_stream_param(state: &ChaCha, param: u32) -> u64 { + let d: Mach::u32x4 = m.unpack(state.d); + ((d.extract((param << 1) | 1) as u64) << 32) | d.extract(param << 1) as u64 + } +}); + +fn read_u32le(xs: &[u8]) -> u32 { + assert_eq!(xs.len(), 4); + u32::from(xs[0]) | (u32::from(xs[1]) << 8) | (u32::from(xs[2]) << 16) | (u32::from(xs[3]) << 24) +} + +dispatch_light128!(m, Mach, { + fn init_chacha(key: &[u8; 32], nonce: &[u8]) -> ChaCha { + let ctr_nonce = [ + 0, + if nonce.len() == 12 { + read_u32le(&nonce[0..4]) + } else { + 0 + }, + read_u32le(&nonce[nonce.len() - 8..nonce.len() - 4]), + read_u32le(&nonce[nonce.len() - 4..]), + ]; + let key0: Mach::u32x4 = m.read_le(&key[..16]); + let key1: Mach::u32x4 = m.read_le(&key[16..]); + ChaCha { + b: key0.into(), + c: key1.into(), + d: ctr_nonce.into(), + } + } +}); + +dispatch_light128!(m, Mach, { + fn init_chacha_x(key: &[u8; 32], nonce: &[u8; 24], rounds: u32) -> ChaCha { + let key0: Mach::u32x4 = m.read_le(&key[..16]); + let key1: Mach::u32x4 = m.read_le(&key[16..]); + let nonce0: Mach::u32x4 = m.read_le(&nonce[..16]); + let mut state = ChaCha { + b: key0.into(), + c: key1.into(), + d: nonce0.into(), + }; + let x = refill_narrow_rounds(&mut state, rounds); + let ctr_nonce1 = [0, 0, read_u32le(&nonce[16..20]), read_u32le(&nonce[20..24])]; + state.b = x.a; + state.c = x.d; + state.d = ctr_nonce1.into(); + state + } +}); diff --git a/third_party/rust/c2-chacha/src/lib.rs b/third_party/rust/c2-chacha/src/lib.rs new file mode 100644 index 0000000000..363a1a7b9d --- /dev/null +++ b/third_party/rust/c2-chacha/src/lib.rs @@ -0,0 +1,45 @@ +// copyright 2019 Kaz Wesley + +//! Pure Rust ChaCha with SIMD optimizations. +//! +//! Stream-cipher usage: +//! ``` +//! extern crate c2_chacha; +//! +//! use c2_chacha::stream_cipher::{NewStreamCipher, SyncStreamCipher, SyncStreamCipherSeek}; +//! use c2_chacha::{ChaCha20, ChaCha12}; +//! +//! let key = b"very secret key-the most secret."; +//! let iv = b"my nonce"; +//! let plaintext = b"The quick brown fox jumps over the lazy dog."; +//! +//! let mut buffer = plaintext.to_vec(); +//! // create cipher instance +//! let mut cipher = ChaCha20::new_var(key, iv).unwrap(); +//! // apply keystream (encrypt) +//! cipher.apply_keystream(&mut buffer); +//! // and decrypt it back +//! cipher.seek(0); +//! cipher.apply_keystream(&mut buffer); +//! // stream ciphers can be used with streaming messages +//! let mut cipher = ChaCha12::new_var(key, iv).unwrap(); +//! for chunk in buffer.chunks_mut(3) { +//! cipher.apply_keystream(chunk); +//! } +//! ``` + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(test)] +#[macro_use] +extern crate hex_literal; + +#[macro_use] +extern crate ppv_lite86; + +pub mod guts; + +#[cfg(feature = "rustcrypto_api")] +mod rustcrypto_impl; +#[cfg(feature = "rustcrypto_api")] +pub use self::rustcrypto_impl::{stream_cipher, ChaCha12, ChaCha20, ChaCha8, Ietf, XChaCha20}; diff --git a/third_party/rust/c2-chacha/src/rustcrypto_impl.rs b/third_party/rust/c2-chacha/src/rustcrypto_impl.rs new file mode 100644 index 0000000000..ce74a63fc7 --- /dev/null +++ b/third_party/rust/c2-chacha/src/rustcrypto_impl.rs @@ -0,0 +1,548 @@ +use byteorder::{ByteOrder, LE}; +use core::cmp; +use crate::guts::generic_array::typenum::{Unsigned, U10, U12, U24, U32, U4, U6, U8}; +use crate::guts::generic_array::{ArrayLength, GenericArray}; +use crate::guts::{ChaCha, Machine, BLOCK, BLOCK64, BUFSZ}; +pub use stream_cipher; +use stream_cipher::{LoopError, NewStreamCipher, SyncStreamCipher, SyncStreamCipherSeek}; + +const BIG_LEN: u64 = 0; +const SMALL_LEN: u64 = 1 << 32; + +#[derive(Clone)] +pub struct Buffer { + pub state: ChaCha, + pub out: [u8; BLOCK], + pub have: i8, + pub len: u64, + pub fresh: bool, +} + +#[derive(Default)] +pub struct X; +#[derive(Default)] +pub struct O; +#[derive(Clone)] +pub struct ChaChaAny<NonceSize, Rounds, IsX> { + pub state: Buffer, + pub _nonce_size: NonceSize, + pub _rounds: Rounds, + pub _is_x: IsX, +} + +impl Buffer { + fn try_apply_keystream<EnableWide: AsBool>( + &mut self, + mut data: &mut [u8], + drounds: u32, + ) -> Result<(), ()> { + // Lazy fill: after a seek() we may be partway into a block we don't have yet. + // We can do this before the overflow check because this is not an effect of the current + // operation. + if self.have < 0 { + self.state.refill(drounds, &mut self.out); + self.have += BLOCK as i8; + // checked in seek() + self.len -= 1; + } + let mut have = self.have as usize; + let have_ready = cmp::min(have, data.len()); + // Check if the requested position would wrap the block counter. Use self.fresh as an extra + // bit to distinguish the initial state from the valid state with no blocks left. + let datalen = (data.len() - have_ready) as u64; + let blocks_needed = datalen / BLOCK64 + u64::from(datalen % BLOCK64 != 0); + let (l, o) = self.len.overflowing_sub(blocks_needed); + if o && !self.fresh { + return Err(()); + } + self.len = l; + self.fresh &= blocks_needed == 0; + // If we have data in the buffer, use that first. + let (d0, d1) = data.split_at_mut(have_ready); + for (data_b, key_b) in d0.iter_mut().zip(&self.out[(BLOCK - have)..]) { + *data_b ^= *key_b; + } + data = d1; + have -= have_ready; + // Process wide chunks. + if EnableWide::BOOL { + let (d0, d1) = data.split_at_mut(data.len() & !(BUFSZ - 1)); + for dd in d0.chunks_exact_mut(BUFSZ) { + let mut buf = [0; BUFSZ]; + self.state.refill4(drounds, &mut buf); + for (data_b, key_b) in dd.iter_mut().zip(buf.iter()) { + *data_b ^= *key_b; + } + } + data = d1; + } + // Handle the tail a block at a time so we'll have storage for any leftovers. + for dd in data.chunks_mut(BLOCK) { + self.state.refill(drounds, &mut self.out); + for (data_b, key_b) in dd.iter_mut().zip(self.out.iter()) { + *data_b ^= *key_b; + } + have = BLOCK - dd.len(); + } + self.have = have as i8; + Ok(()) + } +} + +dispatch_light128!(m, Mach, { + fn seek64(buf: &mut Buffer, ct: u64) { + let blockct = ct / BLOCK64; + buf.len = BIG_LEN.wrapping_sub(blockct); + buf.fresh = blockct == 0; + buf.have = -((ct % BLOCK64) as i8); + buf.state.seek64(m, blockct); + } +}); + +dispatch_light128!(m, Mach, { + fn seek32(buf: &mut Buffer, ct: u64) { + let blockct = ct / BLOCK64; + assert!(blockct < SMALL_LEN || (blockct == SMALL_LEN && ct % BLOCK64 == 0)); + buf.len = SMALL_LEN - blockct; + buf.have = -((ct % BLOCK64) as i8); + buf.state.seek32(m, blockct as u32); + } +}); + +#[cfg(test)] +impl<NonceSize, Rounds: Unsigned, IsX> ChaChaAny<NonceSize, Rounds, IsX> { + pub fn try_apply_keystream_narrow(&mut self, data: &mut [u8]) -> Result<(), ()> { + self.state + .try_apply_keystream::<WideDisabled>(data, Rounds::U32) + } +} + +impl<NonceSize, Rounds> ChaChaAny<NonceSize, Rounds, O> +where + NonceSize: Unsigned + ArrayLength<u8> + Default, + Rounds: Default, +{ + #[inline] + fn new(key: &GenericArray<u8, U32>, nonce: &GenericArray<u8, NonceSize>) -> Self { + let nonce_len = nonce.len(); + ChaChaAny { + state: Buffer { + state: init_chacha(key, nonce), + out: [0; BLOCK], + have: 0, + len: if nonce_len == 12 { SMALL_LEN } else { BIG_LEN }, + fresh: nonce_len != 12, + }, + _nonce_size: Default::default(), + _rounds: Default::default(), + _is_x: Default::default(), + } + } +} + +impl<Rounds: Unsigned + Default> ChaChaAny<U24, Rounds, X> { + fn new(key: &GenericArray<u8, U32>, nonce: &GenericArray<u8, U24>) -> Self { + ChaChaAny { + state: Buffer { + state: init_chacha_x(key, nonce, Rounds::U32), + out: [0; BLOCK], + have: 0, + len: BIG_LEN, + fresh: true, + }, + _nonce_size: Default::default(), + _rounds: Default::default(), + _is_x: Default::default(), + } + } +} + +impl<NonceSize: Unsigned, Rounds, IsX> ChaChaAny<NonceSize, Rounds, IsX> { + #[inline(always)] + fn seek(&mut self, ct: u64) { + if NonceSize::U32 != 12 { + seek64(&mut self.state, ct); + } else { + seek32(&mut self.state, ct); + } + } +} + +impl<NonceSize, Rounds: Unsigned, IsX> ChaChaAny<NonceSize, Rounds, IsX> { + #[inline] + fn try_apply_keystream(&mut self, data: &mut [u8]) -> Result<(), ()> { + self.state + .try_apply_keystream::<WideEnabled>(data, Rounds::U32) + } +} + +impl<NonceSize, Rounds> NewStreamCipher for ChaChaAny<NonceSize, Rounds, O> +where + NonceSize: Unsigned + ArrayLength<u8> + Default, + Rounds: Default, +{ + type KeySize = U32; + type NonceSize = NonceSize; + #[inline] + fn new( + key: &GenericArray<u8, Self::KeySize>, + nonce: &GenericArray<u8, Self::NonceSize>, + ) -> Self { + Self::new(key, nonce) + } +} + +impl<Rounds: Unsigned + Default> NewStreamCipher for ChaChaAny<U24, Rounds, X> { + type KeySize = U32; + type NonceSize = U24; + #[inline] + fn new( + key: &GenericArray<u8, Self::KeySize>, + nonce: &GenericArray<u8, Self::NonceSize>, + ) -> Self { + Self::new(key, nonce) + } +} + +impl<NonceSize: Unsigned, Rounds, IsX> SyncStreamCipherSeek for ChaChaAny<NonceSize, Rounds, IsX> { + #[inline] + fn current_pos(&self) -> u64 { + unimplemented!() + } + #[inline(always)] + fn seek(&mut self, ct: u64) { + Self::seek(self, ct) + } +} + +impl<NonceSize, Rounds: Unsigned, IsX> SyncStreamCipher for ChaChaAny<NonceSize, Rounds, IsX> { + #[inline] + fn try_apply_keystream(&mut self, data: &mut [u8]) -> Result<(), LoopError> { + Self::try_apply_keystream(self, data).map_err(|_| LoopError) + } +} + +trait AsBool { + const BOOL: bool; +} +struct WideEnabled; +impl AsBool for WideEnabled { + const BOOL: bool = true; +} +#[cfg(test)] +struct WideDisabled; +#[cfg(test)] +impl AsBool for WideDisabled { + const BOOL: bool = false; +} + +dispatch_light128!(m, Mach, { + fn init_chacha(key: &GenericArray<u8, U32>, nonce: &[u8]) -> ChaCha { + let ctr_nonce = [ + 0, + if nonce.len() == 12 { + LE::read_u32(&nonce[0..4]) + } else { + 0 + }, + LE::read_u32(&nonce[nonce.len() - 8..nonce.len() - 4]), + LE::read_u32(&nonce[nonce.len() - 4..]), + ]; + let key0: Mach::u32x4 = m.read_le(&key[..16]); + let key1: Mach::u32x4 = m.read_le(&key[16..]); + ChaCha { + b: key0.into(), + c: key1.into(), + d: ctr_nonce.into(), + } + } +}); + +dispatch_light128!(m, Mach, { + fn init_chacha_x( + key: &GenericArray<u8, U32>, + nonce: &GenericArray<u8, U24>, + rounds: u32, + ) -> ChaCha { + let key0: Mach::u32x4 = m.read_le(&key[..16]); + let key1: Mach::u32x4 = m.read_le(&key[16..]); + let nonce0: Mach::u32x4 = m.read_le(&nonce[..16]); + let mut state = ChaCha { + b: key0.into(), + c: key1.into(), + d: nonce0.into(), + }; + let x = state.refill_rounds(rounds); + let ctr_nonce1 = [ + 0, + 0, + LE::read_u32(&nonce[16..20]), + LE::read_u32(&nonce[20..24]), + ]; + state.b = x.a; + state.c = x.d; + state.d = ctr_nonce1.into(); + state + } +}); + +/// IETF RFC 7539 ChaCha. Unsuitable for messages longer than 256 GiB. +pub type Ietf = ChaChaAny<U12, U10, O>; +/// ChaCha20, as used in several standards; from Bernstein's original publication. +pub type ChaCha20 = ChaChaAny<U8, U10, O>; +/// Similar to ChaCha20, but with fewer rounds for higher performance. +pub type ChaCha12 = ChaChaAny<U8, U6, O>; +/// Similar to ChaCha20, but with fewer rounds for higher performance. +pub type ChaCha8 = ChaChaAny<U8, U4, O>; +/// Constructed analogously to XSalsa20; mixes during initialization to support both a long nonce +/// and a full-length (64-bit) block counter. +pub type XChaCha20 = ChaChaAny<U24, U10, X>; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn chacha20_case_1() { + let key = hex!("fa44478c59ca70538e3549096ce8b523232c50d9e8e8d10c203ef6c8d07098a5"); + let nonce = hex!("8d3a0d6d7827c007"); + let expected = hex!(" + 1546a547ff77c5c964e44fd039e913c6395c8f19d43efaa880750f6687b4e6e2d8f42f63546da2d133b5aa2f1ef3f218b6c72943089e4012 + 210c2cbed0e8e93498a6825fc8ff7a504f26db33b6cbe36299436244c9b2eff88302c55933911b7d5dea75f2b6d4761ba44bb6f814c9879d + 2ba2ac8b178fa1104a368694872339738ffb960e33db39efb8eaef885b910eea078e7a1feb3f8185dafd1455b704d76da3a0ce4760741841 + 217bba1e4ece760eaf68617133431feb806c061173af6b8b2a23be90c5d145cc258e3c119aab2800f0c7bc1959dae75481712cab731b7dfd + 783fa3a228f9968aaea68f36a92f43c9b523337a55b97bcaf5f5774447bf41e8"); + let mut state = ChaCha20::new( + GenericArray::from_slice(&key), + GenericArray::from_slice(&nonce), + ); + let offset = 0x3fffffff70u64; + assert!((offset >> 38) != ((offset + 240) >> 38)); // This will overflow the small word of the counter + state.seek(offset); + let mut result = [0; 256]; + state.apply_keystream(&mut result); + assert_eq!(&expected[..], &result[..]); + } + + #[test] + fn chacha12_case_1() { + let key = hex!("27fc120b013b829f1faeefd1ab417e8662f43e0d73f98de866e346353180fdb7"); + let nonce = hex!("db4b4a41d8df18aa"); + let expected = hex!(" + 5f3c8c190a78ab7fe808cae9cbcb0a9837c893492d963a1c2eda6c1558b02c83fc02a44cbbb7e6204d51d1c2430e9c0b58f2937bf593840c + 850bda9051a1f051ddf09d2a03ebf09f01bdba9da0b6da791b2e645641047d11ebf85087d4de5c015fddd044"); + let mut state = ChaCha12::new( + GenericArray::from_slice(&key), + GenericArray::from_slice(&nonce), + ); + let mut result = [0u8; 100]; + state.apply_keystream(&mut result); + assert_eq!(&expected[..], &result[..]); + } + + #[test] + fn chacha8_case_1() { + let key = hex!("641aeaeb08036b617a42cf14e8c5d2d115f8d7cb6ea5e28b9bfaf83e038426a7"); + let nonce = hex!("a14a1168271d459b"); + let mut state = ChaCha8::new( + GenericArray::from_slice(&key), + GenericArray::from_slice(&nonce), + ); + let expected = hex!( + "1721c044a8a6453522dddb3143d0be3512633ca3c79bf8ccc3594cb2c2f310f7bd544f55ce0db38123412d6c45207d5cf9af0c6c680cce1f + 7e43388d1b0346b7133c59fd6af4a5a568aa334ccdc38af5ace201df84d0a3ca225494ca6209345fcf30132e"); + let mut result = [0u8; 100]; + state.apply_keystream(&mut result); + assert_eq!(&expected[..], &result[..]); + } + + #[test] + fn test_ietf() { + let key = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"); + let nonce = hex!("000000090000004a00000000"); + let expected = hex!( + " + 10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4e + d2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e" + ); + let mut state = Ietf::new( + GenericArray::from_slice(&key), + GenericArray::from_slice(&nonce), + ); + let mut result = [0; 64]; + state.seek(64); + state.apply_keystream(&mut result); + assert_eq!(&expected[..], &result[..]); + } + + #[test] + fn rfc_7539_case_1() { + let key = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"); + let nonce = hex!("000000090000004a00000000"); + let mut state = Ietf::new( + GenericArray::from_slice(&key), + GenericArray::from_slice(&nonce), + ); + let mut result = [0; 128]; + state.apply_keystream(&mut result); + let expected = hex!( + "10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4e + d2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e" + ); + assert_eq!(&expected[..], &result[64..]); + } + + #[test] + fn rfc_7539_case_2() { + let key = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"); + let nonce = hex!("000000000000004a00000000"); + let mut state = Ietf::new( + GenericArray::from_slice(&key), + GenericArray::from_slice(&nonce), + ); + let plaintext = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; + let mut buf = [0u8; 178]; + buf[64..].copy_from_slice(plaintext); + state.apply_keystream(&mut buf); + let expected = hex!(" + 6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d624e65152ab + 8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74a35be6b40b8eedf2785e42 + 874d"); + assert_eq!(&expected[..], &buf[64..]); + } + + #[test] + fn rfc_7539_case_2_chunked() { + let key = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"); + let nonce = hex!("000000000000004a00000000"); + let mut state = Ietf::new( + GenericArray::from_slice(&key), + GenericArray::from_slice(&nonce), + ); + let plaintext = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; + let mut buf = [0u8; 178]; + buf[64..].copy_from_slice(plaintext); + state.apply_keystream(&mut buf[..40]); + state.apply_keystream(&mut buf[40..78]); + state.apply_keystream(&mut buf[78..79]); + state.apply_keystream(&mut buf[79..128]); + state.apply_keystream(&mut buf[128..]); + let expected = hex!(" + 6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d624e65152ab + 8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74a35be6b40b8eedf2785e42 + 874d"); + assert_eq!(&expected[..], &buf[64..]); + } + + #[test] + fn xchacha20_case_1() { + let key = hex!("82f411a074f656c66e7dbddb0a2c1b22760b9b2105f4ffdbb1d4b1e824e21def"); + let nonce = hex!("3b07ca6e729eb44a510b7a1be51847838a804f8b106b38bd"); + let mut state = XChaCha20::new( + GenericArray::from_slice(&key), + GenericArray::from_slice(&nonce), + ); + let mut xs = [0u8; 100]; + state.apply_keystream(&mut xs); + let expected = hex!(" + 201863970b8e081f4122addfdf32f6c03e48d9bc4e34a59654f49248b9be59d3eaa106ac3376e7e7d9d1251f2cbf61ef27000f3d19afb76b + 9c247151e7bc26467583f520518eccd2055ccd6cc8a195953d82a10c2065916778db35da2be44415d2f5efb0"); + assert_eq!(&expected[..], &xs[..]); + } + + #[test] + fn seek_off_end() { + let mut st = Ietf::new( + GenericArray::from_slice(&[0xff; 32]), + GenericArray::from_slice(&[0; 12]), + ); + st.seek(0x40_0000_0000); + + assert!(st.try_apply_keystream(&mut [0u8; 1]).is_err()); + } + + #[test] + fn read_last_bytes() { + let mut st = Ietf::new( + GenericArray::from_slice(&[0xff; 32]), + GenericArray::from_slice(&[0; 12]), + ); + + st.seek(0x40_0000_0000 - 10); + st.apply_keystream(&mut [0u8; 10]); + assert!(st.try_apply_keystream(&mut [0u8; 1]).is_err()); + + st.seek(0x40_0000_0000 - 10); + assert!(st.try_apply_keystream(&mut [0u8; 11]).is_err()); + } + + #[test] + fn seek_consistency() { + let mut st = Ietf::new( + GenericArray::from_slice(&[50; 32]), + GenericArray::from_slice(&[44; 12]), + ); + + let mut continuous = [0u8; 1000]; + st.apply_keystream(&mut continuous); + + let mut chunks = [0u8; 1000]; + + st.seek(128); + st.apply_keystream(&mut chunks[128..300]); + + st.seek(0); + st.apply_keystream(&mut chunks[0..10]); + + st.seek(300); + st.apply_keystream(&mut chunks[300..533]); + + st.seek(533); + st.apply_keystream(&mut chunks[533..]); + + st.seek(10); + st.apply_keystream(&mut chunks[10..128]); + + assert_eq!(&continuous[..], &chunks[..]); + } + + #[test] + fn wide_matches_narrow() { + let key = hex!("fa44478c59ca70538e3549096ce8b523232c50d9e8e8d10c203ef6c8d07098a5"); + let nonce = hex!("8d3a0d6d7827c007"); + let mut buf = [0; 2048]; + let mut state = ChaCha20::new( + GenericArray::from_slice(&key), + GenericArray::from_slice(&nonce), + ); + + let lens = [ + 2048, 2047, 1537, 1536, 1535, 1025, 1024, 1023, 768, 513, 512, 511, 200, 100, 50, + ]; + + for &len in &lens { + let buf = &mut buf[0..len]; + + // encrypt with hybrid wide/narrow + state.seek(0); + state.apply_keystream(buf); + state.seek(0); + // decrypt with narrow only + state.try_apply_keystream_narrow(buf).unwrap(); + for &byte in buf.iter() { + assert_eq!(byte, 0); + } + + // encrypt with hybrid wide/narrow + let offset = 0x3fffffff70u64; + state.seek(offset); + state.apply_keystream(buf); + // decrypt with narrow only + state.seek(offset); + state.try_apply_keystream_narrow(buf).unwrap(); + for &byte in buf.iter() { + assert_eq!(byte, 0); + } + } + } +} |