Skip to main content

lib_q_random/
saturnin_det.rs

1//! Saturnin CTR deterministic byte expansion (optional `deterministic-saturnin` feature).
2
3#[cfg(feature = "alloc")]
4use alloc::vec::Vec;
5
6use lib_q_saturnin::SaturninStream;
7
8use crate::Result;
9use crate::kt128_expander::DOMAIN_LIBQ_DET_SATURNIN;
10
11const NONCE_LEN: usize = 16;
12const KEY_LEN: usize = 32;
13const CHUNK: usize = 4096;
14
15/// Deterministic Saturnin CTR keystream expander for testing.
16pub struct SaturninDetExpander {
17    key: [u8; KEY_LEN],
18    nonce: [u8; NONCE_LEN],
19    keystream: Vec<u8>,
20    position: usize,
21    counter: u64,
22}
23
24impl SaturninDetExpander {
25    /// Build from a 32-byte seed (used directly as the Saturnin key).
26    ///
27    /// # Errors
28    ///
29    /// Returns an error if keystream generation fails.
30    pub fn from_seed_32(seed: [u8; 32]) -> Result<Self> {
31        let mut nonce = [0u8; NONCE_LEN];
32        let domain = DOMAIN_LIBQ_DET_SATURNIN;
33        let copy_len = core::cmp::min(NONCE_LEN, domain.len());
34        nonce[..copy_len].copy_from_slice(&domain[..copy_len]);
35
36        let stream = SaturninStream::new();
37        let keystream = stream
38            .generate_keystream(&seed, &nonce, CHUNK)
39            .map_err(|_| crate::Error::platform_rng_failed("saturnin"))?;
40
41        Ok(Self {
42            key: seed,
43            nonce,
44            keystream: keystream.to_vec(),
45            position: 0,
46            counter: 0,
47        })
48    }
49
50    fn refill(&mut self) -> Result<()> {
51        self.counter = self.counter.wrapping_add(1);
52        let mut nonce = self.nonce;
53        nonce[8..16].copy_from_slice(&self.counter.to_le_bytes());
54        let stream = SaturninStream::new();
55        let chunk = stream
56            .generate_keystream(&self.key, &nonce, CHUNK)
57            .map_err(|_| crate::Error::platform_rng_failed("saturnin"))?;
58        self.keystream = chunk.to_vec();
59        self.position = 0;
60        Ok(())
61    }
62
63    /// Fill `dest` with deterministic keystream bytes.
64    ///
65    /// # Errors
66    ///
67    /// Returns an error if keystream refill fails.
68    pub fn fill_bytes(&mut self, dest: &mut [u8]) -> Result<()> {
69        let mut remaining = dest.len();
70        let mut offset = 0;
71        while remaining > 0 {
72            if self.position >= self.keystream.len() {
73                self.refill()?;
74            }
75            let available = self.keystream.len() - self.position;
76            let to_copy = core::cmp::min(remaining, available);
77            dest[offset..offset + to_copy]
78                .copy_from_slice(&self.keystream[self.position..self.position + to_copy]);
79            self.position += to_copy;
80            offset += to_copy;
81            remaining -= to_copy;
82        }
83        Ok(())
84    }
85}
86
87/// Entropy source adapter for [`SaturninDetExpander`].
88#[cfg(feature = "alloc")]
89pub struct SaturninDeterministicEntropySource {
90    expander: SaturninDetExpander,
91    quality: f64,
92}
93
94#[cfg(feature = "alloc")]
95impl SaturninDeterministicEntropySource {
96    /// Create a Saturnin CTR-backed deterministic entropy source.
97    ///
98    /// # Errors
99    ///
100    /// Returns an error if Saturnin keystream initialization fails.
101    pub fn new(seed: [u8; 32]) -> Result<Self> {
102        Ok(Self {
103            expander: SaturninDetExpander::from_seed_32(seed)?,
104            quality: 0.0,
105        })
106    }
107}
108
109#[cfg(feature = "alloc")]
110impl crate::traits::EntropySource for SaturninDeterministicEntropySource {
111    fn get_entropy(&mut self, dest: &mut [u8]) -> Result<()> {
112        self.expander.fill_bytes(dest)
113    }
114
115    fn initialize(&mut self, config: &crate::traits::EntropyConfig) -> Result<()> {
116        let _ = config;
117        Ok(())
118    }
119
120    fn is_available(&self) -> bool {
121        true
122    }
123
124    fn quality(&self) -> f64 {
125        self.quality
126    }
127
128    fn name(&self) -> &'static str {
129        "Saturnin Deterministic Entropy Source"
130    }
131
132    fn source_type(&self) -> crate::traits::EntropySourceType {
133        crate::traits::EntropySourceType::Deterministic
134    }
135
136    fn max_entropy_per_call(&self) -> Option<usize> {
137        None
138    }
139}
140
141#[cfg(all(test, feature = "deterministic-saturnin"))]
142mod tests {
143    use super::*;
144    use crate::kt128_expander::Kt128Expander;
145
146    #[test]
147    fn saturnin_det_differs_from_kt128_det() {
148        let seed = [3u8; 32];
149        let mut sat = SaturninDetExpander::from_seed_32(seed).expect("saturnin");
150        let mut kt = Kt128Expander::from_det_seed_32(seed);
151        let mut a = [0u8; 64];
152        let mut b = [0u8; 64];
153        sat.fill_bytes(&mut a).expect("fill");
154        kt.fill_bytes(&mut b);
155        assert_ne!(a, b);
156    }
157}