ring/aead/
chacha.rs

1// Copyright 2016 Brian Smith.
2// Portions Copyright (c) 2016, Google Inc.
3//
4// Permission to use, copy, modify, and/or distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
11// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
16use super::{counter, iv::Iv, quic::Sample, BLOCK_LEN};
17use crate::{c, endian::*};
18
19#[repr(transparent)]
20pub struct Key([LittleEndian<u32>; KEY_LEN / 4]);
21
22impl From<[u8; KEY_LEN]> for Key {
23    #[inline]
24    fn from(value: [u8; KEY_LEN]) -> Self {
25        Self(FromByteArray::from_byte_array(&value))
26    }
27}
28
29impl Key {
30    #[inline] // Optimize away match on `counter`.
31    pub fn encrypt_in_place(&self, counter: Counter, in_out: &mut [u8]) {
32        unsafe {
33            self.encrypt(
34                CounterOrIv::Counter(counter),
35                in_out.as_ptr(),
36                in_out.len(),
37                in_out.as_mut_ptr(),
38            );
39        }
40    }
41
42    #[inline] // Optimize away match on `iv` and length check.
43    pub fn encrypt_iv_xor_blocks_in_place(&self, iv: Iv, in_out: &mut [u8; 2 * BLOCK_LEN]) {
44        unsafe {
45            self.encrypt(
46                CounterOrIv::Iv(iv),
47                in_out.as_ptr(),
48                in_out.len(),
49                in_out.as_mut_ptr(),
50            );
51        }
52    }
53
54    #[inline]
55    pub fn new_mask(&self, sample: Sample) -> [u8; 5] {
56        let mut out: [u8; 5] = [0; 5];
57        let iv = Iv::assume_unique_for_key(sample);
58
59        unsafe {
60            self.encrypt(
61                CounterOrIv::Iv(iv),
62                out.as_ptr(),
63                out.len(),
64                out.as_mut_ptr(),
65            );
66        }
67
68        out
69    }
70
71    pub fn encrypt_overlapping(&self, counter: Counter, in_out: &mut [u8], in_prefix_len: usize) {
72        // XXX: The x86 and at least one branch of the ARM assembly language
73        // code doesn't allow overlapping input and output unless they are
74        // exactly overlapping. TODO: Figure out which branch of the ARM code
75        // has this limitation and come up with a better solution.
76        //
77        // https://rt.openssl.org/Ticket/Display.html?id=4362
78        let len = in_out.len() - in_prefix_len;
79        if cfg!(any(target_arch = "arm", target_arch = "x86")) && in_prefix_len != 0 {
80            in_out.copy_within(in_prefix_len.., 0);
81            self.encrypt_in_place(counter, &mut in_out[..len]);
82        } else {
83            unsafe {
84                self.encrypt(
85                    CounterOrIv::Counter(counter),
86                    in_out[in_prefix_len..].as_ptr(),
87                    len,
88                    in_out.as_mut_ptr(),
89                );
90            }
91        }
92    }
93
94    #[inline] // Optimize away match on `counter.`
95    unsafe fn encrypt(
96        &self,
97        counter: CounterOrIv,
98        input: *const u8,
99        in_out_len: usize,
100        output: *mut u8,
101    ) {
102        let iv = match counter {
103            CounterOrIv::Counter(counter) => counter.into(),
104            CounterOrIv::Iv(iv) => {
105                assert!(in_out_len <= 32);
106                iv
107            }
108        };
109
110        /// XXX: Although this takes an `Iv`, this actually uses it like a
111        /// `Counter`.
112        extern "C" {
113            fn GFp_ChaCha20_ctr32(
114                out: *mut u8,
115                in_: *const u8,
116                in_len: c::size_t,
117                key: &Key,
118                first_iv: &Iv,
119            );
120        }
121
122        GFp_ChaCha20_ctr32(output, input, in_out_len, self, &iv);
123    }
124
125    #[cfg(target_arch = "x86_64")]
126    #[inline]
127    pub(super) fn words_less_safe(&self) -> &[LittleEndian<u32>; KEY_LEN / 4] {
128        &self.0
129    }
130}
131
132pub type Counter = counter::Counter<LittleEndian<u32>>;
133
134enum CounterOrIv {
135    Counter(Counter),
136    Iv(Iv),
137}
138
139const KEY_BLOCKS: usize = 2;
140pub const KEY_LEN: usize = KEY_BLOCKS * BLOCK_LEN;
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145    use crate::test;
146    use alloc::vec;
147    use core::convert::TryInto;
148
149    // This verifies the encryption functionality provided by ChaCha20_ctr32
150    // is successful when either computed on disjoint input/output buffers,
151    // or on overlapping input/output buffers. On some branches of the 32-bit
152    // x86 and ARM code the in-place operation fails in some situations where
153    // the input/output buffers are not exactly overlapping. Such failures are
154    // dependent not only on the degree of overlapping but also the length of
155    // the data. `open()` works around that by moving the input data to the
156    // output location so that the buffers exactly overlap, for those targets.
157    // This test exists largely as a canary for detecting if/when that type of
158    // problem spreads to other platforms.
159    #[test]
160    pub fn chacha20_tests() {
161        test::run(test_file!("chacha_tests.txt"), |section, test_case| {
162            assert_eq!(section, "");
163
164            let key = test_case.consume_bytes("Key");
165            let key: &[u8; KEY_LEN] = key.as_slice().try_into()?;
166            let key = Key::from(*key);
167
168            let ctr = test_case.consume_usize("Ctr");
169            let nonce = test_case.consume_bytes("Nonce");
170            let input = test_case.consume_bytes("Input");
171            let output = test_case.consume_bytes("Output");
172
173            // Pre-allocate buffer for use in test_cases.
174            let mut in_out_buf = vec![0u8; input.len() + 276];
175
176            // Run the test case over all prefixes of the input because the
177            // behavior of ChaCha20 implementation changes dependent on the
178            // length of the input.
179            for len in 0..(input.len() + 1) {
180                chacha20_test_case_inner(
181                    &key,
182                    &nonce,
183                    ctr as u32,
184                    &input[..len],
185                    &output[..len],
186                    len,
187                    &mut in_out_buf,
188                );
189            }
190
191            Ok(())
192        });
193    }
194
195    fn chacha20_test_case_inner(
196        key: &Key,
197        nonce: &[u8],
198        ctr: u32,
199        input: &[u8],
200        expected: &[u8],
201        len: usize,
202        in_out_buf: &mut [u8],
203    ) {
204        // Straightforward encryption into disjoint buffers is computed
205        // correctly.
206        unsafe {
207            key.encrypt(
208                CounterOrIv::Counter(Counter::from_test_vector(nonce, ctr)),
209                input[..len].as_ptr(),
210                len,
211                in_out_buf.as_mut_ptr(),
212            );
213        }
214        assert_eq!(&in_out_buf[..len], expected);
215
216        // Do not test offset buffers for x86 and ARM architectures (see above
217        // for rationale).
218        let max_offset = if cfg!(any(target_arch = "x86", target_arch = "arm")) {
219            0
220        } else {
221            259
222        };
223
224        // Check that in-place encryption works successfully when the pointers
225        // to the input/output buffers are (partially) overlapping.
226        for alignment in 0..16 {
227            for offset in 0..(max_offset + 1) {
228                in_out_buf[alignment + offset..][..len].copy_from_slice(input);
229                let ctr = Counter::from_test_vector(nonce, ctr);
230                key.encrypt_overlapping(ctr, &mut in_out_buf[alignment..], offset);
231                assert_eq!(&in_out_buf[alignment..][..len], expected);
232            }
233        }
234    }
235}