#![no_std]
#![cfg_attr(feature = "ysc1_simd", feature(portable_simd))]
#![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"
)]
pub use cipher;
use crate::core::Ysc1Core;
mod arx;
mod backends;
mod core;
pub trait Ysc1Variant {
type KeySize: cipher::ArrayLength<u8>;
const KEY_SIZE: usize;
type NonceSize: cipher::ArrayLength<u8>;
const NONCE_SIZE: usize;
const INIT_ROUNDS: usize;
const KEYSTREAM_ROUNDS: usize;
}
#[derive(Clone)]
pub struct Ysc1_512;
impl Ysc1Variant for Ysc1_512 {
type KeySize = cipher::consts::U64;
type NonceSize = cipher::consts::U64;
const INIT_ROUNDS: usize = 16;
const KEYSTREAM_ROUNDS: usize = 1;
const KEY_SIZE: usize = 64;
const NONCE_SIZE: usize = 64;
}
#[derive(Clone)]
pub struct Ysc1_1024;
impl Ysc1Variant for Ysc1_1024 {
type KeySize = cipher::consts::U128;
type NonceSize = cipher::consts::U64;
const INIT_ROUNDS: usize = 20;
const KEYSTREAM_ROUNDS: usize = 1;
const KEY_SIZE: usize = 128;
const NONCE_SIZE: usize = 64;
}
pub type Ysc1_512Cipher = cipher::StreamCipherCoreWrapper<Ysc1Core<Ysc1_512>>;
pub type Ysc1_1024Cipher = cipher::StreamCipherCoreWrapper<Ysc1Core<Ysc1_1024>>;
#[cfg(test)]
mod tests {
use super::{Ysc1_1024Cipher, Ysc1_512Cipher};
use cipher::{KeyIvInit, StreamCipher, StreamCipherSeek};
const PLAINTEXT: &[u8] = b"This is a test message for the YSC1 stream cipher implementation.";
#[test]
fn ysc1_512_encrypt_decrypt() {
let key = [0x01; 64].into();
let nonce = [0x02; 64].into();
let mut buffer = PLAINTEXT.to_vec();
let mut cipher = Ysc1_512Cipher::new(&key, &nonce);
cipher.apply_keystream(&mut buffer);
assert_ne!(
buffer, PLAINTEXT,
"Ciphertext should not be the same as plaintext"
);
let mut cipher = Ysc1_512Cipher::new(&key, &nonce);
cipher.apply_keystream(&mut buffer);
assert_eq!(
buffer, PLAINTEXT,
"Decrypted text should match the original plaintext"
);
}
#[test]
fn ysc1_1024_seek_and_consistency() {
let key = [0x03; 128].into();
let nonce = [0x04; 64].into();
let mut buffer1 = [0u8; 128];
let mut buffer2 = [0u8; 128];
let mut cipher1 = Ysc1_1024Cipher::new(&key, &nonce);
cipher1.apply_keystream(&mut buffer1);
let mut cipher2 = Ysc1_1024Cipher::new(&key, &nonce);
cipher2.seek(64); cipher2.apply_keystream(&mut buffer2[64..]);
assert_eq!(
buffer1[64..],
buffer2[64..],
"Keystream from sought position should match"
);
}
#[test]
#[cfg(feature = "ysc1_simd")]
fn ysc1_simd_vs_soft_consistency() {
use crate::core::Ysc1Core;
use crate::{Ysc1_512, backends};
let key = [0xAB; 64].into();
let nonce = [0xCD; 64].into();
let mut soft_cipher = Ysc1_512_Cipher::new(&key, &nonce);
let mut soft_keystream = vec![0u8; 256];
soft_cipher.apply_keystream(&mut soft_keystream);
let mut simd_core = Ysc1Core::<Ysc1_512>::new(&key, &nonce);
let mut simd_backend = backends::simd::Backend(&mut simd_core);
let mut simd_keystream = vec![0u8; 256];
for chunk in simd_keystream.chunks_mut(64) {
cipher::StreamCipherBackend::gen_ks_block(&mut simd_backend, chunk.into());
}
assert_eq!(soft_keystream, simd_keystream, "SIMD and Soft backends must produce identical keystreams");
}
}