deterministic_keygen/
lib.rs1use anyhow::Error;
2use bip39::{Language, Mnemonic, MnemonicType};
3use blake3;
4use pyo3::exceptions::{PyValueError, PyRuntimeError};
5use pyo3::prelude::*;
6use pyo3::types::PyBytes;
7use rand_chacha::ChaCha20Rng;
8use rand_chacha::rand_core::SeedableRng;
9use rsa::RsaPrivateKey;
10use rsa::pkcs8::{EncodePrivateKey, LineEnding};
11use std::str;
12
13const RSA_CONTEXT: &str = "deterministic-keygen Wed 07 Feb 2024 11:50:00 AM EST RSA v1";
18
19#[pyfunction]
21pub fn generate_phrase() -> String {
22 Mnemonic::new(MnemonicType::Words12, Language::English)
23 .phrase()
24 .to_string()
25}
26
27#[test]
28fn test_generate_phrase_returns_12_words() {
29 let phrase = generate_phrase();
30 assert_eq!(phrase.split_whitespace().count(), 12);
31}
32
33pub fn phrase_to_entropy(phrase: &str) -> Result<Vec<u8>, Error> {
35 let mnemonic = Mnemonic::from_phrase(phrase, Language::English)?;
36 let entropy: &[u8] = mnemonic.entropy();
37 Ok(entropy.to_vec())
38}
39
40#[test]
41fn test_phrase_to_entropy() {
42 let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
43 let entropy = phrase_to_entropy(phrase).unwrap();
44 let expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
45 assert_eq!(entropy, expected);
46}
47
48pub fn derive_rsa_key(entropy: &Vec<u8>, bit_size: usize) -> Result<String, Error> {
50 let seed: [u8; 32] = blake3::derive_key(RSA_CONTEXT, &entropy);
51 let mut rng = ChaCha20Rng::from_seed(seed);
52 let priv_key = RsaPrivateKey::new(&mut rng, bit_size)?;
53 let pem = priv_key.to_pkcs8_pem(LineEnding::LF)?;
54 Ok(pem.to_string())
55}
56
57#[test]
58fn test_derive_rsa_key() {
59 let phrase = generate_phrase();
60 let entropy = phrase_to_entropy(&phrase).unwrap();
61 let key1 = derive_rsa_key(&entropy, 512).unwrap();
62 let key2 = derive_rsa_key(&entropy, 512).unwrap();
63 assert_eq!(key1, key2);
64}
65
66#[pyfunction]
68#[pyo3(name = "derive_rsa_key")]
69#[pyo3(signature = (entropy, bit_size = 2048))]
70pub fn py_derive_rsa_key(entropy: &PyBytes, bit_size: usize) -> PyResult<String> {
71 match derive_rsa_key(&Vec::from(entropy.as_bytes()), bit_size) {
72 Err(error) => Err(PyRuntimeError::new_err(error.to_string())),
73 Ok(key) => Ok(key),
74 }
75}
76
77#[pyfunction]
79#[pyo3(signature = (phrase, bit_size = 2048))]
80pub fn derive_rsa_key_from_phrase(phrase: &str, bit_size: usize) -> PyResult<String> {
81 let entropy = match phrase_to_entropy(phrase) {
82 Err(error) => return Err(PyValueError::new_err(error.to_string())),
83 Ok(entropy) => entropy,
84 };
85 match derive_rsa_key(&entropy, bit_size) {
86 Err(error) => Err(PyRuntimeError::new_err(error.to_string())),
87 Ok(key) => Ok(key),
88 }
89}
90
91#[pymodule]
93fn deterministic_keygen(_py: Python, m: &PyModule) -> PyResult<()> {
94 m.add_function(wrap_pyfunction!(generate_phrase, m)?)?;
95 m.add_function(wrap_pyfunction!(py_derive_rsa_key, m)?)?;
96 m.add_function(wrap_pyfunction!(derive_rsa_key_from_phrase, m)?)?;
97 Ok(())
98}