• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 use super::{counter, iv::Iv, quic::Sample, BLOCK_LEN};
17 use crate::{c, endian::*};
18 
19 #[repr(transparent)]
20 pub struct Key([LittleEndian<u32>; KEY_LEN / 4]);
21 
22 impl From<[u8; KEY_LEN]> for Key {
23     #[inline]
from(value: [u8; KEY_LEN]) -> Self24     fn from(value: [u8; KEY_LEN]) -> Self {
25         Self(FromByteArray::from_byte_array(&value))
26     }
27 }
28 
29 impl Key {
30     #[inline] // Optimize away match on `counter`.
encrypt_in_place(&self, counter: Counter, in_out: &mut [u8])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.
encrypt_iv_xor_blocks_in_place(&self, iv: Iv, in_out: &mut [u8; 2 * BLOCK_LEN])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]
new_mask(&self, sample: Sample) -> [u8; 5]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 
encrypt_overlapping(&self, counter: Counter, in_out: &mut [u8], in_prefix_len: usize)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.`
encrypt( &self, counter: CounterOrIv, input: *const u8, in_out_len: usize, output: *mut u8, )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]
words_less_safe(&self) -> &[LittleEndian<u32>; KEY_LEN / 4]127     pub(super) fn words_less_safe(&self) -> &[LittleEndian<u32>; KEY_LEN / 4] {
128         &self.0
129     }
130 }
131 
132 pub type Counter = counter::Counter<LittleEndian<u32>>;
133 
134 enum CounterOrIv {
135     Counter(Counter),
136     Iv(Iv),
137 }
138 
139 const KEY_BLOCKS: usize = 2;
140 pub const KEY_LEN: usize = KEY_BLOCKS * BLOCK_LEN;
141 
142 #[cfg(test)]
143 mod 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]
chacha20_tests()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 
chacha20_test_case_inner( key: &Key, nonce: &[u8], ctr: u32, input: &[u8], expected: &[u8], len: usize, in_out_buf: &mut [u8], )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 }
236