#![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);
#[allow(unused_variables)]
fn fallback(out: &mut [u8], key: &[u32; 8], counter: &mut [u32; 4]) {
out.chunks_mut(64).enumerate().for_each(|(i, out_chunk)| {
fallback_chacha20::xor(out_chunk, key, counter);
counter[0] = counter[0].wrapping_add(1);
});
}
#[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 key = &*(key as *const [u32; 8]);
let ctr = &mut *(counter as *mut [u32; 4]);
fallback(out, key, ctr);
}
#[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 {
assert!(key.len() == 32, "Key must be 32 bytes");
assert!(nonce.len() == 12, "Nonce must be 12 bytes");
let mut key_words = [0u32; 8];
for i in 0..8 {
key_words[i] =
u32::from_le_bytes([key[i * 4], key[i * 4 + 1], key[i * 4 + 2], key[i * 4 + 3]]);
}
let mut counter = [0u32; 4];
for i in 0..3 {
counter[i + 1] = u32::from_le_bytes([
nonce[i * 4],
nonce[i * 4 + 1],
nonce[i * 4 + 2],
nonce[i * 4 + 3],
]);
}
Self { key_words, counter }
}
pub fn apply_keystream(&mut self, data: &mut [u8]) {
if data.is_empty() {
return;
}
let len = data.len();
init_cpu_caps();
unsafe {
ChaCha20_ctr32(
data.as_mut_ptr(),
data.as_mut_ptr(),
len,
self.key_words.as_ptr(),
self.counter.as_mut_ptr(),
)
}
}
pub fn apply_keystream_pure(&mut self, data: &mut [u8]) {
if data.is_empty() {
return;
}
fallback(data, &self.key_words, &mut self.counter);
}
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)
}