use crate::error::{SaTokenError, SaTokenResult};
pub(super) fn fill_bytes(buf: &mut [u8]) -> SaTokenResult<()> {
getrandom::getrandom(buf)
.map_err(|e| SaTokenError::InternalError(format!("CSPRNG unavailable: {e}")))
}
pub(crate) fn random_hex(length: usize) -> SaTokenResult<String> {
if length == 0 {
return Err(SaTokenError::ConfigError(
"random token length must be > 0".into(),
));
}
let byte_len = length.div_ceil(2);
let mut buf = vec![0u8; byte_len];
fill_bytes(&mut buf)?;
let hex = hex::encode(&buf);
Ok(hex[..length].to_string())
}
const TIK_CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
pub(super) fn random_tik(len: usize) -> SaTokenResult<String> {
let mut out = String::with_capacity(len);
let max_unbiased = (256 / TIK_CHARSET.len()) * TIK_CHARSET.len(); let mut buf = [0u8; 32];
while out.len() < len {
fill_bytes(&mut buf)?;
for b in buf {
if out.len() >= len {
break;
}
if (b as usize) < max_unbiased {
out.push(
TIK_CHARSET
.get((b as usize) % TIK_CHARSET.len())
.copied()
.unwrap_or(b'0') as char,
);
}
}
}
Ok(out)
}