gecko/native/dubp_rs/src/secret_code.rs

148 lines
3.9 KiB
Rust

// 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 <https://www.gnu.org/licenses/>.
use crate::*;
pub(crate) fn compute_secret_code_len(
member_wallet: bool,
secret_code_type: SecretCodeType,
log_n: u8,
) -> Result<usize, DubpError> {
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 {
Ok(10)
} 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<String, DubpError> {
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),
}
}
fn gen_random_digits(n: usize) -> Result<String, DubpError> {
let mut digits_string = dup_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<String, DubpError> {
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<String, DubpError> {
let mut i = dup_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(())
}
}