diff --git a/crypto-glue/Cargo.toml b/crypto-glue/Cargo.toml index 4f2c34f..f3f2a31 100644 --- a/crypto-glue/Cargo.toml +++ b/crypto-glue/Cargo.toml @@ -17,12 +17,13 @@ x25519-dalek = { version = "1.2.0", features = ["std", "u64_backend"], default-f cfg-if = "1.0" foreign-types = "0.5.0" libc = "0.2" -lazy_static = "^1" +once_cell = "1.18.0" ctor = "^0" -#ed25519-dalek still uses rand_core 0.5.1, and that version is incompatible with 0.6.4, so we need to import and implement both. +#ed25519-dalek still uses rand_core 0.5.1, and that version is incompatible with 0.6.4, so we need to implement both. rand_core_051 = { package = "rand_core", version = "0.5.1" } ffi = { package = "openssl-sys", version = "0.9.80", path = "../openssl-sys" } zssp = { git = "ssh://git@github.com/zerotier/zssp.git", branch = "dev" } +rand_xoshiro = { version = "0.6.0"} [dev-dependencies] hex = "0.4.3" diff --git a/crypto-glue/src/p384.rs b/crypto-glue/src/p384.rs index 5007632..e59ee75 100644 --- a/crypto-glue/src/p384.rs +++ b/crypto-glue/src/p384.rs @@ -11,13 +11,12 @@ use std::os::raw::{c_int, c_ulong, c_void}; use std::sync::Mutex; use std::{mem, ptr}; -use lazy_static::lazy_static; - use crate::error::{cvt, cvt_n, cvt_p, ErrorStack}; use crate::hash::SHA384; use crate::secret::Secret; use crate::secure_eq; +use once_cell::sync::Lazy; use zssp::crypto::p384; pub const P384_PUBLIC_KEY_SIZE: usize = 49; @@ -384,9 +383,7 @@ impl OSSLGroup { } unsafe impl Send for OSSLGroup {} unsafe impl Sync for OSSLGroup {} -lazy_static! { - static ref GROUP_P384: OSSLGroup = unsafe { OSSLGroup::p384() }; -} +static GROUP_P384: Lazy = Lazy::new(||unsafe { OSSLGroup::p384() }); impl p384::P384PublicKey for P384PublicKey { fn from_bytes(raw_key: &[u8; p384::P384_PUBLIC_KEY_SIZE]) -> Option { diff --git a/crypto-glue/src/random.rs b/crypto-glue/src/random.rs index c715346..dfb3d8c 100644 --- a/crypto-glue/src/random.rs +++ b/crypto-glue/src/random.rs @@ -9,52 +9,43 @@ use std::sync::Mutex; use libc::c_int; +use once_cell::unsync::Lazy; +use rand_xoshiro::rand_core::{SeedableRng, RngCore, Error, CryptoRng}; -use crate::error::{cvt, ErrorStack}; - -use zssp::crypto::rand_core; +use crate::error::cvt; /// Fill buffer with cryptographically strong pseudo-random bytes. -fn rand_bytes(buf: &mut [u8]) -> Result<(), ErrorStack> { +#[inline] +pub fn fill_bytes_secure(dest: &mut [u8]) { unsafe { - assert!(buf.len() <= c_int::max_value() as usize); - cvt(ffi::RAND_bytes(buf.as_mut_ptr(), buf.len() as c_int)).map(|_| ()) + debug_assert!(dest.len() <= c_int::max_value() as usize); + cvt(ffi::RAND_bytes(dest.as_mut_ptr(), dest.len() as c_int)).unwrap(); } } + pub fn next_u32_secure() -> u32 { - unsafe { - let mut tmp = [0u32; 1]; - rand_bytes(&mut *(tmp.as_mut_ptr().cast::<[u8; 4]>())).unwrap(); - tmp[0] - } + let mut tmp = [0u8; 4]; + fill_bytes_secure(&mut tmp); + u32::from_ne_bytes(tmp) } pub fn next_u64_secure() -> u64 { - unsafe { - let mut tmp = [0u64; 1]; - rand_bytes(&mut *(tmp.as_mut_ptr().cast::<[u8; 8]>())).unwrap(); - tmp[0] - } + let mut tmp = [0u8; 8]; + fill_bytes_secure(&mut tmp); + u64::from_ne_bytes(tmp) } pub fn next_u128_secure() -> u128 { - unsafe { - let mut tmp = [0u128; 1]; - rand_bytes(&mut *(tmp.as_mut_ptr().cast::<[u8; 16]>())).unwrap(); - tmp[0] - } -} - -#[inline(always)] -pub fn fill_bytes_secure(dest: &mut [u8]) { - rand_bytes(dest).unwrap(); + let mut tmp = [0u8; 16]; + fill_bytes_secure(&mut tmp); + u128::from_ne_bytes(tmp) } #[inline(always)] pub fn get_bytes_secure() -> [u8; COUNT] { let mut tmp = [0u8; COUNT]; - rand_bytes(&mut tmp).unwrap(); + fill_bytes_secure(&mut tmp); tmp } @@ -74,7 +65,7 @@ impl SecureRandom { } } -impl rand_core::RngCore for SecureRandom { +impl RngCore for SecureRandom { #[inline(always)] fn next_u32(&mut self) -> u32 { next_u32_secure() @@ -91,7 +82,7 @@ impl rand_core::RngCore for SecureRandom { } #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { fill_bytes_secure(dest); Ok(()) } @@ -121,69 +112,43 @@ impl rand_core_051::RngCore for SecureRandom { } } -impl rand_core::CryptoRng for SecureRandom {} - +impl CryptoRng for SecureRandom {} impl rand_core_051::CryptoRng for SecureRandom {} - unsafe impl Sync for SecureRandom {} unsafe impl Send for SecureRandom {} -/// xorshift* by Marsaglia. -/// Simple and deterministic which makes it good for testing. -pub struct Xorshift64Star(pub u64); -impl Xorshift64Star { - #[inline(always)] - pub fn new(seed: u64) -> Self { - Self(seed) - } +/// This crate contains the most modern, feature rich and high-quality variants of the Xorshift family of random +/// number generators. +/// While they are not cryptographically secure, they are also faster and several times harder to +/// reverse than Xorshift64, so I think we should prefer them. +/// I read the source of this crate and it is low level and efficient. +pub use rand_xoshiro; +/// Xoshiro256** according to my benchmarking is surprisingly twice as fast as vanilla +/// Xorshift64 because there are fewer dependency chains in Xoshiro256** compared to Xorshift64. +pub use rand_xoshiro::Xoshiro256StarStar; + +/// A global Xoshiro256** wrapped in a mutex and a OnceCell. +/// Unsync OnceCell is just a wrapped `Option<>` and is very fast. +/// Also OnceCell is about to be stabilized into Rust std. +pub static GLOBAL_XORSHIFT: Mutex> = Mutex::new(Lazy::new(|| Xoshiro256StarStar::from_rng(SecureRandom).unwrap())); + +/// Quickly creates a new Xoshiro256StarStar state that is randomly seeded and fully owned by the +/// caller (does not require dereferencing and locking a global variable). +#[inline] +pub fn new_xorshift_rng() -> Xoshiro256StarStar { + let mut state = GLOBAL_XORSHIFT.lock().unwrap(); + let ret = state.clone(); + state.jump(); + ret +} +/// Generate a random 64-bit number (not cryptographically secure). +#[inline] +pub fn next_u64_xorshift() -> u64 { + GLOBAL_XORSHIFT.lock().unwrap().next_u64() } -impl rand_core::RngCore for Xorshift64Star { - #[inline(always)] - fn next_u32(&mut self) -> u32 { - self.next_u64() as u32 - } - #[inline(always)] - fn next_u64(&mut self) -> u64 { - self.0 ^= self.0.wrapping_shr(12); - self.0 ^= self.0.wrapping_shl(25); - self.0 ^= self.0.wrapping_shr(27); - self.0.wrapping_mul(0x2545F4914F6CDD1Du64) - } - - #[inline(always)] - fn fill_bytes(&mut self, dest: &mut [u8]) { - // This could be faster with manual unrolling - let mut r = self.next_u64().to_ne_bytes(); - let mut n = 0; - for byte in dest { - *byte = r[n]; - n += 1; - if n >= 8 { - r = self.next_u64().to_ne_bytes(); - n = 0 - } - } - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { - self.fill_bytes(dest); - Ok(()) - } -} - -/// Get a non-cryptographic random number. -pub fn xorshift64_random() -> u64 { - static XORSHIFT64_STATE: Mutex = Mutex::new(0); - let mut x = XORSHIFT64_STATE.lock().unwrap(); - while *x == 0 { - *x = next_u64_secure(); - } - *x ^= x.wrapping_shr(12); - *x ^= x.wrapping_shl(25); - *x ^= x.wrapping_shr(27); - let r = *x; - drop(x); - r.wrapping_mul(0x2545F4914F6CDD1Du64) +/// Generate a random 32-bit number (not cryptographically secure). +#[inline] +pub fn next_u32_xorshift() -> u32 { + GLOBAL_XORSHIFT.lock().unwrap().next_u32() }