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 126 pub type Counter = counter::Counter<LittleEndian<u32>>; 127 128 enum CounterOrIv { 129 Counter(Counter), 130 Iv(Iv), 131 } 132 133 const KEY_BLOCKS: usize = 2; 134 pub const KEY_LEN: usize = KEY_BLOCKS * BLOCK_LEN; 135 136 #[cfg(test)] 137 mod tests { 138 use super::*; 139 use crate::test; 140 use alloc::vec; 141 use core::convert::TryInto; 142 143 // This verifies the encryption functionality provided by ChaCha20_ctr32 144 // is successful when either computed on disjoint input/output buffers, 145 // or on overlapping input/output buffers. On some branches of the 32-bit 146 // x86 and ARM code the in-place operation fails in some situations where 147 // the input/output buffers are not exactly overlapping. Such failures are 148 // dependent not only on the degree of overlapping but also the length of 149 // the data. `open()` works around that by moving the input data to the 150 // output location so that the buffers exactly overlap, for those targets. 151 // This test exists largely as a canary for detecting if/when that type of 152 // problem spreads to other platforms. 153 #[test] chacha20_tests()154 pub fn chacha20_tests() { 155 test::run(test_file!("chacha_tests.txt"), |section, test_case| { 156 assert_eq!(section, ""); 157 158 let key = test_case.consume_bytes("Key"); 159 let key: &[u8; KEY_LEN] = key.as_slice().try_into()?; 160 let key = Key::from(*key); 161 162 let ctr = test_case.consume_usize("Ctr"); 163 let nonce = test_case.consume_bytes("Nonce"); 164 let input = test_case.consume_bytes("Input"); 165 let output = test_case.consume_bytes("Output"); 166 167 // Pre-allocate buffer for use in test_cases. 168 let mut in_out_buf = vec![0u8; input.len() + 276]; 169 170 // Run the test case over all prefixes of the input because the 171 // behavior of ChaCha20 implementation changes dependent on the 172 // length of the input. 173 for len in 0..(input.len() + 1) { 174 chacha20_test_case_inner( 175 &key, 176 &nonce, 177 ctr as u32, 178 &input[..len], 179 &output[..len], 180 len, 181 &mut in_out_buf, 182 ); 183 } 184 185 Ok(()) 186 }); 187 } 188 chacha20_test_case_inner( key: &Key, nonce: &[u8], ctr: u32, input: &[u8], expected: &[u8], len: usize, in_out_buf: &mut [u8], )189 fn chacha20_test_case_inner( 190 key: &Key, 191 nonce: &[u8], 192 ctr: u32, 193 input: &[u8], 194 expected: &[u8], 195 len: usize, 196 in_out_buf: &mut [u8], 197 ) { 198 // Straightforward encryption into disjoint buffers is computed 199 // correctly. 200 unsafe { 201 key.encrypt( 202 CounterOrIv::Counter(Counter::from_test_vector(nonce, ctr)), 203 input[..len].as_ptr(), 204 len, 205 in_out_buf.as_mut_ptr(), 206 ); 207 } 208 assert_eq!(&in_out_buf[..len], expected); 209 210 // Do not test offset buffers for x86 and ARM architectures (see above 211 // for rationale). 212 let max_offset = if cfg!(any(target_arch = "x86", target_arch = "arm")) { 213 0 214 } else { 215 259 216 }; 217 218 // Check that in-place encryption works successfully when the pointers 219 // to the input/output buffers are (partially) overlapping. 220 for alignment in 0..16 { 221 for offset in 0..(max_offset + 1) { 222 in_out_buf[alignment + offset..][..len].copy_from_slice(input); 223 let ctr = Counter::from_test_vector(nonce, ctr); 224 key.encrypt_overlapping(ctr, &mut in_out_buf[alignment..], offset); 225 assert_eq!(&in_out_buf[alignment..][..len], expected); 226 } 227 } 228 } 229 } 230