use ring::rand::{SecureRandom, SystemRandom};
#[derive(Debug)]
pub enum GeneratedParameterError {
GenerationUnavailable,
Construction(saddle_db::internal::ParameterConstructionError),
}
pub struct ParameterFactory<'a> {
pub(crate) request: &'a mut crate::database_capability::DatabaseRequest,
}
impl ParameterFactory<'_> {
#[doc(hidden)]
pub async fn generated_uid(
&mut self,
) -> Result<crate::database::Text, GeneratedParameterError> {
let uid = generate().map_err(|_| GeneratedParameterError::GenerationUnavailable)?;
let text = std::str::from_utf8(&uid).expect("hex is ASCII");
self.request
.parameter_text(text)
.await
.map_err(GeneratedParameterError::Construction)
}
}
pub(crate) fn generate() -> Result<[u8; 32], ()> {
let millis = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_err(|_| ())?
.as_millis();
if millis >= 1u128 << 48 {
return Err(());
}
let mut bytes = [0; 16];
SystemRandom::new().fill(&mut bytes).map_err(|_| ())?;
Ok(encode(millis as u64, bytes))
}
fn encode(millis: u64, mut bytes: [u8; 16]) -> [u8; 32] {
bytes[..6].copy_from_slice(&millis.to_be_bytes()[2..]);
bytes[6] = (bytes[6] & 0x0f) | 0x70;
bytes[8] = (bytes[8] & 0x3f) | 0x80;
const HEX: &[u8; 16] = b"0123456789abcdef";
let mut text = [0; 32];
for (i, byte) in bytes.iter().enumerate() {
text[i * 2] = HEX[(byte >> 4) as usize];
text[i * 2 + 1] = HEX[(byte & 15) as usize];
}
text
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn version_variant_time_and_lowercase_are_exact() {
assert_eq!(
&encode(0x0123456789ab, [0; 16]),
b"0123456789ab70008000000000000000"
);
}
#[test]
fn production_entropy_produces_distinct_v7_values() {
let mut values = std::collections::BTreeSet::new();
for _ in 0..1024 {
let id = generate().unwrap();
assert_eq!(id[12], b'7');
assert!(b"89ab".contains(&id[16]));
assert!(values.insert(id));
}
}
}