iris_crypto/
lib.rs

1pub mod cheetah;
2pub mod slip10;
3
4pub use cheetah::{PrivateKey, PublicKey, Signature};
5pub use slip10::{derive_master_key, ExtendedKey};
6
7use argon2::{Algorithm, Argon2, Params, Version};
8use bip39::Mnemonic;
9
10/// Generate master key from entropy and salt using Argon2 + BIP39 + SLIP-10
11pub fn gen_master_key(entropy: &[u8], salt: &[u8]) -> (String, ExtendedKey) {
12    let mut argon_output = [0u8; 32];
13    let params = Params::new(
14        786_432,  // m_cost: 768 MiB in KiB
15        6,        // t_cost: 6 iterations
16        4,        // p_cost: 4 threads
17        Some(32), // output length
18    )
19    .expect("Invalid Argon2 parameters");
20
21    Argon2::new(Algorithm::Argon2d, Version::V0x13, params)
22        .hash_password_into(entropy, salt, &mut argon_output)
23        .expect("Invalid entropy and/or salt");
24
25    argon_output.reverse();
26
27    let mnemonic = Mnemonic::from_entropy(&argon_output).unwrap();
28    (
29        mnemonic.to_string(),
30        derive_master_key(&mnemonic.to_seed("")),
31    )
32}
33
34#[cfg(test)]
35mod tests {
36    use ibig::UBig;
37    use iris_ztd::Hashable;
38
39    use super::*;
40
41    fn parse_byts_decimal(wid: usize, decimal: &str) -> Vec<u8> {
42        let cleaned: String = decimal.chars().filter(|c| c.is_ascii_digit()).collect();
43        let n = UBig::from_str_radix(&cleaned, 10).expect("Invalid decimal value");
44        let bytes_be = n.to_be_bytes();
45        let mut res = vec![0u8; wid];
46        let mut started = false;
47        let mut idx = 0;
48        for byte in bytes_be.iter() {
49            if *byte != 0 || started {
50                started = true;
51                if idx < wid {
52                    res[idx] = *byte;
53                    idx += 1;
54                }
55            }
56        }
57        res
58    }
59
60    #[test]
61    fn test_keygen() {
62        const LOG_ENTROPY_DEC: &str =
63            "31944036134313954129336387727597658952065175074761089084822804536972439767490";
64        const LOG_SALT_DEC: &str = "143851195137845551434793173733272547792";
65        const LOG_MNEMONIC: &str = "pass destroy hub reject cricket flight camp garden scale liquid increase pool miracle fly tower file door cage vault tone night zero push crime";
66
67        let entropy = parse_byts_decimal(32, LOG_ENTROPY_DEC);
68        let salt = parse_byts_decimal(16, LOG_SALT_DEC);
69
70        let (mnemonic, keypair) = gen_master_key(&entropy, &salt);
71        assert_eq!(mnemonic, LOG_MNEMONIC);
72
73        // check private key, chain code and pkh
74        assert_eq!(
75            hex::encode(keypair.private_key.unwrap().to_be_bytes()),
76            "362b4073814e43f427983a83f11efcceb6741082c18f0d64b7e47340ba4485ba"
77        );
78        assert_eq!(
79            hex::encode(keypair.chain_code),
80            "95b522320f4dfae7486155b9529c582af3d7898ece606a802c43415786ced8d9"
81        );
82        assert_eq!(
83            keypair.public_key.hash().to_string(),
84            "AyzPiJoqcqmdZdjxZ9aGLnVsbYcCphidHERKBWVXyKhNqTirshTmicG"
85        );
86    }
87}