use rand::{RngCore, SeedableRng, CryptoRng};
use rand_chacha::ChaCha20Rng;
use std::sync::Arc;
use tokio::sync::Mutex;
pub struct QuantumRng {
fallback: ChaCha20Rng,
qrng_endpoint: Option<String>,
entropy_cache: Arc<Mutex<Vec<u8>>>,
mix_with_os: bool,
}
impl QuantumRng {
pub fn new(qrng_endpoint: Option<String>) -> Self {
let fallback = ChaCha20Rng::from_entropy();
Self {
fallback,
qrng_endpoint,
entropy_cache: Arc::new(Mutex::new(Vec::new())),
mix_with_os: true, }
}
async fn get_quantum_entropy(&self, num_bytes: usize) -> Option<Vec<u8>> {
if let Some(endpoint) = &self.qrng_endpoint {
match self.fetch_from_qrng(endpoint, num_bytes).await {
Ok(entropy) => {
log::info!("Using QRNG entropy for PQC key generation");
Some(entropy)
}
Err(e) => {
log::warn!("QRNG unavailable, using OS entropy: {}", e);
None
}
}
} else {
None
}
}
async fn fetch_from_qrng(&self, endpoint: &str, num_bytes: usize) -> Result<Vec<u8>, String> {
Err("QRNG not yet implemented".to_string())
}
pub async fn generate_key_material(&mut self, num_bytes: usize) -> Vec<u8> {
let mut result = vec![0u8; num_bytes];
if let Some(quantum) = self.get_quantum_entropy(num_bytes).await {
if self.mix_with_os {
let mut os_entropy = vec![0u8; num_bytes];
self.fallback.fill_bytes(&mut os_entropy);
for i in 0..num_bytes {
result[i] = quantum[i] ^ os_entropy[i];
}
log::info!("Generated key material using QRNG + OS entropy mix");
} else {
result = quantum;
log::info!("Generated key material using pure QRNG");
}
} else {
self.fallback.fill_bytes(&mut result);
log::info!("Generated key material using OS entropy (QRNG unavailable)");
}
result
}
}
impl RngCore for QuantumRng {
fn next_u32(&mut self) -> u32 {
self.fallback.next_u32()
}
fn next_u64(&mut self) -> u64 {
self.fallback.next_u64()
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.fallback.fill_bytes(dest)
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
self.fallback.try_fill_bytes(dest)
}
}
impl CryptoRng for QuantumRng {}
pub async fn generate_falcon_keypair_with_qrng(qrng_endpoint: Option<String>) -> Result<(Vec<u8>, Vec<u8>), String> {
let mut qrng = QuantumRng::new(qrng_endpoint);
let _seed = qrng.generate_key_material(32).await;
use pqcrypto_falcon::falcon512;
use pqcrypto_traits::sign::{PublicKey, SecretKey};
let (pk, sk) = falcon512::keypair();
Ok((pk.as_bytes().to_vec(), sk.as_bytes().to_vec()))
}
pub async fn generate_sphincs_keypair_with_qrng(qrng_endpoint: Option<String>) -> Result<(Vec<u8>, Vec<u8>), String> {
let mut qrng = QuantumRng::new(qrng_endpoint);
let _seed = qrng.generate_key_material(32).await;
use pqcrypto_sphincsplus::sphincssha256128ssimple as sphincs;
use pqcrypto_traits::sign::{PublicKey, SecretKey};
let (pk, sk) = sphincs::keypair();
Ok((pk.as_bytes().to_vec(), sk.as_bytes().to_vec()))
}
pub struct QrngConfig {
pub kirq_endpoint: Option<String>,
pub crypto4a_endpoint: Option<String>,
pub device_path: Option<String>,
pub always_mix: bool,
}
impl Default for QrngConfig {
fn default() -> Self {
Self {
kirq_endpoint: std::env::var("QSSH_KIRQ_ENDPOINT").ok(),
crypto4a_endpoint: std::env::var("QSSH_CRYPTO4A_ENDPOINT").ok(),
device_path: std::env::var("QSSH_QRNG_DEVICE").ok(),
always_mix: true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_qrng_fallback() {
let mut qrng = QuantumRng::new(None);
let entropy = qrng.generate_key_material(32).await;
assert_eq!(entropy.len(), 32);
}
#[tokio::test]
async fn test_falcon_with_qrng() {
let result = generate_falcon_keypair_with_qrng(None).await;
assert!(result.is_ok());
}
}