/* OpenTally: Open-source election vote counting * Copyright © 2021 Lee Yingtong Li (RunasSudo) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use ibig::UBig; use sha2::{Digest, Sha256}; /// Deterministic random number generator using SHA256 pub struct SHARandom<'r> { seed: &'r str, counter: usize, } impl<'r> SHARandom<'r> { /// Return a new [SHARandom] with the given seed pub fn new(seed: &'r str) -> Self { Self { seed: seed, counter: 0, } } /// Draw a random number *n*, such that 0 ≤ *n* < `max` pub fn next(&mut self, max: usize) -> usize { let mut hasher = Sha256::new(); hasher.update(format!("{},{}", self.seed, self.counter).as_bytes()); self.counter += 1; let hash = hasher.finalize(); let hash = UBig::from_be_bytes(&hash); if hash >= UBig::from(2_u32).pow(256) / UBig::from(max) * UBig::from(max) { return self.next(max); } return (hash % UBig::from(max)).to_string().parse().unwrap(); } } #[cfg(test)] mod tests { use super::*; #[test] fn sharandom1() { let mut random = SHARandom::new(&"foobar"); assert_eq!(random.next(10), 0); assert_eq!(random.next(42), 30); assert_eq!(random.next(64), 13); } }