openentropy_core/source.rs
1//! Abstract entropy source trait and runtime state.
2//!
3//! Every entropy source implements the [`EntropySource`] trait, which provides
4//! metadata via [`SourceInfo`], availability checking, and raw sample collection.
5
6use std::time::Duration;
7
8/// Category of entropy source based on physical mechanism.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum SourceCategory {
11 /// Thermal noise in circuits/oscillators.
12 Thermal,
13 /// CPU/memory timing jitter.
14 Timing,
15 /// OS scheduler nondeterminism.
16 Scheduling,
17 /// Storage/peripheral latency variance.
18 IO,
19 /// Inter-process/kernel communication jitter.
20 IPC,
21 /// CPU microarchitecture race conditions.
22 Microarch,
23 /// Graphics pipeline nondeterminism.
24 GPU,
25 /// Network timing/signal noise.
26 Network,
27 /// OS counters/state.
28 System,
29 /// Combines multiple sources.
30 Composite,
31 /// Signal processing entropy.
32 Signal,
33 /// Hardware sensor readings.
34 Sensor,
35}
36
37impl std::fmt::Display for SourceCategory {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 match self {
40 Self::Thermal => write!(f, "thermal"),
41 Self::Timing => write!(f, "timing"),
42 Self::Scheduling => write!(f, "scheduling"),
43 Self::IO => write!(f, "io"),
44 Self::IPC => write!(f, "ipc"),
45 Self::Microarch => write!(f, "microarch"),
46 Self::GPU => write!(f, "gpu"),
47 Self::Network => write!(f, "network"),
48 Self::System => write!(f, "system"),
49 Self::Composite => write!(f, "composite"),
50 Self::Signal => write!(f, "signal"),
51 Self::Sensor => write!(f, "sensor"),
52 }
53 }
54}
55
56/// Target platform for an entropy source.
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
58pub enum Platform {
59 /// Works on any platform.
60 Any,
61 /// Requires macOS.
62 MacOS,
63 /// Requires Linux.
64 Linux,
65}
66
67impl std::fmt::Display for Platform {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 match self {
70 Self::Any => write!(f, "any"),
71 Self::MacOS => write!(f, "macos"),
72 Self::Linux => write!(f, "linux"),
73 }
74 }
75}
76
77/// Hardware/software requirement for an entropy source.
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
79pub enum Requirement {
80 /// GPU compute (Metal framework).
81 Metal,
82 /// CoreAudio (AudioUnit/AudioObject).
83 AudioUnit,
84 /// WiFi hardware.
85 Wifi,
86 /// USB subsystem.
87 Usb,
88 /// Camera hardware.
89 Camera,
90 /// Apple Silicon specific features (AMX, etc.).
91 AppleSilicon,
92 /// Bluetooth hardware.
93 Bluetooth,
94 /// IOKit framework.
95 IOKit,
96 /// IOSurface framework.
97 IOSurface,
98 /// Security framework (Keychain).
99 SecurityFramework,
100}
101
102impl std::fmt::Display for Requirement {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 match self {
105 Self::Metal => write!(f, "metal"),
106 Self::AudioUnit => write!(f, "audio_unit"),
107 Self::Wifi => write!(f, "wifi"),
108 Self::Usb => write!(f, "usb"),
109 Self::Camera => write!(f, "camera"),
110 Self::AppleSilicon => write!(f, "apple_silicon"),
111 Self::Bluetooth => write!(f, "bluetooth"),
112 Self::IOKit => write!(f, "iokit"),
113 Self::IOSurface => write!(f, "iosurface"),
114 Self::SecurityFramework => write!(f, "security_framework"),
115 }
116 }
117}
118
119/// Metadata about an entropy source.
120///
121/// Each source declares its name, a human-readable description, a physics
122/// explanation of how it harvests entropy, its category, platform requirements,
123/// and an estimated entropy rate in bits per sample.
124#[derive(Debug, Clone)]
125pub struct SourceInfo {
126 /// Unique identifier (e.g. `"clock_jitter"`).
127 pub name: &'static str,
128 /// One-line human-readable description.
129 pub description: &'static str,
130 /// Physics explanation of the entropy mechanism.
131 pub physics: &'static str,
132 /// Source category for classification.
133 pub category: SourceCategory,
134 /// Target platform.
135 pub platform: Platform,
136 /// Hardware/software requirements beyond the platform.
137 pub requirements: &'static [Requirement],
138 /// Estimated entropy rate in bits per sample.
139 pub entropy_rate_estimate: f64,
140 /// Whether this is a composite source (combines multiple standalone sources).
141 ///
142 /// Composite sources don't measure a single independent entropy domain.
143 /// They combine or interleave other sources. The CLI displays them
144 /// separately from standalone sources.
145 pub composite: bool,
146}
147
148/// Trait that every entropy source must implement.
149pub trait EntropySource: Send + Sync {
150 /// Source metadata.
151 fn info(&self) -> &SourceInfo;
152
153 /// Check if this source can operate on the current machine.
154 fn is_available(&self) -> bool;
155
156 /// Collect raw entropy samples. Returns a `Vec<u8>` of up to `n_samples` bytes.
157 fn collect(&self, n_samples: usize) -> Vec<u8>;
158
159 /// Convenience: name from info.
160 fn name(&self) -> &'static str {
161 self.info().name
162 }
163}
164
165/// Runtime state for a registered source in the pool.
166pub struct SourceState {
167 pub source: Box<dyn EntropySource>,
168 pub weight: f64,
169 pub total_bytes: u64,
170 pub failures: u64,
171 pub last_entropy: f64,
172 pub last_min_entropy: f64,
173 pub last_collect_time: Duration,
174 pub healthy: bool,
175}
176
177impl SourceState {
178 pub fn new(source: Box<dyn EntropySource>, weight: f64) -> Self {
179 Self {
180 source,
181 weight,
182 total_bytes: 0,
183 failures: 0,
184 last_entropy: 0.0,
185 last_min_entropy: 0.0,
186 last_collect_time: Duration::ZERO,
187 healthy: true,
188 }
189 }
190}