Generate a random alphanumeric confirmation code (e.g. "A7X-3KPX"). Uses a dash separator in the middle for readability.
()
| 44 | /// |
| 45 | /// Uses a dash separator in the middle for readability. |
| 46 | fn generate_confirmation_code() -> String { |
| 47 | use std::collections::hash_map::RandomState; |
| 48 | use std::hash::{BuildHasher, Hasher}; |
| 49 | |
| 50 | let charset = b"ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // no 0/O/1/I ambiguity |
| 51 | let mut code = String::with_capacity(CODE_LENGTH + 1); // +1 for dash |
| 52 | |
| 53 | // Use two independent RandomState instances as entropy sources. Each |
| 54 | // `RandomState::new()` is seeded from the OS. We combine both hashers' |
| 55 | // output per character to avoid depending on a single seed, and mix in |
| 56 | // the character index plus the previous hash for avalanche diffusion. |
| 57 | let state_a = RandomState::new(); |
| 58 | let state_b = RandomState::new(); |
| 59 | let mut prev_hash: u64 = 0; |
| 60 | for i in 0..CODE_LENGTH { |
| 61 | if i == 3 { |
| 62 | code.push('-'); |
| 63 | } |
| 64 | let mut hasher_a = state_a.build_hasher(); |
| 65 | hasher_a.write_usize(i); |
| 66 | hasher_a.write_u64(prev_hash); |
| 67 | let hash_a = hasher_a.finish(); |
| 68 | |
| 69 | let mut hasher_b = state_b.build_hasher(); |
| 70 | hasher_b.write_u64(hash_a); |
| 71 | hasher_b.write_usize(i); |
| 72 | let hash_b = hasher_b.finish(); |
| 73 | |
| 74 | prev_hash = hash_b; |
| 75 | // hash_b is `u64`; truncation to `usize` is acceptable here since we mod |
| 76 | // by charset.len() (small) and only use it as an index. |
| 77 | #[allow(clippy::cast_possible_truncation)] |
| 78 | let idx = (hash_b as usize) % charset.len(); |
| 79 | code.push(charset[idx] as char); |
| 80 | } |
| 81 | code |
| 82 | } |
| 83 | |
| 84 | /// Run the browser-based CF Access auth flow. |
| 85 | /// |