use arrayref::array_ref;
use bip39::{Language, Mnemonic, MnemonicType, Seed};
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
use rsa::{
pkcs1::{EncodeRsaPrivateKey, EncodeRsaPublicKey},
pkcs8::{EncodePrivateKey, EncodePublicKey},
RsaPrivateKey, RsaPublicKey,
};
use rsa::pkcs8::der::zeroize::Zeroizing;
use std::{fs, io::Read};
use zeroize::Zeroize;
use sha2::{self, Digest};
type Keypair = (RsaPrivateKey, RsaPublicKey);
pub fn generate_seedphrase() -> Zeroizing<String> {
let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
let phrase = mnemonic.phrase().to_owned();
Zeroizing::new(phrase)
}
pub fn keypair_from_seedphrase(seedphrase: &Zeroizing<String>) -> Result<Keypair, String> {
match Mnemonic::validate(&seedphrase, Language::English) {
Ok(_) => (),
Err(e) => return Err(e.to_string()),
};
let mnemonic = Mnemonic::from_phrase(seedphrase.as_str(), Language::English).unwrap();
let mut seed = Seed::new(&mnemonic, "");
let mut seed_array = *array_ref!(seed.as_bytes(), 0, 32);
seed.zeroize();
let mut rng = ChaCha20Rng::from_seed(seed_array);
seed_array.zeroize();
let priv_key = RsaPrivateKey::new(&mut rng, 2048).map_err(|err| err.to_string())?;
let pub_key = RsaPublicKey::from(&priv_key);
Ok((priv_key, pub_key))
}
pub fn seedphrase_from_password(password: &Zeroizing<String>) -> Result<Zeroizing<String>, String> {
let mut hasher = sha2::Sha256::new();
hasher.update(password);
let mut password_hash = hasher.finalize();
let mut entropy: [u8 ; 16] = password_hash[..16].try_into().expect("wrong length");
password_hash.zeroize();
let mnemonic = Mnemonic::from_entropy(&entropy, Language::English).expect("failed to create mnemonic");
entropy.zeroize();
let zeroized = Zeroizing::new(mnemonic.phrase().to_owned());
Ok(zeroized)
}
pub fn keypair_from_private_key(priv_key: &RsaPrivateKey) -> (RsaPrivateKey, RsaPublicKey) {
let p = priv_key.clone();
let pub_key = RsaPublicKey::from(&p);
(p, pub_key)
}
pub fn generate_seedphrase_and_keypair() -> Result<(Zeroizing<String>, Keypair), String> {
let seedphrase = generate_seedphrase();
let keypair = keypair_from_seedphrase(&seedphrase).map_err(|err| err.to_string())?;
Ok((seedphrase, keypair))
}
pub fn pkcs1_pem_from_priv_key(priv_key: &RsaPrivateKey) -> Result<Zeroizing<String>, String> {
priv_key.to_pkcs1_pem(Default::default())
.map_err(|err| err.to_string())
}
pub fn pkcs1_pem_from_pub_key(pub_key: &RsaPublicKey) -> Result<Zeroizing<String>, String> {
match pub_key.to_pkcs1_pem(Default::default())
.map_err(|err| err.to_string()) {
Ok(x) => Ok(Zeroizing::new(x)),
Err(e) => Err(e.to_string())
}
}
pub fn pkcs8_pem_from_priv_key(priv_key: &RsaPrivateKey) -> Result<Zeroizing<String>, String> {
priv_key.to_pkcs8_pem(Default::default())
.map_err(|err| err.to_string())
}
pub fn pkcs8_pem_from_pub_key(pub_key: &RsaPublicKey) -> Result<String, String> {
pub_key.to_public_key_pem(Default::default())
.map_err(|err| err.to_string())
}
pub fn store_in_file(keypair: Keypair, seedphrase: &mut Zeroizing<String>, filename: &str) {
let mut priv_key_pem = pkcs8_pem_from_priv_key(&keypair.0).unwrap();
let mut pub_key_pem = pkcs8_pem_from_pub_key(&keypair.1).unwrap();
let data = format!(
"Seedphrase: {}\nPrivate Key: {}\nPublic Key: {}",
seedphrase.as_str(), priv_key_pem.as_str(), pub_key_pem
);
priv_key_pem.zeroize();
seedphrase.zeroize();
pub_key_pem.zeroize();
fs::write(filename, data).expect("failed to write to file");
}
#[test]
fn generate_seedphrase_has_12_words() {
let seedphrase = generate_seedphrase();
let words: Vec<&str> = seedphrase.split(" ").collect();
assert_eq!(words.len(), 12);
}
#[test]
fn generate_keypair_returns_same_pub_key() {
let seedphrase = generate_seedphrase();
let (priv_key, pub_key) = keypair_from_seedphrase(&seedphrase).unwrap();
let (priv_key1, pub_key1) = keypair_from_seedphrase(&seedphrase).unwrap();
assert_eq!(pub_key, pub_key1);
}
#[test]
fn generate_keypair_returns_same_priv_key() {
let seedphrase = generate_seedphrase();
let (priv_key, pub_key) = keypair_from_seedphrase(&seedphrase).unwrap();
let (priv_key1, pub_key1) = keypair_from_seedphrase(&seedphrase).unwrap();
assert_eq!(priv_key, priv_key1);
}
#[test]
fn generate_keypair_returns_same_pub_key_from_priv_key() {
let seedphrase = generate_seedphrase();
let (priv_key, pub_key) = keypair_from_seedphrase(&seedphrase).unwrap();
let (priv_key1, pub_key1) = keypair_from_private_key(&priv_key);
assert_eq!(pub_key, pub_key1);
}