From bdf3e8dde580450b2190ae13b0768717af3684ea Mon Sep 17 00:00:00 2001 From: randogoth Date: Tue, 31 Dec 2024 23:37:57 +0200 Subject: [PATCH] FFI --- Cargo.toml | 4 +- justfile | 6 +- src/bin/main.rs | 146 ------------------------------------------------ src/lib.rs | 80 ++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 150 deletions(-) delete mode 100644 src/bin/main.rs diff --git a/Cargo.toml b/Cargo.toml index bf62e94..8f24fec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,8 +6,6 @@ edition = "2021" [lib] name = "ocelli" path = "src/lib.rs" +crate-type = ["cdylib"] [dependencies] -v4l = "0.14" -chrono = "0.4" -image = "0.25.5" diff --git a/justfile b/justfile index 987eabc..98e6851 100644 --- a/justfile +++ b/justfile @@ -1,2 +1,6 @@ diehard file: - dieharder -a -g 201 -f {{file}} \ No newline at end of file + dieharder -a -g 201 -f {{file}} + +build arg: + cargo build {{arg}} + cargo ndk -t arm64-v8a -t armeabi-v7a -t x86_64 build {{arg}} diff --git a/src/bin/main.rs b/src/bin/main.rs deleted file mode 100644 index a71386c..0000000 --- a/src/bin/main.rs +++ /dev/null @@ -1,146 +0,0 @@ -use ocelli::Ocelli; -use v4l::prelude::*; -use v4l::video::Capture; -use v4l::buffer::Type; -use v4l::format::FourCC; -use v4l::io::traits::CaptureStream; -use image::ImageReader; -use std::fs::File; -use std::io::{stdin, Write}; -use chrono::Local; -use std::time::Instant; - -fn frame_to_grayscale(data: &[u8]) -> Vec { - let img = ImageReader::new(std::io::Cursor::new(data)) - .with_guessed_format() - .expect("Failed to guess format") - .decode() - .expect("Failed to decode image"); - let gray = img.into_luma8(); // Convert to grayscale - gray.into_raw() // Return raw pixel data as Vec -} - -fn main() -> Result<(), Box> { - let args: Vec = std::env::args().collect(); - if args.len() < 3 { - eprintln!("Usage: {} ", args[0]); - std::process::exit(1); - } - - // Check if the quick flag is set - let quick = args.contains(&String::from("-q")); - - let camera_index: usize = args[1].parse().expect("Failed to parse camera index as a number"); - let length: usize = args[2].parse().expect("Failed to parse entropy length as a number"); - - let dev = Device::new(camera_index).expect("Failed to open camera"); - - // Set the desired format and resolution - let mut format = dev.format().expect("Failed to get camera format"); - format.fourcc = FourCC::new(b"MJPG"); // Use MJPG for higher resolutions - format.width = 1920; - format.height = 1080; - - if let Err(_) = dev.set_format(&format) { - println!("Failed to set resolution to 1920x1080. Falling back to 1280x720."); - format.width = 1280; - format.height = 720; - dev.set_format(&format) - .expect("Failed to set resolution to 1280x720"); - } - - println!( - "Using resolution: {}x{} (FourCC: {})", - format.width, format.height, format.fourcc - ); - - let mut stream = MmapStream::with_buffers(&dev, Type::VideoCapture, 4) - .expect("Failed to create stream"); - - let ocelli = Ocelli; - let mut total_entropy = Vec::new(); - let start_time = Instant::now(); - let shannon_threshold = 7.9; - let mut frame_count = 0; - - while total_entropy.len() < length { - // Capture first frame - let (data1, _) = stream.next().expect("Failed to capture frame"); - - // Skip the first 30 frames - if frame_count <= 30 { - frame_count += 1; - } else { - - let grayscale_data1 = frame_to_grayscale(&data1); - - let mut entropy: Vec = [0].to_vec(); - - if quick { - // Quicker capture using Pick and Flip - entropy = ocelli.whiten(&ocelli.pick_and_flip(&grayscale_data1, frame_count as usize)); - } else { - if ocelli.is_covered(&grayscale_data1, 50) { - // Capture second frame - let (data2, _) = stream.next().expect("Failed to capture second frame"); - let grayscale_data2 = frame_to_grayscale(&data2); - - // Generate entropy using chop_and_tack - entropy = ocelli.chop_and_tack(&grayscale_data1, &grayscale_data2, format.width as usize, 30); - } else { - println!("Camera is not covered. Please cover the camera."); - frame_count = 0; - } - } - - let shannon_entropy = ocelli.shannon(&entropy); - - if shannon_entropy >= shannon_threshold { - total_entropy.extend(entropy); - println!( - "Collected {} of {} bytes of entropy (Shannon entropy: {:.3})", - total_entropy.len(), - length, - shannon_entropy - ); - } else { - println!("Rejected entropy for frame {} (Shannon entropy: {:.3})", frame_count, shannon_entropy); - } - - } - } - - // Trim total_entropy to the exact length - total_entropy.truncate(length); - - // Print elapsed time - let elapsed_time = start_time.elapsed(); - println!( - "Process completed in {:.3} seconds.", - elapsed_time.as_secs_f64() - ); - - // Final Shannon entropy test - let final_shannon_entropy = ocelli.shannon(&total_entropy); - println!("Final Shannon entropy: {:.3}", final_shannon_entropy); - - // Ask if the result should be saved - println!("Save result to a file or print as hex string? (file/print):"); - let mut input = String::new(); - stdin().read_line(&mut input).expect("Failed to read input"); - - if input.trim().eq_ignore_ascii_case("file") { - let timestamp = Local::now().format("%Y%m%d_%H%M%S").to_string(); - let filename = format!("entropy_{}.bin", timestamp); - - let mut file = File::create(&filename).expect("Failed to create file"); - file.write_all(&total_entropy).expect("Failed to write data to file"); - - println!("Entropy saved to file: {}", filename); - } else if input.trim().eq_ignore_ascii_case("print") { - let entropy_hex: String = total_entropy.iter().map(|b| format!("{:02x}", b)).collect(); - println!("Generated entropy (hex): {}", entropy_hex); - } - - Ok(()) -} diff --git a/src/lib.rs b/src/lib.rs index c90df95..b6be47b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ use std::collections::{HashMap, HashSet}; +use std::slice; pub struct Ocelli; @@ -135,3 +136,82 @@ impl Ocelli { unique_values.len() < threshold } } + +#[no_mangle] +pub extern "C" fn chop_and_tack( + current_ptr: *const u8, + current_len: usize, + previous_ptr: *const u8, + previous_len: usize, + width: usize, + minimum_distance: usize, + result_ptr: *mut u8, + result_len: &mut usize, +) { + let current = unsafe { slice::from_raw_parts(current_ptr, current_len) }; + let previous = unsafe { slice::from_raw_parts(previous_ptr, previous_len) }; + + let ocelli = Ocelli; + let result = ocelli.chop_and_tack(¤t.to_vec(), &previous.to_vec(), width, minimum_distance); + + unsafe { + let result_slice = slice::from_raw_parts_mut(result_ptr, result.len()); + result_slice.copy_from_slice(&result); + *result_len = result.len(); + } +} + +#[no_mangle] +pub extern "C" fn pick_and_flip( + data_ptr: *const u8, + data_len: usize, + current_frame_index: usize, + result_ptr: *mut u8, + result_len: &mut usize, +) { + let data = unsafe { slice::from_raw_parts(data_ptr, data_len) }; + + let ocelli = Ocelli; + let result = ocelli.pick_and_flip(data, current_frame_index); + + unsafe { + let result_slice = slice::from_raw_parts_mut(result_ptr, result.len()); + result_slice.copy_from_slice(&result); + *result_len = result.len(); + } +} + +#[no_mangle] +pub extern "C" fn shannon(data_ptr: *const u8, data_len: usize) -> f64 { + let data = unsafe { slice::from_raw_parts(data_ptr, data_len) }; + + let ocelli = Ocelli; + ocelli.shannon(&data.to_vec()) +} + +#[no_mangle] +pub extern "C" fn whiten( + entropy_ptr: *const u8, + entropy_len: usize, + result_ptr: *mut u8, + result_len: &mut usize, +) { + let entropy = unsafe { slice::from_raw_parts(entropy_ptr, entropy_len) }; + + let ocelli = Ocelli; + let result = ocelli.whiten(entropy); + + unsafe { + let result_slice = slice::from_raw_parts_mut(result_ptr, result.len()); + result_slice.copy_from_slice(&result); + *result_len = result.len(); + } +} + +#[no_mangle] +pub extern "C" fn is_covered(grayscale_ptr: *const u8, grayscale_len: usize, threshold: usize) -> bool { + let grayscale = unsafe { slice::from_raw_parts(grayscale_ptr, grayscale_len) }; + + let ocelli = Ocelli; + ocelli.is_covered(grayscale, threshold) +}