]> git.nega.tv - josh/narcissus/commitdiff
narcissus-core: Add platform secure random API
authorJosh Simmons <josh@nega.tv>
Mon, 2 Dec 2024 21:45:10 +0000 (22:45 +0100)
committerJosh Simmons <josh@nega.tv>
Sat, 5 Apr 2025 12:33:19 +0000 (14:33 +0200)
engine/narcissus-core/src/crypto_random.rs [new file with mode: 0644]
engine/narcissus-core/src/lib.rs
engine/narcissus-core/src/libc.rs

diff --git a/engine/narcissus-core/src/crypto_random.rs b/engine/narcissus-core/src/crypto_random.rs
new file mode 100644 (file)
index 0000000..b0df874
--- /dev/null
@@ -0,0 +1,60 @@
+use std::ffi::c_void;
+
+use crate::{errno, libc};
+
+/// Fill `bytes` with output from the system's cryptographically secure PRNG.
+///
+/// Linux
+/// ---
+///
+/// Utilizes libc's `getrandom` API in blocking mode.
+pub fn fill_random(bytes: &mut [u8]) {
+    #[cfg(not(target_os = "linux"))]
+    const _: () = panic!("unsupported os");
+
+    #[cfg(target_os = "linux")]
+    unsafe {
+        loop {
+            let res = libc::getrandom(bytes.as_mut_ptr() as *mut c_void, bytes.len(), 0);
+            if res == bytes.len() as isize {
+                break;
+            }
+
+            if res < 0 && errno::errno().0 != errno::unix::EINTR {
+                panic!();
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::fill_random;
+
+    #[test]
+    fn generate_random_small() {
+        let mut data_0 = [0; 256];
+        let mut data_1 = [0; 256];
+
+        fill_random(&mut data_0);
+        fill_random(&mut data_1);
+
+        assert!(!data_0.iter().all(|&byte| byte == 0));
+        assert!(!data_1.iter().all(|&byte| byte == 0));
+        assert_ne!(data_0, data_1);
+    }
+
+    #[test]
+    fn generate_random_large() {
+        const MIB: usize = 1024 * 1024;
+        let mut data_0 = vec![0; 64 * MIB];
+        let mut data_1 = vec![0; 64 * MIB];
+
+        fill_random(&mut data_0);
+        fill_random(&mut data_1);
+
+        assert!(!data_0.iter().all(|&byte| byte == 0));
+        assert!(!data_1.iter().all(|&byte| byte == 0));
+        assert_ne!(data_0, data_1);
+    }
+}
index ce07276e04836c92ae2668a91cde997131fe0ec8..30ad466c5295e5fcd2063636c998059bcbde6c64 100644 (file)
@@ -1,5 +1,6 @@
 mod arena;
 mod bitset;
+pub mod crypto_random;
 pub mod dds;
 mod directory;
 pub mod errno;
index d963b91047233aecf2e61dba68c2e3bc7336da4c..24ec0817a38a66a388c541c70de0dabe86f5a522 100644 (file)
@@ -2,7 +2,10 @@
 #![allow(non_upper_case_globals)]
 #![allow(unused)]
 
-use std::os::raw::{c_int, c_long, c_void};
+use std::{
+    ffi::c_uint,
+    os::raw::{c_int, c_long, c_void},
+};
 
 pub type size_t = usize;
 pub type ptrdiff_t = isize;
@@ -443,4 +446,6 @@ extern "C" {
 
     #[cfg_attr(target_os = "linux", link_name = "__errno_location")]
     pub fn errno_location() -> *mut c_int;
+
+    pub fn getrandom(buf: *mut c_void, buf_len: size_t, flags: c_uint) -> isize;
 }