#[cfg(not(target_env = "sgx"))]
use std::process;
use std::{
hash::Hash,
hint, ptr,
sync::atomic::{AtomicU64, Ordering},
thread,
time::{Instant, SystemTime, UNIX_EPOCH},
};
use lexe_byte_array::ByteArray;
use lexe_sha256::sha256;
use lexe_std::array;
use zeroize::Zeroizing;
use crate::rng::{Crng, RngExt};
pub fn gen_seed(rng: &mut impl Crng) -> [u8; 32] {
const DOMAIN_SEPARATOR: [u8; 32] = array::pad(*b"LEXE-REALM::gen_seed");
let mut ctx = sha256::Context::new();
ctx.update(&DOMAIN_SEPARATOR);
source::unix_timestamp_ns(&mut ctx);
source::monotonic_time(&mut ctx);
source::rng_bytes(&mut ctx, rng);
source::counter(&mut ctx);
source::code_addr(&mut ctx);
source::static_addr(&mut ctx);
source::stack_addr(&mut ctx);
source::heap_addr(&mut ctx, rng);
source::thread_local_addr(&mut ctx);
#[cfg(not(target_env = "sgx"))]
source::process_id(&mut ctx);
source::thread_id(&mut ctx);
source::unix_timestamp_ns(&mut ctx);
source::monotonic_time(&mut ctx);
ctx.finish().to_array()
}
mod source {
use super::*;
pub fn rng_bytes(ctx: &mut sha256::Context, rng: &mut impl Crng) {
let mut bytes = Zeroizing::new([0u8; 256]);
rng.fill_bytes(&mut *bytes);
ctx.update(bytes.as_slice());
}
pub fn unix_timestamp_ns(ctx: &mut sha256::Context) {
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_nanos())
.unwrap_or(0);
ctx.update(×tamp.to_ne_bytes());
}
pub fn monotonic_time(ctx: &mut sha256::Context) {
Instant::now().hash(ctx);
}
pub fn counter(ctx: &mut sha256::Context) {
static COUNTER: AtomicU64 = AtomicU64::new(0);
let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
ctx.update(&counter.to_ne_bytes());
}
pub fn code_addr(ctx: &mut sha256::Context) {
let addr = (code_addr as *const ()).addr();
ctx.update(&addr.to_ne_bytes());
}
pub fn static_addr(ctx: &mut sha256::Context) {
static VALUE: u8 = 0;
let addr = ptr::from_ref(hint::black_box(&VALUE)).addr();
ctx.update(&addr.to_ne_bytes());
}
pub fn stack_addr(ctx: &mut sha256::Context) {
let value = 0u8;
let addr = ptr::from_ref(hint::black_box(&value)).addr();
ctx.update(&addr.to_ne_bytes());
}
pub fn heap_addr(ctx: &mut sha256::Context, rng: &mut impl Crng) {
let len = rng.gen_range_u32(1..1025) as usize;
let value = hint::black_box(Vec::<u8>::with_capacity(len));
let addr = value.as_ptr().addr();
ctx.update(&addr.to_ne_bytes());
}
pub fn thread_local_addr(ctx: &mut sha256::Context) {
thread_local! {
#[allow(clippy::missing_const_for_thread_local)]
static VALUE: u8 = const { 0 };
}
VALUE.with(|value| {
let addr = ptr::from_ref(hint::black_box(value)).addr();
ctx.update(&addr.to_ne_bytes());
});
}
#[cfg(not(target_env = "sgx"))]
pub fn process_id(ctx: &mut sha256::Context) {
ctx.update(&process::id().to_ne_bytes());
}
pub fn thread_id(ctx: &mut sha256::Context) {
thread::current().id().hash(ctx);
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::rng::SysRng;
#[test]
fn test_gen_seed() {
let mut rng = SysRng::new();
assert_ne!(gen_seed(&mut rng), gen_seed(&mut rng));
}
#[test]
fn test_sources() {
let sources: &[fn(&mut sha256::Context)] = &[
source::unix_timestamp_ns,
source::monotonic_time,
source::counter,
source::code_addr,
source::static_addr,
source::stack_addr,
|ctx| source::heap_addr(ctx, &mut SysRng::new()),
source::thread_local_addr,
#[cfg(not(target_env = "sgx"))]
source::process_id,
source::thread_id,
];
let empty = sha256::Context::new().finish();
for &source in sources {
assert_ne!(digest_source(source), empty);
}
}
#[test]
fn test_counter() {
assert_ne!(
digest_source(source::counter),
digest_source(source::counter)
);
}
fn digest_source(source: fn(&mut sha256::Context)) -> sha256::Hash {
let mut ctx = sha256::Context::new();
source(&mut ctx);
ctx.finish()
}
}