From d081bc1f78035644252ecc0a5711e87d5a2274d9 Mon Sep 17 00:00:00 2001 From: Alex Silva Date: Thu, 21 Mar 2024 20:07:56 +0100 Subject: [PATCH] Add user, secretdata and aesgcm modules --- crates/sp_client/Cargo.toml | 14 ++ crates/sp_client/src/aesgcm.rs | 177 ++++++++++++++ crates/sp_client/src/lib.rs | 5 + crates/sp_client/src/secretdata.rs | 266 ++++++++++++++++++++ crates/sp_client/src/user.rs | 374 +++++++++++++++++++++++++++++ 5 files changed, 836 insertions(+) create mode 100644 crates/sp_client/src/aesgcm.rs create mode 100644 crates/sp_client/src/secretdata.rs create mode 100644 crates/sp_client/src/user.rs diff --git a/crates/sp_client/Cargo.toml b/crates/sp_client/Cargo.toml index 249e291..771af5c 100644 --- a/crates/sp_client/Cargo.toml +++ b/crates/sp_client/Cargo.toml @@ -18,6 +18,20 @@ wasm-logger = "0.2.0" rand = "0.8.5" log = "0.4.6" tsify = { git = "https://github.com/Sosthene00/tsify", branch = "next" } +web-sys = { version = "0.3", features = ["console"] } +bs58 = "0.4" +hex = "0.4.3" +sha2 = "0.10.8" +aes-gcm = "0.10.3" +aes = "0.8.3" +base64 = "0.21.7" +image = "0.24.9" +img-parts = "0.3.0" +bytes = "1.5.0" +scrypt = "0.11.0" +shamir = "2.0.0" +bitcoin = { version = "0.31.1", features = ["serde", "base64"] } + [dev-dependencies] wasm-bindgen-test = "0.3" diff --git a/crates/sp_client/src/aesgcm.rs b/crates/sp_client/src/aesgcm.rs new file mode 100644 index 0000000..3783e01 --- /dev/null +++ b/crates/sp_client/src/aesgcm.rs @@ -0,0 +1,177 @@ +/* This module is temporary. We'll use the module described in key_encription +module defined in sdk_common repository! Some of the methods there were copied here. +*/ +use wasm_bindgen::JsValue; +use web_sys::console; +use core::result::Result as CoreResult; +use rand::RngCore; + +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; + +use aes::cipher::consts::U32; +use aes::cipher::generic_array::GenericArray; +use aes_gcm::{ + aead::{AeadInPlace, KeyInit}, + Aes256Gcm, +}; +use rand::rngs::OsRng; +use hex; +use hex::FromHexError; + + +pub struct Aes256GcmIv96Bit { + pub key: GenericArray, +} + + +impl Aes256GcmIv96Bit { + pub fn new() -> Self { + let mut key_bytes = [0u8; 32]; + OsRng.fill_bytes(&mut key_bytes); + let key = GenericArray::from_slice(&key_bytes); + Aes256GcmIv96Bit { key: key.clone() } + } + + pub fn encrypt(&self, data: &[u8]) -> CoreResult, aes_gcm::Error> { + let cipher = Aes256Gcm::new(&self.key); + let mut nonce = [0u8; 12]; + OsRng.fill_bytes(&mut nonce); + + let mut buffer = data.to_vec(); + cipher.encrypt_in_place(GenericArray::from_slice(&nonce), b"", &mut buffer)?; + + Ok([nonce.to_vec(), buffer].concat()) + } + + pub fn decrypt(&self, data: &[u8]) -> CoreResult, aes_gcm::Error> { + if data.len() < 12 { + return Err(aes_gcm::Error); // Remplacer par une erreur appropriée + } + + let (nonce, encrypted_data) = data.split_at(12); + let mut buffer = encrypted_data.to_vec(); + let cipher = Aes256Gcm::new(&self.key); + cipher.decrypt_in_place(GenericArray::from_slice(nonce), b"", &mut buffer)?; + + Ok(buffer) + } + + pub fn encrypt_string(&self, data: &str) -> CoreResult { + match self.encrypt(data.as_bytes()) { + Ok(encrypted_data) => Ok(base64::encode(encrypted_data)), + Err(_) => Err("Erreur de chiffrement".to_string()), + } + } + + pub fn decrypt_string(&self, data: &str) -> CoreResult { + let decoded_data = match base64::decode(data) { + Ok(data) => data, + Err(_) => return Err("Erreur de décodage Base64".to_string()), + }; + + match self.decrypt(&decoded_data) { + Ok(decrypted_data) => match String::from_utf8(decrypted_data) { + Ok(text) => Ok(text), + Err(_) => Err("Erreur de conversion UTF-8".to_string()), + }, + Err(_) => Err("Erreur de déchiffrement".to_string()), + } + } + + pub fn export_key(&self) -> String { + base64::encode(&self.key) + } + + pub fn import_key(encoded_key: &str) -> CoreResult { + match base64::decode(encoded_key) { + Ok(decoded_key) => { + if decoded_key.len() == 32 { + let key = GenericArray::from_slice(&decoded_key); + Ok(Aes256GcmIv96Bit { key: key.clone() }) + } else { + Err("La taille de la clé n'est pas valide".to_string()) + } + } + Err(_) => Err("Échec du décodage de la clé".to_string()), + } + } + +} + +#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct KeyEncryption { + pub attribute_name: Option, + pub key: Option, + pub algorithm: Option, +} + +impl KeyEncryption { + pub fn new( + attribute_name: Option, + key: Option, + algorithm: Option, + ) -> Self { + KeyEncryption { + attribute_name, + key, + algorithm, + } + } + + pub fn encode(&self, data: String) -> CoreResult { + + if let Some(ref key) = self.key { + let decoded_key = Aes256GcmIv96Bit::import_key(key)?; + let encrypted_data = decoded_key.encrypt_string(&data)?; + Ok(encrypted_data) + } else { + Err("Aucune clé n'est définie".to_string()) + } + } + + pub fn decode(&self, encrypted_data: String) -> CoreResult { + if let Some(ref key) = self.key { + let decoded_key = Aes256GcmIv96Bit::import_key(key)?; + let decrypted_data = decoded_key.decrypt_string(&encrypted_data)?; + Ok(decrypted_data) + } else { + Err("Aucune clé n'est définie".to_string()) + } + } + + pub fn enc(&self, data: Value) -> String { + let data_string = serde_json::to_string(&data).unwrap_or_else(|_| "".to_string()); + self.encode(data_string).unwrap_or_else(|_| "".to_string()) + } + pub fn enc_string(&self, data: String) -> String { + self.enc(Value::String(data)) + } + pub fn enc_i64(&self, data: i64) -> String { + self.enc(Value::Number(data.into())) + } + pub fn enc_u64(&self, data: u64) -> String { + self.enc(Value::Number(data.into())) + } + pub fn enc_u32(&self, data: u32) -> String { + self.enc(Value::Number(data.into())) + } + pub fn enc_vec_string(&self, list: Vec) -> String { + self.enc(Value::Array(list.into_iter().map(Value::String).collect())) + } + pub fn enc_vec_key_encryption(&self, list: Vec) -> String { + // Utilisez `serde_json::to_value` pour convertir chaque `KeyEncryption` en `Value` + let json_list: Vec = list + .into_iter() + .map(|key_enc| serde_json::to_value(key_enc).unwrap_or_else(|_| json!({}))) + .collect(); + + self.enc(Value::Array(json_list)) + } +} + +fn hex_to_generic_array(hex_string: &str) -> Result, FromHexError> { + let byte_vec = hex::decode(hex_string)?; + let array = GenericArray::clone_from_slice(&byte_vec[..32]); + Ok(array) +} \ No newline at end of file diff --git a/crates/sp_client/src/lib.rs b/crates/sp_client/src/lib.rs index 0ce9757..b9931a4 100644 --- a/crates/sp_client/src/lib.rs +++ b/crates/sp_client/src/lib.rs @@ -1,3 +1,8 @@ +#![allow(warnings)] pub mod api; mod injecteurhtml; mod process; +mod user; +mod aesgcm; +mod secretdata; + diff --git a/crates/sp_client/src/secretdata.rs b/crates/sp_client/src/secretdata.rs new file mode 100644 index 0000000..d05e366 --- /dev/null +++ b/crates/sp_client/src/secretdata.rs @@ -0,0 +1,266 @@ +use rand::{thread_rng, RngCore}; + +pub struct SecretData { + pub secret_data: Option, + pub coefficients: Vec>, +} + +#[derive(Debug)] +pub enum ShamirError { + /// The number of shares must be between 1 and 255 + InvalidShareCount, +} + +impl SecretData { + pub fn with_secret(secret: &str, threshold: u8) -> SecretData { + let mut coefficients: Vec> = vec![]; + let mut rng = thread_rng(); + let mut rand_container = vec![0u8; (threshold - 1) as usize]; + for c in secret.as_bytes() { + rng.fill_bytes(&mut rand_container); + let mut coef: Vec = vec![*c]; + for r in rand_container.iter() { + coef.push(*r); + } + coefficients.push(coef); + } + + SecretData { + secret_data: Some(secret.to_string()), + coefficients, + } + } + + pub fn get_share(&self, id: u8) -> Result, ShamirError> { + if id == 0 { + return Err(ShamirError::InvalidShareCount); + } + let mut share_bytes: Vec = vec![]; + let coefficients = self.coefficients.clone(); + for coefficient in coefficients { + //let b = SecretData::accumulate_share_bytes(id, coefficient)?; + let b = r#try!(SecretData::accumulate_share_bytes(id, coefficient)); + share_bytes.push(b); + } + + share_bytes.insert(0, id); + Ok(share_bytes) + } + + pub fn is_valid_share(&self, share: &[u8]) -> bool { + let id = share[0]; + match self.get_share(id) { + Ok(s) => s == share, + _ => false, + } + } + + pub fn recover_secret(threshold: u8, shares: Vec>) -> Option { + if threshold as usize > shares.len() { + println!("Number of shares is below the threshold"); + return None; + } + let mut xs: Vec = vec![]; + + for share in shares.iter() { + if xs.contains(&share[0]) { + println!("Multiple shares with the same first byte"); + return None; + } + + if share.len() != shares[0].len() { + println!("Shares have different lengths"); + return None; + } + + xs.push(share[0].to_owned()); + } + let mut mycoefficients: Vec = vec![]; + let mut mysecretdata: Vec = vec![]; + let rounds = shares[0].len() - 1; + + for byte_to_use in 0..rounds { + let mut fxs: Vec = vec![]; + for share in shares.clone() { + fxs.push(share[1..][byte_to_use]); + } + + match SecretData::full_lagrange(&xs, &fxs) { + None => return None, + Some(resulting_poly) => { + mycoefficients.push(String::from_utf8_lossy(&resulting_poly[..]).to_string()); + mysecretdata.push(resulting_poly[0]); + } + } + } + + match String::from_utf8(mysecretdata) { + Ok(s) => Some(s), + Err(e) => { + println!("{:?}", e); + None + } + } + } + + fn accumulate_share_bytes(id: u8, coefficient_bytes: Vec) -> Result { + if id == 0 { + return Err(ShamirError::InvalidShareCount); + } + let mut accumulator: u8 = 0; + + let mut x_i: u8 = 1; + + for c in coefficient_bytes { + accumulator = SecretData::gf256_add(accumulator, SecretData::gf256_mul(c, x_i)); + x_i = SecretData::gf256_mul(x_i, id); + } + + Ok(accumulator) + } + + fn full_lagrange(xs: &[u8], fxs: &[u8]) -> Option> { + let mut returned_coefficients: Vec = vec![]; + let len = fxs.len(); + for i in 0..len { + let mut this_polynomial: Vec = vec![1]; + + for j in 0..len { + if i == j { + continue; + } + + let denominator = SecretData::gf256_sub(xs[i], xs[j]); + let first_term = SecretData::gf256_checked_div(xs[j], denominator); + let second_term = SecretData::gf256_checked_div(1, denominator); + match (first_term, second_term) { + (Some(a), Some(b)) => { + let this_term = vec![a, b]; + this_polynomial = + SecretData::multiply_polynomials(&this_polynomial, &this_term); + } + (_, _) => return None, + }; + } + if fxs.len() + 1 >= i { + this_polynomial = SecretData::multiply_polynomials(&this_polynomial, &[fxs[i]]) + } + returned_coefficients = + SecretData::add_polynomials(&returned_coefficients, &this_polynomial); + } + Some(returned_coefficients) + } + + #[inline] + fn gf256_add(a: u8, b: u8) -> u8 { + a ^ b + } + + #[inline] + fn gf256_sub(a: u8, b: u8) -> u8 { + SecretData::gf256_add(a, b) + } + + #[inline] + fn gf256_mul(a: u8, b: u8) -> u8 { + if a == 0 || b == 0 { + 0 + } else { + GF256_EXP[((u16::from(GF256_LOG[a as usize]) + u16::from(GF256_LOG[b as usize])) % 255) + as usize] + } + } + + #[inline] + fn gf256_checked_div(a: u8, b: u8) -> Option { + if a == 0 { + Some(0) + } else if b == 0 { + None + } else { + let a_log = i16::from(GF256_LOG[a as usize]); + let b_log = i16::from(GF256_LOG[b as usize]); + + let mut diff = a_log - b_log; + + if diff < 0 { + diff += 255; + } + Some(GF256_EXP[(diff % 255) as usize]) + } + } + + #[inline] + fn multiply_polynomials(a: &[u8], b: &[u8]) -> Vec { + let mut resultterms: Vec = vec![]; + + let mut termpadding: Vec = vec![]; + + for bterm in b { + let mut thisvalue = termpadding.clone(); + for aterm in a { + thisvalue.push(SecretData::gf256_mul(*aterm, *bterm)); + } + resultterms = SecretData::add_polynomials(&resultterms, &thisvalue); + termpadding.push(0); + } + resultterms + } + + #[inline] + fn add_polynomials(a: &[u8], b: &[u8]) -> Vec { + let mut a = a.to_owned(); + let mut b = b.to_owned(); + if a.len() < b.len() { + let mut t = vec![0; b.len() - a.len()]; + a.append(&mut t); + } else if a.len() > b.len() { + let mut t = vec![0; a.len() - b.len()]; + b.append(&mut t); + } + let mut results: Vec = vec![]; + + for i in 0..a.len() { + results.push(SecretData::gf256_add(a[i], b[i])); + } + results + } +} + +static GF256_EXP: [u8; 256] = [ + 0x01, 0x03, 0x05, 0x0f, 0x11, 0x33, 0x55, 0xff, 0x1a, 0x2e, 0x72, 0x96, 0xa1, 0xf8, 0x13, 0x35, + 0x5f, 0xe1, 0x38, 0x48, 0xd8, 0x73, 0x95, 0xa4, 0xf7, 0x02, 0x06, 0x0a, 0x1e, 0x22, 0x66, 0xaa, + 0xe5, 0x34, 0x5c, 0xe4, 0x37, 0x59, 0xeb, 0x26, 0x6a, 0xbe, 0xd9, 0x70, 0x90, 0xab, 0xe6, 0x31, + 0x53, 0xf5, 0x04, 0x0c, 0x14, 0x3c, 0x44, 0xcc, 0x4f, 0xd1, 0x68, 0xb8, 0xd3, 0x6e, 0xb2, 0xcd, + 0x4c, 0xd4, 0x67, 0xa9, 0xe0, 0x3b, 0x4d, 0xd7, 0x62, 0xa6, 0xf1, 0x08, 0x18, 0x28, 0x78, 0x88, + 0x83, 0x9e, 0xb9, 0xd0, 0x6b, 0xbd, 0xdc, 0x7f, 0x81, 0x98, 0xb3, 0xce, 0x49, 0xdb, 0x76, 0x9a, + 0xb5, 0xc4, 0x57, 0xf9, 0x10, 0x30, 0x50, 0xf0, 0x0b, 0x1d, 0x27, 0x69, 0xbb, 0xd6, 0x61, 0xa3, + 0xfe, 0x19, 0x2b, 0x7d, 0x87, 0x92, 0xad, 0xec, 0x2f, 0x71, 0x93, 0xae, 0xe9, 0x20, 0x60, 0xa0, + 0xfb, 0x16, 0x3a, 0x4e, 0xd2, 0x6d, 0xb7, 0xc2, 0x5d, 0xe7, 0x32, 0x56, 0xfa, 0x15, 0x3f, 0x41, + 0xc3, 0x5e, 0xe2, 0x3d, 0x47, 0xc9, 0x40, 0xc0, 0x5b, 0xed, 0x2c, 0x74, 0x9c, 0xbf, 0xda, 0x75, + 0x9f, 0xba, 0xd5, 0x64, 0xac, 0xef, 0x2a, 0x7e, 0x82, 0x9d, 0xbc, 0xdf, 0x7a, 0x8e, 0x89, 0x80, + 0x9b, 0xb6, 0xc1, 0x58, 0xe8, 0x23, 0x65, 0xaf, 0xea, 0x25, 0x6f, 0xb1, 0xc8, 0x43, 0xc5, 0x54, + 0xfc, 0x1f, 0x21, 0x63, 0xa5, 0xf4, 0x07, 0x09, 0x1b, 0x2d, 0x77, 0x99, 0xb0, 0xcb, 0x46, 0xca, + 0x45, 0xcf, 0x4a, 0xde, 0x79, 0x8b, 0x86, 0x91, 0xa8, 0xe3, 0x3e, 0x42, 0xc6, 0x51, 0xf3, 0x0e, + 0x12, 0x36, 0x5a, 0xee, 0x29, 0x7b, 0x8d, 0x8c, 0x8f, 0x8a, 0x85, 0x94, 0xa7, 0xf2, 0x0d, 0x17, + 0x39, 0x4b, 0xdd, 0x7c, 0x84, 0x97, 0xa2, 0xfd, 0x1c, 0x24, 0x6c, 0xb4, 0xc7, 0x52, 0xf6, 0x01, +]; + +static GF256_LOG: [u8; 256] = [ + 0x00, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1a, 0xc6, 0x4b, 0xc7, 0x1b, 0x68, 0x33, 0xee, 0xdf, 0x03, + 0x64, 0x04, 0xe0, 0x0e, 0x34, 0x8d, 0x81, 0xef, 0x4c, 0x71, 0x08, 0xc8, 0xf8, 0x69, 0x1c, 0xc1, + 0x7d, 0xc2, 0x1d, 0xb5, 0xf9, 0xb9, 0x27, 0x6a, 0x4d, 0xe4, 0xa6, 0x72, 0x9a, 0xc9, 0x09, 0x78, + 0x65, 0x2f, 0x8a, 0x05, 0x21, 0x0f, 0xe1, 0x24, 0x12, 0xf0, 0x82, 0x45, 0x35, 0x93, 0xda, 0x8e, + 0x96, 0x8f, 0xdb, 0xbd, 0x36, 0xd0, 0xce, 0x94, 0x13, 0x5c, 0xd2, 0xf1, 0x40, 0x46, 0x83, 0x38, + 0x66, 0xdd, 0xfd, 0x30, 0xbf, 0x06, 0x8b, 0x62, 0xb3, 0x25, 0xe2, 0x98, 0x22, 0x88, 0x91, 0x10, + 0x7e, 0x6e, 0x48, 0xc3, 0xa3, 0xb6, 0x1e, 0x42, 0x3a, 0x6b, 0x28, 0x54, 0xfa, 0x85, 0x3d, 0xba, + 0x2b, 0x79, 0x0a, 0x15, 0x9b, 0x9f, 0x5e, 0xca, 0x4e, 0xd4, 0xac, 0xe5, 0xf3, 0x73, 0xa7, 0x57, + 0xaf, 0x58, 0xa8, 0x50, 0xf4, 0xea, 0xd6, 0x74, 0x4f, 0xae, 0xe9, 0xd5, 0xe7, 0xe6, 0xad, 0xe8, + 0x2c, 0xd7, 0x75, 0x7a, 0xeb, 0x16, 0x0b, 0xf5, 0x59, 0xcb, 0x5f, 0xb0, 0x9c, 0xa9, 0x51, 0xa0, + 0x7f, 0x0c, 0xf6, 0x6f, 0x17, 0xc4, 0x49, 0xec, 0xd8, 0x43, 0x1f, 0x2d, 0xa4, 0x76, 0x7b, 0xb7, + 0xcc, 0xbb, 0x3e, 0x5a, 0xfb, 0x60, 0xb1, 0x86, 0x3b, 0x52, 0xa1, 0x6c, 0xaa, 0x55, 0x29, 0x9d, + 0x97, 0xb2, 0x87, 0x90, 0x61, 0xbe, 0xdc, 0xfc, 0xbc, 0x95, 0xcf, 0xcd, 0x37, 0x3f, 0x5b, 0xd1, + 0x53, 0x39, 0x84, 0x3c, 0x41, 0xa2, 0x6d, 0x47, 0x14, 0x2a, 0x9e, 0x5d, 0x56, 0xf2, 0xd3, 0xab, + 0x44, 0x11, 0x92, 0xd9, 0x23, 0x20, 0x2e, 0x89, 0xb4, 0x7c, 0xb8, 0x26, 0x77, 0x99, 0xe3, 0xa5, + 0x67, 0x4a, 0xed, 0xde, 0xc5, 0x31, 0xfe, 0x18, 0x0d, 0x63, 0x8c, 0x80, 0xc0, 0xf7, 0x70, 0x07, +]; \ No newline at end of file diff --git a/crates/sp_client/src/user.rs b/crates/sp_client/src/user.rs new file mode 100644 index 0000000..9fdc76f --- /dev/null +++ b/crates/sp_client/src/user.rs @@ -0,0 +1,374 @@ +use bitcoin::secp256k1::SecretKey; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use rand::{self, Rng,thread_rng, RngCore}; +use wasm_bindgen::JsValue; +use wasm_bindgen::prelude::*; +use web_sys::console; +use anyhow::Error; + +use crate::aesgcm::{Aes256GcmIv96Bit,KeyEncryption}; +use crate::secretdata::SecretData; +use hex; +use sha2::{Sha256, Digest}; +use bytes::Bytes; +use std::fs::File; + +use crate::api::{generate_sp_wallet_return,generate_sp_wallet}; +use sp_backend::spclient::SpendKey; +use sp_backend::spclient::{SpClient, OutputList}; +use sp_backend::silentpayments::sending::SilentPaymentAddress; + +use img_parts::jpeg::Jpeg; +use img_parts::{ImageEXIF, ImageICC}; + +use scrypt::{ + password_hash::{ + rand_core::OsRng, + PasswordHash, PasswordHasher, PasswordVerifier, SaltString + }, + Scrypt +}; + +//extern crate shamir; +//use shamir::SecretData; + + +#[wasm_bindgen] +#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct User { + image_recover: ImageRecover, + image_revoke: ImageRevoke, + sharding: Sharding, + pre_id: String, + recovered_spend_key: Option, +} + +#[wasm_bindgen] +impl User { + #[wasm_bindgen(constructor)] + pub fn new(new_password: &str, image_to_recover: &[u8], image_to_revoke: &[u8]) -> Self { + + let password = new_password.to_string(); + let random_seed1 = generate_random_key(32); + let random_seed2 = generate_random_key(32); + //wallet recover + let wallet_rec: String = match generate_sp_wallet(None,50000, true) { + Some(sp_wallet) => sp_wallet.sp_client_json, + None => panic!("No wallet recover available"), + }; + let sp_client_rec: SpClient = serde_json::from_str(&wallet_rec).unwrap(); + let priv_recover_scan_key_bytes = sp_client_rec.get_scan_key().secret_bytes(); + let priv_recover_scan_key = from_b64_to_hex(&base64::encode(priv_recover_scan_key_bytes)); + let priv_recover_spend_key_bytes = match sp_client_rec.get_spend_key(){ + SpendKey::Secret(key)=> key.secret_bytes(), + SpendKey::Public(_) => panic!("No recover spend key created on Signet"), + }; + let priv_recover_spend_key = from_b64_to_hex(&base64::encode(priv_recover_spend_key_bytes)); + console::log_2(&"priv_recover_spend_key".into(),&JsValue::from_str(&priv_recover_spend_key)); + //wallet revoke + let wallet_rev: String = match generate_sp_wallet(None, 50000, true) { + Some(sp_wallet) => sp_wallet.sp_client_json, + None => panic!("No wallet revoke available"), + }; + let sp_client_rev: SpClient = serde_json::from_str(&wallet_rec).unwrap(); + let priv_revoke_scan_key_bytes = sp_client_rev.get_scan_key().secret_bytes(); + let priv_revoke_scan_key = from_b64_to_hex(&base64::encode(priv_revoke_scan_key_bytes)); + let priv_revoke_spend_key_bytes = match sp_client_rev.get_spend_key(){ + SpendKey::Secret(key)=> key.secret_bytes(), + SpendKey::Public(_) => panic!("No revoke spend key created on Signet"), + }; + let priv_revoke_spend_key = from_b64_to_hex(&base64::encode(priv_revoke_spend_key_bytes)); + + //split recover spend key + let (part1_key, part2_key) = priv_recover_spend_key.split_at(priv_recover_spend_key.len()/2); + + //part1 enc + let pwd_hash_part1 = from_hex_to_b64(&sha_256(&format!("{}{}",password, &random_seed1))); + let key_enc_part1 = KeyEncryption::new(None, Some(pwd_hash_part1.clone()), None); + let part1_key_enc = key_enc_part1.enc_string(part1_key.to_string()); + //part2 enc + let pwd_hash_part2 = from_hex_to_b64(&sha_256(&format!("{}{}", password, random_seed2))); + let key_enc_part2 = KeyEncryption::new(None, Some(pwd_hash_part2.clone()), None); + let part2_key_enc = key_enc_part2.enc_string(part2_key.to_string()); + //image recover + let image_recover = ImageRecover::new(image_to_recover, &random_seed1, &random_seed2,&part1_key_enc); + //image revoke + //let priv_revoke_spend_key = wallet.priv_revoke_spend_key.to_owned(); + //let priv_revoke_scan_key = wallet.priv_revoke_scan_key.to_owned(); + let image_revoke = ImageRevoke::new(image_to_revoke,&priv_revoke_spend_key,&priv_revoke_scan_key); + //create shardings + let sharding = Sharding::new(&part2_key_enc, 10u8); //nMembers = 10 for testing, need to recover nmember elsewhere + //Pre ID + let pre_id = sha_256(&format!("{}{}",password, part2_key_enc)); + + //Create PRDList + //@todo + //Send messages PRDList + //@todo + //Receive List Items (PCD) + + console::log_1(&"authentication: ok".into()); + User {image_recover, + image_revoke, + sharding, + pre_id, + recovered_spend_key:None + } + } + + pub fn login(&self,password: &str, image_recover: &[u8]) -> Option{ + let exif_image_bytes = read_exif(image_recover).unwrap_or_else(|error| { + panic!("Unable to read the image exif: {}", error); + }); + + let exif_image_string = String::from_utf8(exif_image_bytes.to_vec()).unwrap(); + let exif_image_json: Value = serde_json::from_str(&exif_image_string).unwrap(); + let random_seed1 = exif_image_json["random_seed1"].as_str().unwrap_or("N/A"); + let random_seed2 = exif_image_json["random_seed2"].as_str().unwrap_or("N/A"); + let part1_key_enc = exif_image_json["part1_key_enc"].as_str().unwrap_or("N/A"); + let part1_recovered = Self::recover_part1(password,random_seed1,part1_key_enc); + let part1_trimmed = part1_recovered.trim_matches('"'); + + //@todo: get shardings from member managers!! + let shardings = self.sharding.shares_vec.clone(); // temporary + + let part2_recovered = Self::recover_part2(&password,&random_seed2, shardings); + let part2_trimmed = part2_recovered.trim_matches('"'); + let recover_key_hex: String = format!("{}{}", part1_trimmed, part2_trimmed); + + Some(recover_key_hex) + } + + fn recover_part1(password: &str, random_seed1: &str, part1_key_enc: & str) -> String{ + let pwd_hash_part1 = from_hex_to_b64(&sha_256(&format!("{}{}",password, random_seed1))); + let key_dec_part1 = KeyEncryption::new(None, Some(pwd_hash_part1), None); + let part1_key_recovered = key_dec_part1.decode(part1_key_enc.to_string()).unwrap_or_else(|_| "".to_string()); + part1_key_recovered + } + + fn recover_part2(password: &str, random_seed2: &str, shares_vec: Vec>) -> String{ + let quorum_sharding = (Sharding::QUORUM_SHARD * f32::from(shares_vec.len() as u8)).round() as u8; + let part2_key_enc = SecretData::recover_secret(quorum_sharding, shares_vec).unwrap(); + let pwd_hash_part2 = from_hex_to_b64(&sha_256(&format!("{}{}",password, random_seed2))); + let key_dec_part2 = KeyEncryption::new(None, Some(pwd_hash_part2), None); + let part2_key_recovered = key_dec_part2.decode(part2_key_enc).unwrap_or_else(|_| "".to_string()); + part2_key_recovered + } + + + //not used + pub fn pbkdf2(password: &str, data: & str)->String { + let data_salt = data.trim_end_matches('='); + let salt = SaltString::from_b64(data_salt) + .map(|s| { s }) + .unwrap_or_else(|_| { + panic!("Failed to parse salt value from base64 string") + }); + + let mut password_hash = String::new(); + if let Ok(pwd) = Scrypt.hash_password(password.as_bytes(), &salt) { + password_hash.push_str(&pwd.to_string()); + } + sha_256(&password_hash) + + + } + + pub fn get_image_recover(&self)-> Vec{ + return self.image_recover.image_recover_bytes.clone(); + } + + pub fn get_exif_image(&self,image:&[u8])-> Vec{ + return read_exif(image).expect("Error reading the exif"); + } + + pub fn get_image_revoke(&self)-> Vec{ + return self.image_revoke.image_revoke_bytes.clone(); + } + + // Test sharing JS side + pub fn get_shares(&self)->Vec{ + self.sharding.shares_format_str.clone() + + } + + //Test sharing Js side + pub fn get_secret(&self,shardings: Vec)->String{ + let mut shares_vec = Vec::new(); + + for s in shardings.iter(){ + let bytes_vec: Vec = s + .trim_matches(|c| c == '[' || c == ']') + .split(',') + .filter_map(|s| s.trim().parse().ok()) + .collect(); + shares_vec.push(bytes_vec); + + } + self.sharding.recover_secrete(shares_vec.clone()) + } + + } + +#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct ImageRecover { + image_recover_bytes: Vec, +} + +impl ImageRecover{ + pub fn new(image_to_recover: &[u8], + random_seed1: &str, + random_seed2: &str, + part1_key_enc: &str, + ) -> Self{ + let data_exif_json = json!({ + "random_seed1": random_seed1, + "random_seed2": random_seed2, + "part1_key_enc": part1_key_enc + }); + + let data = serde_json::to_string(&data_exif_json).unwrap(); + let image_recover = write_exif(image_to_recover, &data); + ImageRecover{ + image_recover_bytes: image_recover.expect("Image recover not generated!") + } + } + +} + +#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct ImageRevoke { + image_revoke_bytes: Vec, +} +impl ImageRevoke{ + pub fn new(image_to_revoke: &[u8], + priv_revoke_spend_key: &str, + priv_revoke_scan_key: &str, + + )->Self{ + let data_exif_json = json!({ + "priv_revoke_spend_key":priv_revoke_spend_key, + "priv_revoke_scan_key":priv_revoke_scan_key + }); + + let data = serde_json::to_string(&data_exif_json).unwrap(); + let image_revoke = write_exif(image_to_revoke, &data); + ImageRevoke{ + image_revoke_bytes: image_revoke.expect("Image revoke not generated!") + } + } + +} + + +#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Sharding { + shares_vec: Vec>, + shares_format_str: Vec, + +} + +impl Sharding{ + const QUORUM_SHARD: f32= 0.80_f32; + pub fn new( + part2_key_enc: &str, + number_members: u8, + )->Self{ + let secret_data = SecretData::with_secret(part2_key_enc, number_members); + let mut shares_format_str: Vec = Vec::new(); + let shares_vec = (1..=number_members).map(|i| match secret_data.get_share(i) + { + Ok(share) => { + let string = format!("[{}]", share.clone().iter() + .map(|b| format!("{}", b)) + .collect::>() + .join(",")); + shares_format_str.push(string.clone()); + share + }, + Err(_) => panic!("Not able to recover the shares!"), + } + ).collect::>(); + + Sharding{ + shares_vec, + shares_format_str + + } + } + + pub fn recover_secrete(&self, shares: Vec>) -> String { + let quorum_sharding = (Self::QUORUM_SHARD * f32::from(shares.len() as u8)).round() as u8; + SecretData::recover_secret(quorum_sharding, shares).unwrap() + } +} + +//associated functions +pub fn generate_random_key(length:usize) ->String { + let mut rng = rand::thread_rng(); + let random_bytes: Vec = (0..length) + .map(|_| rng.gen_range(0x00..=0xFF)) + .collect(); + base64::encode(random_bytes) +} + +pub fn sha_256(data: &str)-> String{ + let mut hasher = Sha256::new(); + hasher.update(data); + let result = hasher.finalize(); + hex::encode(result) +} + + pub fn write_exif(image_to_recover: &[u8], data: &str) -> Result, String>{ + let image_to_recover_bytes = Bytes::from(image_to_recover.to_vec()); + let mut jpeg = Jpeg::from_bytes(image_to_recover_bytes).unwrap(); + let data_bytes = Bytes::from(data.as_bytes().to_vec()); + jpeg.set_exif(Some(data_bytes)); + let output_image_bytes = jpeg.encoder().bytes(); + let output_image = output_image_bytes.as_ref(); + Ok(output_image.to_vec()) +} + + + pub fn read_exif(image: &[u8])->Result, String>{ + let image_bytes = Bytes::from(image.to_vec()); + let jpeg = Jpeg::from_bytes(image_bytes).unwrap(); + + //exif out + let mut exif_image = Bytes::new(); + if let Some(ref meta) = jpeg.exif(){ + exif_image = meta.clone(); + }else { + return Err("No exif data".to_string()); + } + let exif_bytes =exif_image.as_ref(); + Ok(exif_bytes.to_vec()) +} + +//change for return Result? +pub fn from_hex_to_b58(hex_string: &str)-> String{ + let decoded_data = hex::decode(hex_string).expect("Failed to decode hex string"); + let base58_string = bs58::encode(decoded_data).into_string(); + base58_string +} +//change for return Result? +pub fn from_b58_to_hex(base58_string: &str)-> String{ + let decoded_data = bs58::decode(base58_string.to_owned()).into_vec().unwrap(); + let hex_string = decoded_data.iter().map(|b| format!("{:02x}", b)).collect::(); + hex_string +} + +fn from_b64_to_hex(base64_string:&str)->String{ + let decoded_data = base64::decode(base64_string).unwrap(); + let hex_string = decoded_data.iter().map(|b| format!("{:02x}", b)).collect::(); + hex_string +} +fn from_hex_to_b64(hex_string:&str)->String{ + let decoded_data = hex::decode(hex_string).expect("Failed to decode hex string"); + let base64_string = base64::encode(decoded_data); + base64_string +} + + +