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