documentation, cleanup
This commit is contained in:
parent
bdf3e8dde5
commit
8507e0fe5d
3 changed files with 64 additions and 43 deletions
81
README.md
81
README.md
|
|
@ -1,50 +1,57 @@
|
||||||
# ocelli: Camera-Based TRNG
|
# ocelli: Camera-Based TRNG
|
||||||
|
|
||||||
**Ocelli** is a Rust application that generates high-quality entropy using a camera feed. The application supports two entropy generation methods (`chop_and_tack` and `pick_and_flip`) and optionally applies Van Neumann whitening for enhanced randomness. Generated entropy is saved as a binary file.
|
**Ocelli** is a Rust library with FFI bindings that can be used to generate high-quality entropy using a camera feed. The application supports two entropy generation methods (`chop_and_tack` and `pick_and_flip`) and optionally applies Van Neumann whitening for enhanced randomness.
|
||||||
|
|
||||||
## Features
|
### Chop and Tack
|
||||||
|
|
||||||
- **Entropy Methods**:
|
(ported from NoiseBasedCamRng by Andika Wasisto https://github.com/awasisto/camrng)
|
||||||
- `chop_and_tack`: Compares pixel values between frames.
|
|
||||||
- `pick_and_flip`: Processes pixel data and flips bits every second frame.
|
|
||||||
- **Van Neumann Whitening**: Optional, enabled via the `-w` flag.
|
|
||||||
- **Shannon Entropy Test**: Ensures the randomness quality of generated entropy.
|
|
||||||
|
|
||||||
## Usage
|
The algorithm extracts entropy from two arrays with 8 bit integers that can be obtained by taking two consecutive frames from a camera feed and reading their brightness levels. It is required to cover the lens of the camera so it only sees uniform blackness. Due to thermal and quantum effects the image sensor will still sense fluctuations in brightness.
|
||||||
|
|
||||||
|
The outer 100 pixel wide edges of each frames are ignored since they can be prone to bias. Entropy is obtained by comparing the remaining pixel values in a grid pattern 30 pixels apart to avoid correlation.
|
||||||
|
|
||||||
|
### Pick and Flip
|
||||||
|
|
||||||
|
(inspired by R. Li, "A True Random Number Generator algorithm from digital camera image noise for varying lighting conditions," SoutheastCon 2015, Fort Lauderdale, FL, USA, 2015, pp. 1-8, doi: 10.1109/SECON.2015.7132901.)
|
||||||
|
|
||||||
|
The algorithm extracts entropy from an array of 8-bit values (camera frame pixel brightness) by analyzing the least significant bit (LSB) of each value. This process leverages the natural variability in pixel brightness across a frame. Entropy is derived by first examining whether the brightness value of a pixel falls within the range of 2 to 253, to avoid bias. The LSB of qualifying pixel values is then used to form a bitstream. To avoid correlations, the bits of every second array are flipped. The resulting bits are sequentially packed into bytes, forming the output entropy.
|
||||||
|
|
||||||
|
## Main Methods
|
||||||
|
|
||||||
|
* **`chop_and_tack`** takes two 8 bit integer arrays `current` and `previous` representing consecutive grayscale image frames, an usize `width` and an usize `height` of the original image frame dimensions, and a `minimum_distance` usize to define the grid distance between qualifying pixels. It returns an array of random 8 bit chunks.
|
||||||
|
|
||||||
|
* **`pick_and_flip`** takes an 8 bit integer array representing a grayscale image frame and an usize `current_frame_index` representing a frame count of which every even number triggers flipped bits for the frame for the output array of random 8 bit chunks.
|
||||||
|
|
||||||
|
## Helper Methods
|
||||||
|
|
||||||
|
* **`is_covered`** can be used to check if a camera lens is covered (a requirement for the Chop and Tack method to work properly). It takes an 8 bit integer array and an 8 bit integer `threshold` value. It checks how many unique values are present in the array. If the number of unique values lies under the threshold the method returns `true`.
|
||||||
|
|
||||||
|
* **`shannon`** can be used to calculate the Shannon Entropy value for an array of 8 bit integers.
|
||||||
|
|
||||||
|
* **`whiten`** applies Van Neumann whitening to an array of 8 bit values, halving its size but increasing the entropy amount by filtering out bias.
|
||||||
|
|
||||||
|
## Recommended Use
|
||||||
|
|
||||||
|
1. If *Chop and Tack* is to be used, utilize the `is_covered` method with a threshold of 50 to determine if the camera sensor is covered.
|
||||||
|
2. Read the desired amount of frames from the camera and extract the brightness levels as 8 bit integers into arrays.
|
||||||
|
3. Feed the arrays into one of the main methods and make sure to provide all required arguments.
|
||||||
|
4. Whitening can be applied using the `whiten` method to filter out bias and increase the entropy of the result
|
||||||
|
5. It is recommended to check the resulting entropy quality using the `shannon` method and drop the result if it falls below a threshold (e.g. 7.9).
|
||||||
|
6. Loop through the previous steps and accumulate the resulting entropy until the desired amount of random bytes is reached.
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run --release -- <entropy_length_in_bytes> <resolution_width> <resolution_height> [-w]
|
cargo build --release
|
||||||
```
|
```
|
||||||
|
|
||||||
### Example
|
Android
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run --release -- 1 1024 -w
|
cargo ndk -t arm64-v8a -t armeabi-v7a -t x86_64 build --result
|
||||||
```
|
```
|
||||||
|
|
||||||
This generates 1024 bytes of whitened entropy using camera 1.
|
iOS
|
||||||
|
```bash
|
||||||
## Requirements
|
cargo build --release --target aarch64-apple-ios
|
||||||
|
cargo build --release --target x86_64-apple-ios
|
||||||
- OpenCV 4.x
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
1. Install Rust: https://www.rust-lang.org/tools/install
|
|
||||||
2. Install OpenCV: Follow the [official guide](https://docs.opencv.org/).
|
|
||||||
3. Clone the repository:
|
|
||||||
```bash
|
|
||||||
git clone <repository_url>
|
|
||||||
cd ocelli-entropy-generator
|
|
||||||
```
|
|
||||||
4. Run the application:
|
|
||||||
```bash
|
|
||||||
cargo run --release -- <arguments>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Output
|
|
||||||
|
|
||||||
Generated entropy files are saved in the current directory with a name format:
|
|
||||||
```
|
|
||||||
<method>[_whitened]_YYYYMMDD_HHMMSS.bin
|
|
||||||
```
|
```
|
||||||
6
justfile
6
justfile
|
|
@ -1,6 +0,0 @@
|
||||||
diehard 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}}
|
|
||||||
20
src/lib.rs
20
src/lib.rs
|
|
@ -6,6 +6,10 @@ pub struct Ocelli;
|
||||||
impl Ocelli {
|
impl Ocelli {
|
||||||
|
|
||||||
pub fn chop_and_tack(&self, current: &Vec<u8>, previous: &Vec<u8>, width: usize, minimum_distance: usize) -> Vec<u8> {
|
pub fn chop_and_tack(&self, current: &Vec<u8>, previous: &Vec<u8>, width: usize, minimum_distance: usize) -> Vec<u8> {
|
||||||
|
// Extracts entropy from two frames by comparing pixel values in a specific grid pattern.
|
||||||
|
// The resulting entropy is constructed by appending 1s or 0s based on pixel differences.
|
||||||
|
// Algorithm ported from NoiseBasedCamRng by Andika Wasisto https://github.com/awasisto/camrng
|
||||||
|
|
||||||
let mut entropy = Vec::new();
|
let mut entropy = Vec::new();
|
||||||
let mut current_byte = 0u8;
|
let mut current_byte = 0u8;
|
||||||
let mut bit_count = 0;
|
let mut bit_count = 0;
|
||||||
|
|
@ -57,6 +61,11 @@ impl Ocelli {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pick_and_flip(&self, data: &[u8], current_frame_index: usize) -> Vec<u8> {
|
pub fn pick_and_flip(&self, data: &[u8], current_frame_index: usize) -> Vec<u8> {
|
||||||
|
// Extracts the least significant bit (LSB) of each pixel brightness, flipping it based on the frame index.
|
||||||
|
// Generates entropy by combining these bits into bytes.
|
||||||
|
// Algorithm is a simplified version of R. Li, "A True Random Number Generator algorithm from
|
||||||
|
// digital camera image noise for varying lighting conditions," doi: 10.1109/SECON.2015.7132901.
|
||||||
|
|
||||||
let mut entropy = Vec::new();
|
let mut entropy = Vec::new();
|
||||||
let mut current_byte = 0u8;
|
let mut current_byte = 0u8;
|
||||||
let mut bit_count = 0;
|
let mut bit_count = 0;
|
||||||
|
|
@ -84,6 +93,9 @@ impl Ocelli {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shannon(&self, data: &Vec<u8>) -> f64 {
|
pub fn shannon(&self, data: &Vec<u8>) -> f64 {
|
||||||
|
// Calculates the Shannon entropy of a given byte vector to measure its randomness.
|
||||||
|
// Uses a frequency map to compute probabilities and their contributions to entropy.
|
||||||
|
|
||||||
let mut frequency_map = HashMap::new();
|
let mut frequency_map = HashMap::new();
|
||||||
let data_len = data.len();
|
let data_len = data.len();
|
||||||
|
|
||||||
|
|
@ -98,6 +110,9 @@ impl Ocelli {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn whiten(&self, entropy: &[u8]) -> Vec<u8> {
|
pub fn whiten(&self, entropy: &[u8]) -> Vec<u8> {
|
||||||
|
// Applies von Neumann whitening to reduce bias in the input entropy.
|
||||||
|
// Pairs of bits are analyzed, and only unbiased pairs are used to construct the output.
|
||||||
|
|
||||||
let mut whitened_entropy = Vec::new();
|
let mut whitened_entropy = Vec::new();
|
||||||
let mut current_byte = 0u8;
|
let mut current_byte = 0u8;
|
||||||
let mut bit_count = 0;
|
let mut bit_count = 0;
|
||||||
|
|
@ -131,6 +146,11 @@ impl Ocelli {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_covered(&self, grayscale: &[u8], threshold: usize) -> bool {
|
pub fn is_covered(&self, grayscale: &[u8], threshold: usize) -> bool {
|
||||||
|
// Checks if the grayscale image contains fewer unique values than the specified threshold.
|
||||||
|
// Useful for ensuring the chop and tack method only sees noise and no image data, resulting
|
||||||
|
// in higher quality entropy.
|
||||||
|
// Recommended default threshold is 50
|
||||||
|
|
||||||
let unique_values: HashSet<_> = grayscale.iter().copied().collect();
|
let unique_values: HashSet<_> = grayscale.iter().copied().collect();
|
||||||
// println!("UV: {:?}", unique_values);
|
// println!("UV: {:?}", unique_values);
|
||||||
unique_values.len() < threshold
|
unique_values.len() < threshold
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue