From 430c696873c5c1a637f5ef5d415f0495cdc29562 Mon Sep 17 00:00:00 2001 From: CounterFire2023 <136581895+CounterFire2023@users.noreply.github.com> Date: Thu, 17 Aug 2023 16:57:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=92=B1=E5=8C=85=E6=A0=B8?= =?UTF-8?q?=E5=BF=83=E4=BB=A3=E7=A0=81=EF=BC=8C=20=E5=92=8Cnative=E7=AB=AF?= =?UTF-8?q?=E4=BF=9D=E6=8C=81=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 2 + Cargo.toml | 5 ++ scripts/wasm_build.sh | 3 +- src/lib.rs | 114 +++++++++++++------------- src/utils/crypto_utils.rs | 167 ++++++++++++++++++++++++++++++++++++++ src/utils/mod.rs | 2 +- src/utils/pass_utils.rs | 36 -------- src/wallet/wallet_impl.rs | 139 +++++++++---------------------- 8 files changed, 275 insertions(+), 193 deletions(-) create mode 100644 src/utils/crypto_utils.rs delete mode 100644 src/utils/pass_utils.rs diff --git a/Cargo.lock b/Cargo.lock index d0735b7..e6fdbe3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -697,6 +697,7 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" name = "rustwallet" version = "0.1.0" dependencies = [ + "aes-gcm", "anyhow", "argon2", "base64 0.21.0", @@ -706,6 +707,7 @@ dependencies = [ "hex", "primitive-types", "qrcodegen", + "rand 0.8.5", "secp256k1", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 9a19a1d..ff73474 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,11 @@ wasm-bindgen = "0.2.83" qrcodegen = "1.8.0" base64 = "0.21.0" argon2 = { version = "0.5.0" } +aes-gcm = "0.10.1" +rand = "0.8.5" [profile.release] lto = true + +[package.metadata.wasm-pack.profile.release] +wasm-opt = false \ No newline at end of file diff --git a/scripts/wasm_build.sh b/scripts/wasm_build.sh index 9f55ac6..5959f2b 100755 --- a/scripts/wasm_build.sh +++ b/scripts/wasm_build.sh @@ -1 +1,2 @@ -AR=/opt/homebrew/opt/llvm/bin/llvm-ar CC=/opt/homebrew/opt/llvm/bin/clang wasm-pack build --target web --release \ No newline at end of file +# AR=/opt/homebrew/opt/llvm/bin/llvm-ar CC=/opt/homebrew/opt/llvm/bin/clang wasm-pack build --target web --release +AR=/opt/homebrew/opt/llvm/bin/llvm-ar CC=/opt/homebrew/opt/llvm/bin/clang wasm-pack build --target nodejs --release \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index d438846..03765d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,10 @@ use qr::qr_code::QR; -use utils::pass_utils::{hash_password, verify_password}; +use secp256k1::PublicKey; +use secp256k1::SecretKey; +use std::str::FromStr; +use utils::crypto_utils::{hash_password, verify_password, zdecrypt, zencrypt}; use utils::str_utils::{base64_to_hex, hex_to_base64}; use wasm_bindgen::prelude::*; @@ -44,50 +47,31 @@ macro_rules! console_log { } #[wasm_bindgen] -pub fn get_public_key( - msg_key: String, - master_key: String, - second_key: Option, - backup_key: Option, -) -> String { +pub fn get_public_key(msg_key: String, master_key: String, second_key: String) -> String { let rwallet = Wallet { msg_key, master_key, second_key, - backup_key, }; rwallet.get_public_key().to_string() } #[wasm_bindgen] -pub fn generate_sec_key( - msg_key: String, - master_key: String, - second_key: Option, - backup_key: Option, -) -> String { +pub fn generate_sec_key(msg_key: String, master_key: String, second_key: String) -> String { let rwallet = Wallet { msg_key, master_key, second_key, - backup_key, }; rwallet.generate_sec_key() } #[wasm_bindgen] -pub fn sign( - msg_key: String, - master_key: String, - second_key: Option, - backup_key: Option, - msg: String, -) -> String { +pub fn sign(msg_key: String, master_key: String, second_key: String, msg: String) -> String { let rwallet = Wallet { msg_key, master_key, second_key, - backup_key, }; let signature = rwallet.sign(msg); let r = match signature { @@ -101,15 +85,13 @@ pub fn sign( pub fn sign_for_tran( msg_key: String, master_key: String, - second_key: Option, - backup_key: Option, + second_key: String, msg: String, ) -> String { let rwallet = Wallet { msg_key, master_key, second_key, - backup_key, }; let signature = rwallet.sign_for_tran(msg); @@ -122,18 +104,12 @@ pub fn sign_for_tran( } #[wasm_bindgen] -pub fn wget_address( - msg_key: String, - master_key: String, - second_key: Option, - backup_key: Option, -) -> String { +pub fn wget_address(msg_key: String, master_key: String, second_key: String) -> String { console_log!("wget_address: {}, {}!", msg_key, master_key); let rwallet = Wallet { msg_key, master_key, second_key, - backup_key, }; console_log!("wallet: {:?}", rwallet); let address = rwallet.get_address(); @@ -142,64 +118,92 @@ pub fn wget_address( } #[wasm_bindgen] -pub fn wencrypt( - msg_key: String, - master_key: String, - second_key: Option, - backup_key: Option, - msg: String, -) -> String { +pub fn wencrypt(msg_key: String, master_key: String, second_key: String, msg: String) -> String { let rwallet = Wallet { msg_key, master_key, second_key, - backup_key, }; - let r = match rwallet.zencrypt(&msg) { + let pk = rwallet.get_public_key(); + let r = match zencrypt(pk, &msg) { Ok(v) => v, Err(err) => panic!("Problem encrypt: {:?}", err), }; r } #[wasm_bindgen] -pub fn wdecrypt( - msg_key: String, - master_key: String, - second_key: Option, - backup_key: Option, - msg: String, -) -> String { +pub fn wdecrypt(msg_key: String, master_key: String, second_key: String, msg: String) -> String { let rwallet = Wallet { msg_key, master_key, second_key, - backup_key, }; - let r = match rwallet.zdecrypt(&msg) { + let pk = rwallet.get_secret_key(); + let r = match zdecrypt(pk, &msg) { Ok(v) => v, Err(err) => panic!("Problem encrypt: {:?}", err), }; r } +#[wasm_bindgen] +pub fn aes_encrypt(content: String, key: String) -> String { + let pass = utils::crypto_utils::keccak256_hash(&key); + let msg_hex = utils::crypto_utils::aes_encrypt(&content, &pass); + msg_hex +} + +#[wasm_bindgen] +pub fn aes_decrypt(content: String, key: String) -> String { + let pass = utils::crypto_utils::keccak256_hash(&key); + let msg_hex = utils::crypto_utils::aes_decrypt(&content, &pass); + msg_hex +} + +#[wasm_bindgen] +pub fn rencrypt(pk: String, msg: String) -> String { + let public_key = match PublicKey::from_str(&msg) { + Ok(v) => v, + Err(e) => panic!("error parse publickey: {}", e), + }; + let msg_encrypt = match zencrypt(public_key, &msg) { + Ok(v) => v, + Err(err) => panic!("error encrypt: {:?}", err), + }; + msg_encrypt +} + +#[wasm_bindgen] +pub fn rdecrypt(sk: String, msg: String) -> String { + let private_key = match SecretKey::from_str(&sk) { + Ok(v) => v, + Err(e) => panic!("error parse publickey: {}", e), + }; + let msg_decrypt = match zdecrypt(private_key, &msg) { + Ok(v) => v, + Err(err) => panic!("error encrypt: {:?}", err), + }; + msg_decrypt +} + #[wasm_bindgen] pub fn generate_qr(content: String) -> String { QR::parse(&content) } #[wasm_bindgen] -pub fn str_deflate(content: String) -> String { +pub fn hex_deflate(content: String) -> String { hex_to_base64(&content) } #[wasm_bindgen] -pub fn str_inflate(content: String) -> String { +pub fn hex_inflate(content: String) -> String { base64_to_hex(&content) } #[wasm_bindgen] -pub fn password_hash(pass: String) -> String { - hash_password(&pass) +pub fn password_hash(pass: String, salt: String) -> String { + hash_password(&pass, &salt) } #[wasm_bindgen] diff --git a/src/utils/crypto_utils.rs b/src/utils/crypto_utils.rs new file mode 100644 index 0000000..a06a0ae --- /dev/null +++ b/src/utils/crypto_utils.rs @@ -0,0 +1,167 @@ +use aes_gcm::{ + aead::{generic_array::GenericArray, Aead, KeyInit}, + Aes256Gcm, +}; +use anyhow::Result; +use argon2::{ + password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, + Argon2, +}; +use base64::{engine::general_purpose, Engine as _}; +use ecies::{decrypt, encrypt}; +use rand::prelude::*; +use secp256k1::{PublicKey, SecretKey}; +use std::str; +use tiny_keccak::keccak256; + +pub fn random_argon2_salt() -> String { + let salt: SaltString = SaltString::generate(&mut OsRng); + salt.as_str().to_string() +} +pub fn hash_password(pass: &str, salt: &str) -> String { + // let salt: SaltString = SaltString::generate(&mut OsRng); + let salt = SaltString::from_b64(salt).expect("error parse salt"); + let password = pass.as_bytes(); + let password_hash: String = match Argon2::default().hash_password(password, &salt) { + Ok(v) => v.to_string(), + Err(e) => panic!("error hash password: {}", e), + }; + general_purpose::STANDARD_NO_PAD.encode(&password_hash) +} + +pub fn verify_password(pass: &str, password_hash: &str) -> bool { + let str_tmp = match general_purpose::STANDARD_NO_PAD.decode(password_hash) { + Ok(v) => v, + Err(e) => panic!("error decode base64 str: {}", e), + }; + let s = match str::from_utf8(&str_tmp) { + Ok(v) => v, + Err(e) => panic!("Invalid UTF-8 sequence: {}", e), + }; + let parsed_hash = match PasswordHash::new(&s) { + Ok(v) => v, + Err(e) => panic!("error parse password hash: {}", e), + }; + + let password = pass.as_bytes(); + Argon2::default() + .verify_password(password, &parsed_hash) + .is_ok() +} + +pub fn keccak256_hash(str: &str) -> String { + let data = str.as_bytes(); + let hasher = keccak256(data); + hex::encode(&hasher) +} + +pub fn hash_pass_client(str: &str) -> String { + let message = str.as_bytes(); + let mut eth_message = format!("\x22cebg password pre hash:\n{}", message.len()).into_bytes(); + eth_message.extend_from_slice(message); + + let hasher = keccak256(ð_message); + hex::encode(&hasher) +} + +pub fn hash_pass_svr(str: &str) -> String { + let message = str.as_bytes(); + let mut eth_message = format!("\x23cebg email regist:\n{}", message.len()).into_bytes(); + eth_message.extend_from_slice(message); + + let hasher = keccak256(ð_message); + hex::encode(&hasher) +} +// +// This function is used to encrypt a string using AES-256-GCM algorithm +pub fn aes_encrypt(str: &str, key: &str) -> String { + // Convert the input string to bytes + let str_data = str.as_bytes(); + + // Decode the input key from hex to bytes and create a GenericArray object + let key = hex::decode(&key).expect("Invalid hex string"); + let key = GenericArray::from_slice(&key); + + // Create an AES-256-GCM cipher with the specified key + let cipher = Aes256Gcm::new(&key); + + // Generate a random nonce of 96 bits (12 bytes) + let mut nonce_data = [0u8; 12]; + thread_rng().fill_bytes(&mut nonce_data); + let nonce = GenericArray::from_slice(&nonce_data); + + // Encrypt the data using the cipher and nonce + let ciphertext = match cipher.encrypt(nonce, str_data) { + Ok(v) => v, + Err(e) => panic!("error encrypt: {}", e), + }; + // Encode the ciphertext and nonce as base64 strings + let cipher_str = general_purpose::STANDARD_NO_PAD.encode(&ciphertext); + let nonce_str = general_purpose::STANDARD_NO_PAD.encode(&nonce_data); + // Concatenate the encoded ciphertext and nonce with a period separator and return the result + nonce_str + cipher_str.as_str() +} + +pub fn aes_decrypt(str: &str, key: &str) -> String { + let str_data = match general_purpose::STANDARD_NO_PAD.decode(str) { + Ok(v) => v, + Err(e) => panic!("error decode base64 str: {}", e), + }; + if str_data.len() < 12 { + panic!("error decrypt: invalid data"); + } + let v = 12; + let nonce_data = &str_data[..v]; + let cipher_data = &str_data[v..]; + + let nonce = GenericArray::from_slice(&nonce_data); + let key = hex::decode(&key).expect("Invalid hex string"); + let key = GenericArray::from_slice(&key); + let cipher = Aes256Gcm::new(&key); + + let plaintext = match cipher.decrypt(nonce, cipher_data) { + Ok(v) => v, + Err(e) => panic!("error decrypt: {}", e), + }; + let plaintext = str::from_utf8(&plaintext).expect("err convert to utf8"); + plaintext.to_string() +} +// encrypt content with wallet public key +pub fn zencrypt(pk: PublicKey, msg: &str) -> Result { + let pk = &pk.serialize(); + let msg = msg.as_bytes(); + // println!("msg before encrypt: {:?}", msg); + let msg_encrypt = match encrypt(pk, &msg) { + Ok(v) => v, + Err(e) => panic!("error encrypt content: {}", e), + }; + // println!("msg after encrypt: {:?}", &msg_encrypt); + let str_encrypt = hex::encode(&msg_encrypt); + Ok(str_encrypt) +} +// decrypt content with wallet private key +pub fn zdecrypt(sk: SecretKey, msg1: &str) -> Result { + let sk = sk.secret_bytes(); + let mut msg: String = msg1.clone().to_string(); + if msg.len() % 2 == 1 { + msg = "0".to_owned() + &msg; + } + println!("msg to decrypt: {:?}", &msg); + let msg = match hex::decode(&msg) { + Ok(v) => v, + Err(e) => panic!("error decode hex str: {}", e), + }; + let msg_decrypt = match decrypt(&sk, &msg) { + Ok(v) => v, + Err(e) => panic!("error decrypt content: {}", e), + }; + // println!("msg after decrypt: {:?}", &msg_decrypt); + // let msg_decrypt = hex::encode(msg_decrypt); + let str_decrypt = match str::from_utf8(&msg_decrypt) { + Ok(v) => v, + Err(e) => panic!("Invalid UTF-8 sequence: {}", e), + }; + let result = str_decrypt.to_string(); + + Ok(result) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 25d5f55..e143402 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,2 +1,2 @@ -pub mod pass_utils; +pub mod crypto_utils; pub mod str_utils; diff --git a/src/utils/pass_utils.rs b/src/utils/pass_utils.rs deleted file mode 100644 index 7738cab..0000000 --- a/src/utils/pass_utils.rs +++ /dev/null @@ -1,36 +0,0 @@ -use argon2::{ - password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, - Argon2, -}; -use base64::{engine::general_purpose, Engine as _}; -use std::str; - -pub fn hash_password(pass: &str) -> String { - let salt: SaltString = SaltString::generate(&mut OsRng); - let password = pass.as_bytes(); - let password_hash: String = match Argon2::default().hash_password(password, &salt) { - Ok(v) => v.to_string(), - Err(e) => panic!("error hash password: {}", e), - }; - general_purpose::STANDARD_NO_PAD.encode(&password_hash) -} - -pub fn verify_password(pass: &str, password_hash: &str) -> bool { - let str_tmp = match general_purpose::STANDARD_NO_PAD.decode(password_hash) { - Ok(v) => v, - Err(e) => panic!("error decode base64 str: {}", e), - }; - let s = match str::from_utf8(&str_tmp) { - Ok(v) => v, - Err(e) => panic!("Invalid UTF-8 sequence: {}", e), - }; - let parsed_hash = match PasswordHash::new(&s) { - Ok(v) => v, - Err(e) => panic!("error parse password hash: {}", e), - }; - - let password = pass.as_bytes(); - Argon2::default() - .verify_password(password, &parsed_hash) - .is_ok() -} diff --git a/src/wallet/wallet_impl.rs b/src/wallet/wallet_impl.rs index 39ef366..ddbf63a 100644 --- a/src/wallet/wallet_impl.rs +++ b/src/wallet/wallet_impl.rs @@ -1,10 +1,10 @@ extern crate hex; +use crate::utils::{crypto_utils as crypro_utils, str_utils}; use anyhow::Result; use core::fmt::Write; -use ecies::{decrypt, encrypt}; use primitive_types::{H160, H256}; -use secp256k1::rand::rngs::OsRng; +// use secp256k1::rand::rngs::OsRng; use secp256k1::{Message, PublicKey, Secp256k1, SecretKey}; use serde::{Deserialize, Serialize}; use shamir_secret_sharing::num_bigint::BigInt; @@ -13,12 +13,31 @@ use std::str; use std::str::FromStr; use tiny_keccak::keccak256; -pub fn generate_keypair() -> (SecretKey, PublicKey) { - let secp = Secp256k1::new(); - // let mut rng = OsRng::new().expect("OsRng"); - secp.generate_keypair(&mut OsRng) +// generate client key +pub fn generate_client_key(password: &str, openid: &str, salt: &str) -> String { + let password = crypro_utils::hash_pass_client(password); + let mut s = String::new(); + s.push_str(&password); + s.push_str(openid); + let salt = crypro_utils::keccak256_hash(&salt); + let salt = str_utils::hex_to_base64(&salt); + let pass_hash = crypro_utils::hash_password(&s, &salt); + let key = crypro_utils::keccak256_hash(&pass_hash); + key } +pub fn local_pass_hasher(password: &str) -> String { + let password = crypro_utils::hash_pass_client(password); + let salt = crypro_utils::random_argon2_salt(); + let pass_hash = crypro_utils::hash_password(&password, &salt); + pass_hash +} + +// pub fn generate_keypair() -> (SecretKey, PublicKey) { +// let secp = Secp256k1::new(); +// secp.generate_keypair(&mut OsRng) +// } + pub fn public_key_address(public_key: &PublicKey) -> H160 { let public_key = public_key.serialize_uncompressed(); debug_assert_eq!(public_key[0], 0x04); @@ -43,16 +62,16 @@ pub fn get_sss(msg: &str) -> SSS { } } -pub fn generate_sss_keypair(msg: &str, skey: &str) -> Vec { - let secret = BigInt::parse_bytes(&skey.as_bytes(), 16).unwrap(); - let sss = get_sss(msg); - let shares = sss.split(secret.clone()); - let mut shares_str: Vec = Vec::new(); - for (_i, v) in &shares { - shares_str.push(v.to_str_radix(16)); - } - shares_str -} +// pub fn generate_sss_keypair(msg: &str, skey: &str) -> Vec { +// let secret = BigInt::parse_bytes(&skey.as_bytes(), 16).unwrap(); +// let sss = get_sss(msg); +// let shares = sss.split(secret.clone()); +// let mut shares_str: Vec = Vec::new(); +// for (_i, v) in &shares { +// shares_str.push(v.to_str_radix(16)); +// } +// shares_str +// } pub fn hash_message(message: S) -> H256 where @@ -70,55 +89,17 @@ where pub struct Wallet { pub msg_key: String, pub master_key: String, - pub second_key: Option, - pub backup_key: Option, + pub second_key: String, } impl Wallet { - pub fn new(msg: &str) -> Self { - let (secret_key, _pub_key) = generate_keypair(); - let s = hex::encode(&secret_key.secret_bytes()); - let shares_str = generate_sss_keypair(msg, &s); - // println!("secret key: {:?}", secret_key); - // println!("{:?}", s); - let second_key = shares_str.get(1).map(String::clone); - let backup_key = shares_str.get(2).map(String::clone); - Wallet { - msg_key: msg.to_string(), - master_key: shares_str.get(0).unwrap().to_string(), - second_key: second_key, - backup_key: backup_key, - } - } - - pub fn reset_wallet(&self) -> Self { - let secret_key = self.get_secret_key(); - let s = hex::encode(&secret_key.secret_bytes()); - let shares_str = generate_sss_keypair(&self.msg_key, &s); - let second_key = shares_str.get(1).map(String::clone); - let backup_key = shares_str.get(2).map(String::clone); - - Wallet { - msg_key: self.msg_key.clone(), - master_key: shares_str.get(0).unwrap().to_string(), - second_key: second_key, - backup_key: backup_key, - } - } - - fn get_secret_key(&self) -> SecretKey { + pub fn get_secret_key(&self) -> SecretKey { let key_str_0: &str = &self.master_key; let key0 = BigInt::parse_bytes(&key_str_0.as_bytes(), 16).unwrap(); let kp0: (usize, BigInt) = (1, key0); - let (i, key_str_1) = match (&self.second_key, &self.backup_key) { - (Some(val), _) => (2, val), - (_, Some(val)) => (3, val), - _ => { - panic!("error generate key") - } - }; + let key_str_1: &str = &self.second_key; let key1 = BigInt::parse_bytes(&key_str_1.as_bytes(), 16).unwrap(); - let kp1 = (i, key1); + let kp1 = (2, key1); let _tmp = vec![kp0, kp1]; let sss = get_sss(&self.msg_key); let secret_b = sss.recover(&_tmp); @@ -166,7 +147,6 @@ impl Wallet { write!(s, "{:02x}", rv).unwrap(); Ok(s) } - pub fn sign_for_tran(&self, msg: S) -> Result<(String, i32)> where S: AsRef<[u8]>, @@ -185,45 +165,4 @@ impl Wallet { let recid = _recovery_id.to_i32(); Ok((s, recid)) } - - pub fn zencrypt(&self, msg: &str) -> Result { - let pk = self.get_public_key(); - let pk = &pk.serialize(); - let msg = msg.as_bytes(); - // println!("msg before encrypt: {:?}", msg); - let msg_encrypt = match encrypt(pk, &msg) { - Ok(v) => v, - Err(e) => panic!("error encrypt content: {}", e), - }; - // println!("msg after encrypt: {:?}", &msg_encrypt); - let str_encrypt = hex::encode(&msg_encrypt); - Ok(str_encrypt) - } - - pub fn zdecrypt(&self, msg1: &str) -> Result { - let sk = self.get_secret_key(); - let sk = sk.secret_bytes(); - let mut msg: String = msg1.clone().to_string(); - if msg.len() % 2 == 1 { - msg = "0".to_owned() + &msg; - } - println!("msg to decrypt: {:?}", &msg); - let msg = match hex::decode(&msg) { - Ok(v) => v, - Err(e) => panic!("error decode hex str: {}", e), - }; - let msg_decrypt = match decrypt(&sk, &msg) { - Ok(v) => v, - Err(e) => panic!("error decrypt content: {}", e), - }; - // println!("msg after decrypt: {:?}", &msg_decrypt); - // let msg_decrypt = hex::encode(msg_decrypt); - let str_decrypt = match str::from_utf8(&msg_decrypt) { - Ok(v) => v, - Err(e) => panic!("Invalid UTF-8 sequence: {}", e), - }; - let result = str_decrypt.to_string(); - - Ok(result) - } }