use sha2::{Digest, Sha256};
use std::time::{SystemTime, UNIX_EPOCH};
use uuid::Uuid;
#[derive(Debug)]
pub struct Identity {
id: String,
birth_time: u128,
#[allow(dead_code)]
entropy: [u8; 32],
}
impl Identity {
pub fn create() -> Self {
let birth_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards - ontological violation")
.as_nanos();
let uuid = Uuid::new_v4();
let mut entropy = [0u8; 32];
let uuid_bytes = uuid.as_bytes();
for (i, byte) in uuid_bytes.iter().enumerate() {
entropy[i] = *byte;
entropy[i + 16] = byte.wrapping_add(i as u8);
}
let mut hasher = Sha256::new();
hasher.update(birth_time.to_le_bytes());
hasher.update(&entropy);
hasher.update(uuid_bytes);
let hash = hasher.finalize();
let id = format!("{:x}", hash);
Identity {
id,
birth_time,
entropy,
}
}
pub fn id(&self) -> &str {
&self.id
}
pub fn birth_time(&self) -> u128 {
self.birth_time
}
pub fn is_same_instance(&self, other: &Self) -> bool {
std::ptr::eq(self, other)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn identity_is_unique() {
let id1 = Identity::create();
let id2 = Identity::create();
assert_ne!(id1.id(), id2.id(), "Identities must be unique");
assert_ne!(id1.birth_time(), id2.birth_time(), "Birth times must differ");
}
#[test]
fn identity_cannot_be_cloned() {
let _id = Identity::create();
}
#[test]
fn identity_id_is_stable() {
let id = Identity::create();
let first_read = id.id();
let second_read = id.id();
assert_eq!(first_read, second_read, "Identity must be stable");
}
#[test]
fn identity_hash_is_irreversible() {
let id = Identity::create();
let hash = id.id();
assert_eq!(hash.len(), 64, "Hash must be SHA256");
}
#[test]
fn identity_cannot_be_recreated() {
let id1 = Identity::create();
let hash1 = id1.id();
std::thread::sleep(std::time::Duration::from_nanos(100));
let id2 = Identity::create();
let hash2 = id2.id();
assert_ne!(hash1, hash2);
assert_ne!(id1.birth_time(), id2.birth_time());
}
#[test]
fn identity_cannot_be_assigned() {
let id = Identity::create();
let _other = Identity::create();
let original_hash = id.id();
let same_hash = id.id();
assert_eq!(original_hash, same_hash);
}
}