#![no_std]
#![allow(unused_variables)]
#![allow(non_upper_case_globals)]
#![allow(unused_imports)]
use core::{
ptr, slice,
sync::atomic::{AtomicBool, Ordering},
};
pub mod fallback_chacha20;
static FALLBACK_TRIGGERED: AtomicBool = AtomicBool::new(false);
#[inline(always)]
fn fallback(
out: &mut [u8],
len: usize,
keystream_only: bool,
key: &[u32; 8],
counter: &mut [u32; 4],
double_rounds: usize,
) {
let mut offset = 0;
while offset < len {
let block_len = (len - offset).min(64);
fallback_chacha20::xor(
&mut out[offset..offset + block_len],
keystream_only,
key,
counter,
double_rounds,
);
counter[0] = counter[0].wrapping_add(1);
offset += block_len;
}
}
#[no_mangle]
pub unsafe extern "C" fn ChaCha20_ctr32_c(
out: *mut u8,
inp: *const u8,
len: usize,
key: *const u32,
counter: *const u32,
) {
FALLBACK_TRIGGERED.store(true, Ordering::SeqCst);
let out = slice::from_raw_parts_mut(out, len);
let keystream_only = inp.is_null();
let key = &*(key as *const [u32; 8]);
let ctr = &mut *(counter as *mut [u32; 4]);
fallback(out, len, keystream_only, key, ctr, 10);
}
#[cfg(fast_chacha_asm)]
extern "C" {
fn ChaCha20_ctr32(
out: *mut u8,
inp: *const u8,
len: usize,
key: *const u32,
counter: *const u32,
);
}
#[cfg(not(fast_chacha_asm))]
#[no_mangle]
pub unsafe extern "C" fn ChaCha20_ctr32(
out: *mut u8,
inp: *const u8,
len: usize,
key: *const u32,
counter: *const u32,
) {
ChaCha20_ctr32_c(out, inp, len, key, counter);
}
#[cfg(fast_chacha_asm)]
mod cpucaps;
#[cfg(fast_chacha_asm)]
pub use cpucaps::init as init_cpu_caps;
#[cfg(not(fast_chacha_asm))]
fn init_cpu_caps() {
}
#[derive(Clone)]
pub struct FastChaCha20 {
key_words: [u32; 8],
counter: [u32; 4],
}
impl FastChaCha20 {
pub fn new(key: &[u8; 32], nonce: &[u8; 12]) -> Self {
debug_assert!(key.len() == 32, "Key must be 32 bytes");
debug_assert!(nonce.len() == 12, "Nonce must be 12 bytes");
init_cpu_caps();
let key_words = [
u32::from_le_bytes([key[0], key[1], key[2], key[3]]),
u32::from_le_bytes([key[4], key[5], key[6], key[7]]),
u32::from_le_bytes([key[8], key[9], key[10], key[11]]),
u32::from_le_bytes([key[12], key[13], key[14], key[15]]),
u32::from_le_bytes([key[16], key[17], key[18], key[19]]),
u32::from_le_bytes([key[20], key[21], key[22], key[23]]),
u32::from_le_bytes([key[24], key[25], key[26], key[27]]),
u32::from_le_bytes([key[28], key[29], key[30], key[31]]),
];
let counter = [
0,
u32::from_le_bytes([nonce[0], nonce[1], nonce[2], nonce[3]]),
u32::from_le_bytes([nonce[4], nonce[5], nonce[6], nonce[7]]),
u32::from_le_bytes([nonce[8], nonce[9], nonce[10], nonce[11]]),
];
Self { key_words, counter }
}
pub fn apply_keystream(&mut self, data: &mut [u8]) {
if data.is_empty() {
return;
}
unsafe {
ChaCha20_ctr32(
data.as_mut_ptr(),
data.as_mut_ptr(),
data.len(),
self.key_words.as_ptr(),
self.counter.as_mut_ptr(),
)
}
}
pub fn apply_keystream_pure(&mut self, data: &mut [u8], double_rounds: usize) {
if data.is_empty() {
return;
}
fallback(data, data.len(), false, &self.key_words, &mut self.counter, double_rounds);
}
pub fn keystream_only(&mut self, data: &mut [u8]) {
if data.is_empty() {
return;
}
fallback(data, data.len(), true, &self.key_words, &mut self.counter, 10);
}
pub fn reset(&mut self) {
self.counter[0] = 0;
}
pub fn set_counter(&mut self, counter: u32) {
self.counter[0] = counter;
}
pub fn seek(&mut self, pos: u64) {
self.counter[0] = (pos / 64) as u32;
}
pub fn current_pos(&self) -> u64 {
(self.counter[0] as u64) * 64
}
pub fn new_with_counter(key: [u8; 32], nonce: [u8; 12], counter: u32) -> Self {
let mut s = Self::new(&key, &nonce);
s.counter[0] = counter;
s
}
}
#[repr(align(16))]
struct Dummy([u8; 16]);
static mut DUMMY: Dummy = Dummy([0; 16]);
pub fn is_asm_available() -> bool {
FALLBACK_TRIGGERED.store(false, Ordering::SeqCst);
unsafe {
init_cpu_caps();
let dummy_ptr = core::ptr::addr_of_mut!(DUMMY.0) as *mut u8;
ChaCha20_ctr32(dummy_ptr, dummy_ptr, 0, dummy_ptr as *const u32, dummy_ptr as *const u32);
}
!FALLBACK_TRIGGERED.load(Ordering::SeqCst)
}