rs_password_utils/dice/
mod.rs1use rand::RngCore;
15use rand::rngs::OsRng;
16
17mod diceware8k;
18
19pub fn generate(size: usize) -> String {
23 generate_passphrase(size, None)
24}
25
26pub fn generate_with_separator(size: usize, separator: &str) -> String {
30 generate_passphrase(size, separator)
31}
32
33fn generate_passphrase<'a, SEP>(size: usize, separator: SEP) -> String
34 where SEP: Into<Option<&'a str>> {
35 let separator = separator.into().unwrap_or(" ");
36 if size <= 0 {
37 "".to_string()
38 } else {
39 (0..size)
40 .map(|_| {
41 let mut buf = [0u8; 16];
42 OsRng.fill_bytes(&mut buf);
43 let random_u64 = OsRng.next_u64() as usize;
44 diceware8k::get_dice_word(random_u64)
45 })
46 .fold("".to_string(), |mut acc, passphrase| {
47 if !acc.is_empty() {
48 acc.push_str(separator);
49 }
50 acc.push_str(passphrase);
51 acc
52 })
53 }
54}
55
56#[cfg(test)]
57mod dice_unit_tests {
58 use super::*;
59
60 #[test]
61 fn test_generate_passphrase() {
62 assert!(generate_passphrase(0, None) == "");
63 let mut all: Vec<String> = Vec::new();
64
65 for _ in 0..1000 {
66 let passphrase = generate_passphrase(10, None);
67 if all.contains(&passphrase) {
68 assert!(false, "Same passphrase has already been generated: {}", passphrase);
69 }
70 all.push(passphrase);
71 }
72
73 let passphrase = generate_passphrase(3, "~");
74 let s: Vec<&str> = passphrase.split('~').collect();
75 assert_eq!(s.len(), 3);
76 }
77
78 #[test]
79 fn test_generate_with_separator() {
80 let passphrase = generate_with_separator(3, "~");
81 let s: Vec<&str> = passphrase.split('~').collect();
82 assert_eq!(s.len(), 3);
83
84 let passphrase = generate_with_separator(3, "");
85 let s: Vec<&str> = passphrase.split(" ").collect();
86 assert_eq!(s.len(), 1);
87 }
88
89 #[test]
90 fn test_generate() {
91 let passphrase = generate_with_separator(3, " ");
92 let s: Vec<&str> = passphrase.split(' ').collect();
93 assert_eq!(s.len(), 3);
94 }
95
96}