// Copyright (C) 2020 Éloïs SANCHEZ. // // 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 crate::*; pub(crate) fn compute_secret_code_len( member_wallet: bool, secret_code_type: SecretCodeType, log_n: u8, ) -> Result { match secret_code_type { SecretCodeType::Digits => { if member_wallet { Err(DubpError::DigitsCodeForbidForMemberWallet) } else if log_n >= 15 { Ok(7) } else { Ok(8) } } SecretCodeType::Letters => { if member_wallet { if log_n >= 15 { Ok(7) } else { Ok(8) } } else if log_n >= 15 { Ok(5) } else { Ok(6) } } } } pub(crate) fn gen_secret_code( member_wallet: bool, secret_code_type: SecretCodeType, log_n: u8, ) -> Result { let secret_code_len = compute_secret_code_len(member_wallet, secret_code_type, log_n)?; match secret_code_type { SecretCodeType::Digits => gen_random_digits(secret_code_len), SecretCodeType::Letters => gen_random_letters(secret_code_len), } } pub(crate) fn is_ascii_letters(secret_code: &str) -> bool { secret_code.len() == secret_code .chars() .filter(|char| char.is_ascii_alphabetic()) .count() } fn gen_random_digits(n: usize) -> Result { let mut digits_string = dubp_client::crypto::rand::gen_u32() .map_err(|_| DubpError::RandErr)? .to_string(); digits_string.truncate(n); if digits_string.len() == n { Ok(digits_string) } else { let missing_digits = n - digits_string.len(); let mut digits_string_ = String::with_capacity(n); for _ in 0..missing_digits { digits_string_.push('0'); } digits_string_.push_str(&digits_string); Ok(digits_string_) } } fn gen_random_letters(mut n: usize) -> Result { let mut letters = String::with_capacity(n); while n >= 6 { letters.push_str(&gen_random_letters_inner(6)?); n -= 6; } letters.push_str(&gen_random_letters_inner(n)?); Ok(letters) } fn gen_random_letters_inner(n: usize) -> Result { let mut i = dubp_client::crypto::rand::gen_u32().map_err(|_| DubpError::RandErr)?; let mut letters = String::new(); for _ in 0..n { letters.push(to_char(i)); i /= 26; } Ok(letters) } fn to_char(i: u32) -> char { match i % 26 { 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', 21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', _ => unreachable!(), } } #[cfg(test)] mod tests { use super::*; #[test] fn test_gen_random_digits() -> Result<(), DubpError> { assert_eq!(gen_random_digits(8)?.len(), 8); //println!("TMP: {}", gen_random_digits(8)?); Ok(()) } #[test] fn test_gen_random_letters() -> Result<(), DubpError> { assert_eq!(gen_random_letters(6)?.len(), 6); //println!("TMP: {}", gen_random_letters(6)?); Ok(()) } }