From 71e74c37804ae14447f252c905f0295a278e93ba Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Wed, 24 Apr 2024 11:00:55 +0200 Subject: [PATCH] encrypt private key with password --- Cargo.lock | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 5 ++++- src/lib.rs | 37 +++++++++++++++++++++++++++++++++--- src/main.rs | 8 +++++++- 4 files changed, 99 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 10cb4f3..e36b5db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,17 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "amplify" version = "4.6.0" @@ -176,6 +187,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clap" version = "4.5.4" @@ -320,6 +341,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "libc" version = "0.2.153" @@ -425,16 +455,40 @@ dependencies = [ "thiserror", ] +[[package]] +name = "rpassword" +version = "7.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.48.0", +] + +[[package]] +name = "rtoolbox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "s2id" version = "0.1.0" dependencies = [ + "aes", "amplify", "baid58", "clap", "crossbeam-channel", "rand", + "rpassword", "secp256k1", + "sha2", "shellexpand", ] diff --git a/Cargo.toml b/Cargo.toml index bfe4a16..400a2c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,11 @@ secp256k1 = { version = "0.29.0", features = ["rand", "global-context"] } rand = "0.8.5" clap = { version = "4.5.4", features = ["derive"], optional = true } shellexpand = { version = "3.1.0", optional = true } +sha2 = "0.10.8" +rpassword = { version = "7.3.1", optional = true } +aes = { version = "0.8.4", optional = true } crossbeam-channel = { version = "0.5.12", optional = true } [features] default = ["cli"] -cli = ["clap", "crossbeam-channel", "shellexpand"] +cli = ["clap", "crossbeam-channel", "shellexpand", "rpassword", "aes"] diff --git a/src/lib.rs b/src/lib.rs index 2500a9c..f22f0ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,11 +24,16 @@ extern crate amplify; use std::fmt; use std::fmt::{Display, Formatter}; +use std::hash::Hash; use std::str::FromStr; +use aes::cipher::generic_array::GenericArray; +use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit}; +use aes::{Aes256, Block}; use amplify::{Bytes, Display}; use baid58::{Baid58ParseError, Chunking, FromBaid58, ToBaid58, CHUNKING_32}; use secp256k1::SECP256K1; +use sha2::{Digest, Sha256}; #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display, Default)] #[non_exhaustive] @@ -223,7 +228,7 @@ impl SsiSecret { use rand::thread_rng; loop { let sk = secp256k1::SecretKey::new(&mut thread_rng()); - let (pk, _) = sk.x_only_public_key(&SECP256K1); + let (pk, _) = sk.x_only_public_key(SECP256K1); let data = pk.serialize(); if data[30] == u8::from(Algo::Bip340) && data[31] == u8::from(chain) { let mut key = [0u8; 30]; @@ -252,10 +257,36 @@ impl SsiSecret { rx.recv().expect("threading failed") } + pub fn encrypt(&mut self, passwd: impl AsRef) { + let key = Sha256::digest(passwd.as_ref().as_bytes()); + let key = GenericArray::from_slice(key.as_slice()); + let cipher = Aes256::new(key); + + let mut source = self.0.secret_bytes().to_vec(); + for chunk in source.chunks_mut(16) { + let block = Block::from_mut_slice(chunk); + cipher.encrypt_block(block); + } + self.0 = secp256k1::SecretKey::from_slice(&source).expect("same size") + } + + pub fn decrypt(&mut self, passwd: impl AsRef) { + let key = Sha256::digest(passwd.as_ref().as_bytes()); + let key = GenericArray::from_slice(key.as_slice()); + let cipher = Aes256::new(key); + + let mut source = self.0.secret_bytes().to_vec(); + for chunk in source.chunks_mut(16) { + let block = Block::from_mut_slice(chunk); + cipher.decrypt_block(block); + } + self.0 = secp256k1::SecretKey::from_slice(&source).expect("same size") + } + pub fn to_public(&self) -> Ssi { - let (pk, _) = self.0.x_only_public_key(&SECP256K1); + let (pk, _) = self.0.x_only_public_key(SECP256K1); let data = pk.serialize(); - return Ssi::from(data); + Ssi::from(data) } pub fn secret_bytes(&self) -> [u8; 32] { self.0.secret_bytes() } diff --git a/src/main.rs b/src/main.rs index dd8df89..db95bf2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -72,13 +72,19 @@ fn main() { threads, name, } => { + let passwd = rpassword::prompt_password("Password for private key encryption: ") + .expect("unable to read password"); + eprintln!("Generating new identity...."); - let secret = match prefix { + let mut secret = match prefix { Some(prefix) => SsiSecret::vanity(&prefix, chain, threads), None => SsiSecret::new(chain), }; let ssi = secret.to_public(); println!("{ssi}"); + + secret.encrypt(passwd); + let mut path = data_dir.clone(); path.push(name); fs::write(&path, format!("{secret}")).expect("unable to save secret key");