use std::time::Instant;
use crate::source::{EntropySource, Platform, SourceCategory, SourceInfo};
use crate::sources::helpers::extract_timing_entropy;
static PE_CORE_INFO: SourceInfo = SourceInfo {
name: "pe_core_arithmetic",
description: "P-core/E-core migration timing entropy from arithmetic loop jitter",
physics: "Times a fixed-iteration arithmetic loop (LCG multiply-add). On Apple Silicon, \
the kernel migrates threads between high-performance P-cores and \
high-efficiency E-cores based on thermal state, QoS, and load. Migration \
events cause 2-4x timing jumps. Even without migration, each cluster's \
independent DVFS controller adjusts frequency based on die temperature, \
making subsequent runs of the same loop non-deterministic. Measured Shannon \
entropy: 6.35+ bits/byte in low 8 bits of timing deltas. LSB bias ~0.499, \
transitions ~49.5% — near-ideal entropy at the bit level. No special \
hardware access or permissions required.",
category: SourceCategory::Scheduling,
platform: Platform::Any,
requirements: &[],
entropy_rate_estimate: 6.0,
composite: false,
is_fast: false,
};
const LCG_ITERS: u64 = 4096;
const LCG_MUL: u64 = 6364136223846793005;
const LCG_ADD: u64 = 1442695040888963407;
pub struct PECoreArithmeticSource;
impl EntropySource for PECoreArithmeticSource {
fn info(&self) -> &SourceInfo {
&PE_CORE_INFO
}
fn is_available(&self) -> bool {
true
}
fn collect(&self, n_samples: usize) -> Vec<u8> {
let raw_count = n_samples * 4 + 64;
let mut timings: Vec<u64> = Vec::with_capacity(raw_count);
let mut sink: u64 = 0xDEAD_BEEF_CAFE_BABE;
for _ in 0..16 {
for _ in 0..LCG_ITERS {
sink = sink.wrapping_mul(LCG_MUL).wrapping_add(LCG_ADD);
}
}
std::hint::black_box(sink);
for _ in 0..raw_count {
let t0 = Instant::now();
let mut x: u64 = sink;
for _ in 0..LCG_ITERS {
x = x.wrapping_mul(LCG_MUL).wrapping_add(LCG_ADD);
}
sink = std::hint::black_box(x);
let elapsed = t0.elapsed().as_nanos() as u64;
timings.push(elapsed);
if timings.len().is_multiple_of(64) {
std::thread::yield_now();
}
}
std::hint::black_box(sink);
extract_timing_entropy(&timings, n_samples)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn info() {
let src = PECoreArithmeticSource;
assert_eq!(src.info().name, "pe_core_arithmetic");
assert!(matches!(src.info().category, SourceCategory::Scheduling));
assert!(!src.info().composite);
}
#[test]
fn is_available() {
assert!(PECoreArithmeticSource.is_available());
}
#[test]
#[ignore] fn collects_bytes() {
let src = PECoreArithmeticSource;
let data = src.collect(64);
assert!(!data.is_empty());
assert!(data.len() <= 64);
let unique: std::collections::HashSet<u8> = data.iter().copied().collect();
assert!(
unique.len() > 4,
"expected high byte diversity from PE core timing"
);
}
}