keybox_core/lib.rs
1use bs58;
2use itertools::Itertools;
3use sha3::{digest::*, Shake256};
4
5// Bitcoin style
6// 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
7
8pub struct KeyGenerator {
9 password_digest: String,
10 hasher: Shake256,
11}
12
13impl KeyGenerator {
14 pub fn new(password: String) -> Self {
15 let mut hasher = Shake256::default();
16 hasher.update(password);
17 let password_digest = hasher.finalize_boxed_reset(64);
18 Self {
19 password_digest: bs58::encode(password_digest.into_vec()).into_string(),
20 hasher,
21 }
22 }
23
24 pub fn gen(
25 &mut self,
26 nickname: String,
27 key_length: usize,
28 additional_characters: Vec<char>,
29 ) -> String {
30 let password_digest = &self.password_digest;
31 let source = key_length.to_string() + password_digest + &nickname;
32 self.hasher.update(source);
33 let result = self.hasher.finalize_boxed_reset(64);
34 let mut encoded = bs58::encode(result.into_vec()).into_string();
35 encoded.truncate(key_length);
36 let char_counts = encoded.chars().counts();
37 for (from, to) in char_counts
38 .into_iter()
39 .sorted()
40 .zip(additional_characters.into_iter())
41 {
42 encoded = encoded.replace(&String::from(from.0), &String::from(to));
43 }
44 encoded
45 }
46}
47
48#[cfg(test)]
49mod test {
50 use crate::*;
51 #[test]
52 fn test_hasher() {
53 let mut key_gen =
54 KeyGenerator::new(String::from("My very very long rememberable password!!!!!"));
55 let key_length = 16;
56 let key = key_gen.gen(
57 String::from("nickname of a service or something that I can remember"),
58 key_length,
59 vec!['!', '#'],
60 );
61
62 // source : `My very very long rememberable password!!!!!`
63 // shake256 (512 bits) : `521322bc4f5f53a5b37265d7c0df3c043ddc47e885f260c2903645043e9b8d6d8efaed4bd13b2cb86569e99cb1068c0daea40ea0a77bed6d1caa984a1455a22f`
64 // base58 : `2eB7XEwM9pFnRfSfy9NJXfzNn1HVmpUYVz61z8Cwd4paKw5cQVt35cihEMQ5heW5i1bn4cDy9hh6Yy1QZUwHCJog`
65
66 // source is key_length + password_digest + nickname
67 // source : `162eB7XEwM9pFnRfSfy9NJXfzNn1HVmpUYVz61z8Cwd4paKw5cQVt35cihEMQ5heW5i1bn4cDy9hh6Yy1QZUwHCJognickname of a service or something that I can remember`
68 // shake256 (512 bits) : `b00050118d5b49f85808d09ce9c5953fa3905b4964539bfe5c2e4de045f37b5723a39fb9dfd7c0355abe22a321867e13dbc370c4f9ca1b0f38b7fc15812f5f4e`
69 // base58 : `4X6Lbepf8fUz9Sa63aNHpGar35ptodWaAEPPxA7mFtHXzMxPcPNE6PdZ3rfmRuhb5w3h9aZbfag4dPcjiFHmRGay`
70
71 // key is first {key_length} characters of base58
72 // key : `4X6Lbepf8fUz9Sa6`
73 // if additional characters are specified, characters in key is replaced from small(in ascii code) character.
74 // numbers -> capital letters -> small letters
75 // key : `!X#Lbepf8fUz9Sa#`
76
77 assert_eq!(key, "!X#Lbepf8fUz9Sa#");
78 }
79}