#![no_std]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![warn(missing_docs, rust_2018_idioms, trivial_casts, unused_qualifications)]
pub use cipher;
use cipher::{
Block, BlockSizeUser, IvSizeUser, KeyIvInit, KeySizeUser, StreamCipherClosure,
StreamCipherCore, StreamCipherCoreWrapper, StreamCipherSeekCore,
array::{Array, typenum::Unsigned},
consts::{U4, U6, U8, U10, U24, U32, U64},
};
use core::marker::PhantomData;
#[cfg(feature = "zeroize")]
use cipher::zeroize::{Zeroize, ZeroizeOnDrop};
mod backends;
mod xsalsa;
pub use xsalsa::{XSalsa8, XSalsa12, XSalsa20, XSalsaCore, hsalsa};
pub type Salsa8 = StreamCipherCoreWrapper<SalsaCore<U4>>;
pub type Salsa12 = StreamCipherCoreWrapper<SalsaCore<U6>>;
pub type Salsa20 = StreamCipherCoreWrapper<SalsaCore<U10>>;
pub type Key = Array<u8, U32>;
pub type Nonce = Array<u8, U8>;
pub type XNonce = Array<u8, U24>;
const STATE_WORDS: usize = 16;
const CONSTANTS: [u32; 4] = [0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574];
pub struct SalsaCore<R: Unsigned> {
state: [u32; STATE_WORDS],
rounds: PhantomData<R>,
}
impl<R: Unsigned> SalsaCore<R> {
pub fn from_raw_state(state: [u32; STATE_WORDS]) -> Self {
Self {
state,
rounds: PhantomData,
}
}
}
impl<R: Unsigned> KeySizeUser for SalsaCore<R> {
type KeySize = U32;
}
impl<R: Unsigned> IvSizeUser for SalsaCore<R> {
type IvSize = U8;
}
impl<R: Unsigned> BlockSizeUser for SalsaCore<R> {
type BlockSize = U64;
}
impl<R: Unsigned> KeyIvInit for SalsaCore<R> {
fn new(key: &Key, iv: &Nonce) -> Self {
let mut state = [0u32; STATE_WORDS];
state[0] = CONSTANTS[0];
for (i, chunk) in key[..16].chunks(4).enumerate() {
state[1 + i] = u32::from_le_bytes(chunk.try_into().unwrap());
}
state[5] = CONSTANTS[1];
for (i, chunk) in iv.chunks(4).enumerate() {
state[6 + i] = u32::from_le_bytes(chunk.try_into().unwrap());
}
state[8] = 0;
state[9] = 0;
state[10] = CONSTANTS[2];
for (i, chunk) in key[16..].chunks(4).enumerate() {
state[11 + i] = u32::from_le_bytes(chunk.try_into().unwrap());
}
state[15] = CONSTANTS[3];
Self {
state,
rounds: PhantomData,
}
}
}
impl<R: Unsigned> StreamCipherCore for SalsaCore<R> {
#[inline(always)]
fn remaining_blocks(&self) -> Option<usize> {
let rem = u64::MAX - self.get_block_pos();
rem.try_into().ok()
}
fn process_with_backend(&mut self, f: impl StreamCipherClosure<BlockSize = Self::BlockSize>) {
f.call(&mut backends::soft::Backend(self));
}
}
impl<R: Unsigned> StreamCipherSeekCore for SalsaCore<R> {
type Counter = u64;
#[inline(always)]
fn get_block_pos(&self) -> u64 {
(self.state[8] as u64) + ((self.state[9] as u64) << 32)
}
#[inline(always)]
fn set_block_pos(&mut self, pos: u64) {
self.state[8] = (pos & 0xffff_ffff) as u32;
self.state[9] = ((pos >> 32) & 0xffff_ffff) as u32;
}
}
#[cfg(feature = "zeroize")]
impl<R: Unsigned> Drop for SalsaCore<R> {
fn drop(&mut self) {
self.state.zeroize();
}
}
#[cfg(feature = "zeroize")]
impl<R: Unsigned> ZeroizeOnDrop for SalsaCore<R> {}