summaryrefslogtreecommitdiffstats
path: root/third_party/rust/c2-chacha
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/c2-chacha
parentInitial commit. (diff)
downloadfirefox-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.json1
-rw-r--r--third_party/rust/c2-chacha/Cargo.toml46
-rw-r--r--third_party/rust/c2-chacha/LICENSE-APACHE201
-rw-r--r--third_party/rust/c2-chacha/LICENSE-MIT25
-rw-r--r--third_party/rust/c2-chacha/README.md24
-rw-r--r--third_party/rust/c2-chacha/benches/chacha20.rs20
-rw-r--r--third_party/rust/c2-chacha/src/guts.rs299
-rw-r--r--third_party/rust/c2-chacha/src/lib.rs45
-rw-r--r--third_party/rust/c2-chacha/src/rustcrypto_impl.rs548
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);
+ }
+ }
+ }
+}