1use std::time::Duration;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum SourceCategory {
11 Thermal,
13 Timing,
15 Scheduling,
17 IO,
19 IPC,
21 Microarch,
23 GPU,
25 Network,
27 System,
29 Signal,
31 Sensor,
33 Quantum,
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::Signal => write!(f, "signal"),
50 Self::Sensor => write!(f, "sensor"),
51 Self::Quantum => write!(f, "quantum"),
52 }
53 }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
58pub enum Platform {
59 Any,
61 MacOS,
63 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
79pub enum Requirement {
80 Metal,
82 AudioUnit,
84 Wifi,
86 Usb,
88 Camera,
90 AppleSilicon,
92 Bluetooth,
94 IOKit,
96 IOSurface,
98 SecurityFramework,
100 RawBlockDevice,
102 QCicada,
104}
105
106impl std::fmt::Display for Requirement {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 match self {
109 Self::Metal => write!(f, "metal"),
110 Self::AudioUnit => write!(f, "audio_unit"),
111 Self::Wifi => write!(f, "wifi"),
112 Self::Usb => write!(f, "usb"),
113 Self::Camera => write!(f, "camera"),
114 Self::AppleSilicon => write!(f, "apple_silicon"),
115 Self::Bluetooth => write!(f, "bluetooth"),
116 Self::IOKit => write!(f, "iokit"),
117 Self::IOSurface => write!(f, "iosurface"),
118 Self::SecurityFramework => write!(f, "security_framework"),
119 Self::RawBlockDevice => write!(f, "raw_block_device"),
120 Self::QCicada => write!(f, "qcicada"),
121 }
122 }
123}
124
125impl Requirement {
126 pub fn icon(&self) -> &'static str {
128 match self {
129 Self::QCicada => "🔮",
130 Self::Camera => "📷",
131 Self::AudioUnit => "🎤",
132 Self::Metal => "🎮",
133 Self::Wifi => "📶",
134 Self::Bluetooth => "📡",
135 Self::Usb => "🔌",
136 Self::RawBlockDevice => "💾",
137 _ => "", }
139 }
140
141 pub fn label(&self) -> &'static str {
143 match self {
144 Self::QCicada => "QCicada QRNG (USB)",
145 Self::Camera => "Camera",
146 Self::AudioUnit => "Microphone (CoreAudio)",
147 Self::Metal => "GPU (Metal)",
148 Self::Wifi => "WiFi adapter",
149 Self::Bluetooth => "Bluetooth",
150 Self::Usb => "USB subsystem",
151 Self::RawBlockDevice => "Raw block device",
152 Self::AppleSilicon => "Apple Silicon",
153 Self::IOKit => "IOKit",
154 Self::IOSurface => "IOSurface",
155 Self::SecurityFramework => "Security Framework",
156 }
157 }
158
159 pub fn from_display_name(name: &str) -> Option<Self> {
161 match name {
162 "metal" => Some(Self::Metal),
163 "audio_unit" => Some(Self::AudioUnit),
164 "wifi" => Some(Self::Wifi),
165 "usb" => Some(Self::Usb),
166 "camera" => Some(Self::Camera),
167 "apple_silicon" => Some(Self::AppleSilicon),
168 "bluetooth" => Some(Self::Bluetooth),
169 "iokit" => Some(Self::IOKit),
170 "iosurface" => Some(Self::IOSurface),
171 "security_framework" => Some(Self::SecurityFramework),
172 "raw_block_device" => Some(Self::RawBlockDevice),
173 "qcicada" => Some(Self::QCicada),
174 _ => None,
175 }
176 }
177
178 pub fn icon_for_display_name(name: &str) -> &'static str {
183 Self::from_display_name(name).map_or("", |r| r.icon())
184 }
185
186 pub fn label_for_display_name(name: &str) -> &'static str {
191 Self::from_display_name(name).map_or("Unknown", |r| r.label())
192 }
193}
194
195pub fn best_icon(requirements: &[Requirement]) -> &'static str {
197 requirements
198 .iter()
199 .map(Requirement::icon)
200 .find(|icon| !icon.is_empty())
201 .unwrap_or("")
202}
203
204pub fn best_icon_from_names(names: &[String]) -> &'static str {
206 names
207 .iter()
208 .map(|n| Requirement::icon_for_display_name(n))
209 .find(|icon| !icon.is_empty())
210 .unwrap_or("")
211}
212
213#[derive(Debug, Clone)]
219pub struct SourceInfo {
220 pub name: &'static str,
222 pub description: &'static str,
224 pub physics: &'static str,
226 pub category: SourceCategory,
228 pub platform: Platform,
230 pub requirements: &'static [Requirement],
232 pub entropy_rate_estimate: f64,
234 pub composite: bool,
240 pub is_fast: bool,
242}
243
244pub trait EntropySource: Send + Sync {
246 fn info(&self) -> &SourceInfo;
248
249 fn is_available(&self) -> bool;
251
252 fn collect(&self, n_samples: usize) -> Vec<u8>;
254
255 fn name(&self) -> &'static str {
257 self.info().name
258 }
259
260 fn set_config(&self, _key: &str, _value: &str) -> Result<(), String> {
262 Err("source does not support runtime configuration".into())
263 }
264
265 fn config_options(&self) -> Vec<(&'static str, String)> {
267 vec![]
268 }
269}
270
271pub struct SourceState {
273 pub source: std::sync::Arc<dyn EntropySource>,
274 pub total_bytes: u64,
275 pub failures: u64,
276 pub last_entropy: f64,
277 pub last_min_entropy: f64,
278 pub last_autocorrelation: f64,
279 pub last_collect_time: Duration,
280 pub healthy: bool,
281}
282
283impl SourceState {
284 pub fn new(source: Box<dyn EntropySource>) -> Self {
285 Self {
286 source: std::sync::Arc::from(source),
287 total_bytes: 0,
288 failures: 0,
289 last_entropy: 0.0,
290 last_min_entropy: 0.0,
291 last_autocorrelation: 0.0,
292 last_collect_time: Duration::ZERO,
293 healthy: true,
294 }
295 }
296}