encryption refactoring

This commit is contained in:
Sosthene00 2024-03-25 23:02:04 +01:00
parent 40267ea224
commit 19eb1b66ab
2 changed files with 195 additions and 218 deletions

View File

@ -1,173 +1,168 @@
/* 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 core::result::Result as CoreResult;
use rand::RngCore;
use std::collections::HashMap;
use anyhow::{Error, Result};
use sp_backend::{
bitcoin::{
consensus::serde::hex,
hex::DisplayHex,
key::constants::SECRET_KEY_SIZE,
secp256k1::{ecdh::SharedSecret, SecretKey},
Txid,
},
silentpayments::sending::SilentPaymentAddress,
};
use wasm_bindgen::JsValue;
use web_sys::console;
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 aes::{
cipher::consts::{U32, U8},
Aes256,
};
use hex;
use hex::FromHexError;
use rand::rngs::OsRng;
use aes_gcm::{
aead::{Aead, AeadInPlace, KeyInit, Nonce},
AeadCore, Aes256Gcm, AesGcm, Key, TagSize,
};
use rand::{thread_rng, RngCore};
pub struct Aes256GcmIv96Bit {
pub key: GenericArray<u8, U32>,
const HALFKEYSIZE: usize = SECRET_KEY_SIZE / 2;
pub type HalfKey = [u8; HALFKEYSIZE];
pub enum EncryptionTarget {
Login(HalfKey),
}
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<Vec<u8>, 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<Vec<u8>, 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<String, String> {
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<String, String> {
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<Self, String> {
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()),
}
}
pub enum DecryptionTarget {
Login,
}
#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct KeyEncryption {
pub attribute_name: Option<String>,
pub key: Option<String>,
pub algorithm: Option<String>,
pub enum PlainText {
Login(HalfKey),
}
impl KeyEncryption {
pub type CipherText = Vec<u8>;
pub struct Aes256Decryption {
target: DecryptionTarget,
aes_key: [u8; 32],
nonce: [u8; 12],
cipher_text: CipherText,
}
impl Aes256Decryption {
pub fn new(
attribute_name: Option<String>,
key: Option<String>,
algorithm: Option<String>,
) -> Self {
KeyEncryption {
attribute_name,
key,
algorithm,
target: DecryptionTarget,
encrypted_aes_key: Vec<u8>,
shared_secret: SharedSecret,
cipher_text: CipherText,
) -> Result<Self> {
if encrypted_aes_key.len() <= 12 {
return Err(Error::msg("encrypted_aes_key is shorter than nonce length"));
}
// take the first 12 bytes form encrypted_aes_key as nonce
let (decrypt_key_nonce, encrypted_key) = encrypted_aes_key.split_at(12);
// decrypt key with shared_secret obtained from transaction
let decrypt_key_cipher = Aes256Gcm::new_from_slice(shared_secret.as_ref())
.map_err(|e| Error::msg(format!("{}", e)))?;
let aes_key_plain = decrypt_key_cipher
.decrypt(decrypt_key_nonce.into(), encrypted_key)
.map_err(|e| Error::msg(format!("{}", e)))?;
if aes_key_plain.len() != 32 {
return Err(Error::msg("Invalid length for decrypted key"));
}
let mut aes_key = [0u8; 32];
aes_key.copy_from_slice(&aes_key_plain);
if cipher_text.len() <= 12 {
return Err(Error::msg("cipher_text is shorter than nonce lenght"));
}
let (message_nonce, message_cipher) = cipher_text.split_at(12);
let mut nonce = [0u8; 12];
nonce.copy_from_slice(message_nonce);
Ok(Self {
target,
aes_key,
nonce,
cipher_text: message_cipher.to_vec(),
})
}
pub fn decrypt_with_key(&self) -> Result<PlainText> {
match self.target {
DecryptionTarget::Login => self.decrypt_login()
}
}
pub fn encode(&self, data: String) -> CoreResult<String, String> {
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())
fn decrypt_login(&self) -> Result<PlainText> {
let cipher = Aes256Gcm::new(&self.aes_key.into());
let plain = cipher
.decrypt(&self.nonce.into(), &*self.cipher_text)
.map_err(|e| Error::msg(format!("{}", e)))?;
if plain.len() != SECRET_KEY_SIZE / 2 {
return Err(Error::msg("Plain text of invalid lenght for a login"));
}
}
pub fn decode(&self, encrypted_data: String) -> CoreResult<String, String> {
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>) -> String {
self.enc(Value::Array(list.into_iter().map(Value::String).collect()))
}
pub fn enc_vec_key_encryption(&self, list: Vec<KeyEncryption>) -> String {
// Utilisez `serde_json::to_value` pour convertir chaque `KeyEncryption` en `Value`
let json_list: Vec<Value> = list
.into_iter()
.map(|key_enc| serde_json::to_value(key_enc).unwrap_or_else(|_| json!({})))
.collect();
self.enc(Value::Array(json_list))
let mut key_half = [0u8; SECRET_KEY_SIZE / 2];
key_half.copy_from_slice(&plain);
Ok(PlainText::Login(key_half))
}
}
fn hex_to_generic_array(hex_string: &str) -> Result<GenericArray<u8, U32>, FromHexError> {
let byte_vec = hex::decode(hex_string)?;
let array = GenericArray::clone_from_slice(&byte_vec[..32]);
Ok(array)
pub struct Aes256Encryption {
pub target: EncryptionTarget,
aes_key: [u8; 32],
nonce: [u8; 12],
shared_secrets: HashMap<Txid, HashMap<SilentPaymentAddress, SharedSecret>>,
}
impl Aes256Encryption {
pub fn new(target: EncryptionTarget) -> Result<Self> {
let mut rng = thread_rng();
let aes_key: [u8; 32] = Aes256Gcm::generate_key(&mut rng).into();
let nonce: [u8; 12] = Aes256Gcm::generate_nonce(&mut rng).into();
Ok(Self {
target,
aes_key,
nonce,
shared_secrets: HashMap::new(),
})
}
pub fn set_shared_secret(
&mut self,
shared_secrets: HashMap<Txid, HashMap<SilentPaymentAddress, SharedSecret>>,
) -> Result<()> {
unimplemented!();
}
pub fn import_key(
target: EncryptionTarget,
aes_key: [u8; 32],
nonce: [u8; 12],
) -> Result<Self> {
Ok(Self {
target,
aes_key,
nonce,
shared_secrets: HashMap::new(),
})
}
pub fn encrypt_with_aes_key(&self) -> Result<CipherText> {
match self.target {
EncryptionTarget::Login(half_key) => self.encrypt_login(half_key),
}
}
fn encrypt_login(&self, plaintext: HalfKey) -> Result<CipherText> {
let cipher = Aes256Gcm::new(&self.aes_key.into());
let cipher_text = cipher
.encrypt(&self.nonce.into(), plaintext.as_slice())
.map_err(|e| Error::msg(format!("{}", e)))?;
let mut res = Vec::with_capacity(self.nonce.len() + cipher_text.len());
res.extend_from_slice(&self.nonce);
res.extend_from_slice(&cipher_text);
Ok(res)
}
}

View File

@ -5,6 +5,7 @@ use aes_gcm::KeyInit;
use aes_gcm::{aead::Buffer, Aes256Gcm, Key};
use anyhow::{Error, Result};
use bytes::Buf;
use js_sys::JsString;
use rand::{self, thread_rng, Rng, RngCore};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
@ -15,21 +16,14 @@ use sp_backend::bitcoin::secp256k1::SecretKey;
use tsify::Tsify;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
use web_sys::console;
use web_sys::js_sys::JsString;
use crate::aesgcm::{Aes256GcmIv96Bit, KeyEncryption};
// use crate::secretdata::SecretData;
use bytes::Bytes;
use hex;
use sha2::{Digest, Sha256};
use shamir::SecretData;
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::str::FromStr;
use crate::api::{generate_sp_wallet, generate_sp_wallet_return};
use sp_backend::bitcoin::secp256k1::constants::SECRET_KEY_SIZE;
use sp_backend::silentpayments::bitcoin_hashes::sha256;
use sp_backend::silentpayments::sending::SilentPaymentAddress;
@ -39,10 +33,8 @@ use sp_backend::spclient::{OutputList, SpClient};
use img_parts::jpeg::Jpeg;
use img_parts::{ImageEXIF, ImageICC};
use scrypt::{
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Scrypt,
};
use crate::aesgcm::HalfKey;
use crate::aesgcm::{Aes256Encryption, EncryptionTarget};
//extern crate shamir;
//use shamir::SecretData;
@ -66,6 +58,8 @@ impl User {
recover_image: &[u8],
revoke_image: &[u8],
) -> Result<Self> {
let mut rng = thread_rng();
// image revoke
// We just take the 2 revoke keys and put it in the revoke_img file
let mut revoke_data = [0u8; SECRET_KEY_SIZE * 2];
@ -76,20 +70,18 @@ impl User {
// split recover spend key
let (part1_key, part2_key) = recover_spend_key.as_ref().split_at(SECRET_KEY_SIZE / 2);
let mut recover_data = Vec::<u8>::with_capacity(88);
let mut recover_data = Vec::<u8>::with_capacity(64); // 32 * 2
// generate 2 tokens of 32B entropy
let mut entropy_1 = [0u8; 32];
entropy_1.copy_from_slice(&generate_random_key(32));
let mut entropy_2 = [0u8; 32];
entropy_2.copy_from_slice(&generate_random_key(32));
let mut entropy_1: [u8; 32] = Aes256Gcm::generate_key(&mut rng).into();
let mut entropy_2: [u8; 32] = Aes256Gcm::generate_key(&mut rng).into();
recover_data.extend_from_slice(&entropy_1);
recover_data.extend_from_slice(&entropy_2);
// convert the password in a Vec<u8>
// convert the password in a String, i.e. a Vec<u8>
// Be careful of javascript strings: https://rustwasm.github.io/wasm-bindgen/reference/types/str.html#utf-16-vs-utf-8
assert!(user_password.is_valid_utf16());
assert!(user_password.is_valid_utf16()); // we can think better than panicking in this case
let password: String = user_password.into();
// hash the concatenation
@ -99,20 +91,17 @@ impl User {
let hash1 = sha256::Hash::from_engine(engine);
// take it as a AES key
let key1: &Key<Aes256Gcm> = hash1.as_byte_array().try_into()?;
let cipher = Aes256Gcm::new(&key1);
let part1_encryption = Aes256Encryption::import_key(
EncryptionTarget::Login(part1_key.try_into()?),
hash1.to_byte_array(),
Aes256Gcm::generate_nonce(&mut rng).into(),
)?;
// encrypt the part1 of the key
let nonce1 = Aes256Gcm::generate_nonce(&mut thread_rng());
let nonce2 = Aes256Gcm::generate_nonce(&mut thread_rng());
let cipher_recover_part1 = cipher
.encrypt(&nonce1, part1_key)
.map_err(|e| anyhow::Error::msg(format!("{}", e)))?;
let cipher_recover_part1 = part1_encryption.encrypt_with_aes_key()?;
log::debug!("cipher_part1 length: {}", cipher_recover_part1.len());
recover_data.extend_from_slice(&nonce1);
recover_data.extend_from_slice(&nonce2);
recover_data.extend_from_slice(&cipher_recover_part1);
//image recover
@ -125,13 +114,14 @@ impl User {
let hash2 = sha256::Hash::from_engine(engine);
// take it as a AES key
let key2: &Key<Aes256Gcm> = hash2.as_byte_array().try_into()?;
let cipher = Aes256Gcm::new(&key2);
let part2_encryption = Aes256Encryption::import_key(
EncryptionTarget::Login(part2_key.try_into()?),
hash2.to_byte_array(),
Aes256Gcm::generate_nonce(&mut rng).into(),
)?;
// encrypt the part2 of the key
let cipher_recover_part2 = cipher
.encrypt(&nonce2, part2_key)
.map_err(|e| anyhow::Error::msg(format!("{}", e)))?;
let cipher_recover_part2 = part2_encryption.encrypt_with_aes_key()?;
//create shardings
let sharding = Sharding::new(&cipher_recover_part2.to_lower_hex_string(), 10u8); //nMembers = 10 for testing, need to recover nmember elsewhere
@ -341,14 +331,6 @@ impl Sharding {
}
}
//associated functions
pub fn generate_random_key(length: usize) -> Vec<u8> {
let mut rng = rand::thread_rng();
let mut entropy = Vec::<u8>::with_capacity(length);
rng.fill_bytes(&mut entropy);
entropy
}
pub fn write_exif(image_to_recover: &[u8], data: &[u8]) -> Result<Vec<u8>> {
let mut jpeg = Jpeg::from_bytes(Bytes::from(image_to_recover.to_vec()))?;
let data_bytes = Bytes::from(data.to_owned());
@ -373,32 +355,32 @@ pub fn read_exif(image: &[u8]) -> Result<Vec<u8>, String> {
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::<String>();
hex_string
}
// //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::<String>();
// 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::<String>();
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
}
// 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::<String>();
// 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
// }