diff --git a/crates/sp_client/Cargo.toml b/crates/sp_client/Cargo.toml index 25f7965..1a725bc 100644 --- a/crates/sp_client/Cargo.toml +++ b/crates/sp_client/Cargo.toml @@ -18,8 +18,7 @@ wasm-logger = "0.2.0" rand = "0.8.5" log = "0.4.6" tsify = { git = "https://github.com/Sosthene00/tsify", branch = "next" } -aes-gcm = "0.10.3" -aes = "0.8.3" +sdk_common = { git = "https://git.4nkweb.com/4nk/sdk_common.git", branch = "demo" } shamir = { git = "https://github.com/Sosthene00/shamir", branch = "master" } img-parts = "0.3.0" diff --git a/crates/sp_client/src/crypto.rs b/crates/sp_client/src/crypto.rs deleted file mode 100644 index 59ab295..0000000 --- a/crates/sp_client/src/crypto.rs +++ /dev/null @@ -1,447 +0,0 @@ -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 serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; - -use aes::cipher::generic_array::GenericArray; -use aes::{ - cipher::consts::{U32, U8}, - Aes256, -}; -use aes_gcm::{ - aead::{Aead, AeadInPlace, KeyInit, Nonce}, - AeadCore, Aes256Gcm, AesGcm, Key, TagSize, -}; -use rand::{thread_rng, RngCore}; - -const HALFKEYSIZE: usize = SECRET_KEY_SIZE / 2; - -const THIRTYTWO: usize = 32; - -pub struct HalfKey([u8; HALFKEYSIZE]); - -impl TryFrom> for HalfKey { - type Error = anyhow::Error; - fn try_from(value: Vec) -> std::prelude::v1::Result { - if value.len() == HALFKEYSIZE { - let mut buf = [0u8; HALFKEYSIZE]; - buf.copy_from_slice(&value); - Ok(HalfKey(buf)) - } else { - Err(Error::msg("Invalid length for HalfKey")) - } - } -} - -impl HalfKey { - pub fn as_slice(&self) -> &[u8] { - &self.0 - } - - pub fn to_inner(&self) -> Vec { - self.0.to_vec() - } -} - -pub enum Purpose { - Login, - ThirtyTwoBytes, -} - -pub type CipherText = Vec; - -pub type EncryptedKey = Vec; - -pub struct Aes256Decryption { - pub purpose: Purpose, - cipher_text: CipherText, - aes_key: [u8; 32], - nonce: [u8; 12], -} - -impl Aes256Decryption { - pub fn new( - purpose: Purpose, - cipher_text: CipherText, - encrypted_aes_key: Vec, // If shared_secret is none this is actually the aes_key - shared_secret: Option, // We don't need that for certain purpose, like Login - ) -> Result { - let mut aes_key = [0u8; 32]; - if let Some(shared_secret) = shared_secret { - if encrypted_aes_key.len() <= 12 { - return Err(Error::msg("encrypted_aes_key is shorter than nonce length")); - } // Actually we could probably test that if the remnant is not a multiple of 32, something's wrong - // 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")); - } - aes_key.copy_from_slice(&aes_key_plain); - } else { - if encrypted_aes_key.len() != 32 { - return Err(Error::msg("Invalid length for decrypted key")); - } - aes_key.copy_from_slice(&encrypted_aes_key); - } - if cipher_text.len() <= 12 { - return Err(Error::msg("cipher_text is shorter than nonce length")); - } - let (message_nonce, message_cipher) = cipher_text.split_at(12); - let mut nonce = [0u8; 12]; - nonce.copy_from_slice(message_nonce); - Ok(Self { - purpose, - cipher_text: message_cipher.to_vec(), - aes_key, - nonce, - }) - } - - pub fn decrypt_with_key(&self) -> Result> { - match self.purpose { - Purpose::Login => { - let half_key = self.decrypt_login()?; - Ok(half_key.to_inner()) - } - Purpose::ThirtyTwoBytes => { - let thirty_two_buf = self.decrypt_thirty_two()?; - Ok(thirty_two_buf.to_vec()) - } - } - } - - fn decrypt_login(&self) -> Result { - 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")); - } - let mut key_half = [0u8; SECRET_KEY_SIZE / 2]; - key_half.copy_from_slice(&plain); - Ok(HalfKey(key_half)) - } - - fn decrypt_thirty_two(&self) -> Result<[u8; THIRTYTWO]> { - 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() != THIRTYTWO { - return Err(Error::msg("Plain text of invalid length, should be 32")); - } - let mut thirty_two = [0u8; THIRTYTWO]; - thirty_two.copy_from_slice(&plain); - Ok(thirty_two) - } -} - -pub struct Aes256Encryption { - pub purpose: Purpose, - plaintext: Vec, - aes_key: [u8; 32], - nonce: [u8; 12], - shared_secrets: HashMap>, -} - -impl Aes256Encryption { - pub fn new(purpose: Purpose, plaintext: Vec) -> Result { - 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(); - Self::import_key(purpose, plaintext, aes_key, nonce) - } - - pub fn set_shared_secret( - &mut self, - shared_secrets: HashMap>, - ) { - self.shared_secrets = shared_secrets; - } - - pub fn encrypt_keys_with_shared_secrets( - &self, - ) -> Result> { - let mut res = HashMap::new(); - let mut rng = thread_rng(); - - for (_, sp_address2shared_secret) in self.shared_secrets.iter() { - for (sp_address, shared_secret) in sp_address2shared_secret { - let cipher = Aes256Gcm::new_from_slice(shared_secret.as_ref()) - .map_err(|e| Error::msg(format!("{}", e)))?; - let nonce = Aes256Gcm::generate_nonce(&mut rng); - let encrypted_key = cipher - .encrypt(&nonce, self.aes_key.as_slice()) - .map_err(|e| Error::msg(format!("{}", e)))?; - - let mut ciphertext = Vec::::with_capacity(nonce.len() + encrypted_key.len()); - ciphertext.extend(nonce); - ciphertext.extend(encrypted_key); - - res.insert(sp_address.to_owned(), ciphertext); - } - } - Ok(res) - } - - pub fn import_key( - purpose: Purpose, - plaintext: Vec, - aes_key: [u8; 32], - nonce: [u8; 12], - ) -> Result { - if plaintext.len() == 0 { - return Err(Error::msg("Can't create encryption for an empty message")); - } - Ok(Self { - purpose, - plaintext, - aes_key, - nonce, - shared_secrets: HashMap::new(), - }) - } - - pub fn encrypt_with_aes_key(&self) -> Result { - match self.purpose { - Purpose::Login => self.encrypt_login(), - Purpose::ThirtyTwoBytes => self.encrypt_thirty_two(), - } - } - - fn encrypt_login(&self) -> Result { - let half_key: HalfKey = self.plaintext.clone().try_into()?; - let cipher = Aes256Gcm::new(&self.aes_key.into()); - let cipher_text = cipher - .encrypt(&self.nonce.into(), half_key.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) - } - - fn encrypt_thirty_two(&self) -> Result { - if self.plaintext.len() != 32 { - return Err(Error::msg("Invalid length, should be 32")); - } - let mut thirty_two = [0u8; 32]; - thirty_two.copy_from_slice(&self.plaintext); - let cipher = Aes256Gcm::new(&self.aes_key.into()); - let cipher_text = cipher - .encrypt(&self.nonce.into(), thirty_two.as_slice()) - .map_err(|e| Error::msg(format!("{}", e)))?; - let mut res = Vec::with_capacity(self.nonce.len() + cipher_text.len()); - log::info!("{}", cipher_text.len()); - res.extend_from_slice(&self.nonce); - res.extend_from_slice(&cipher_text); - Ok(res) - } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use super::*; - - const ALICE_SP_ADDRESS: &str = "tsp1qqw3lqr6xravz9nf8ntazgwwl0fqv47kfjdxsnxs6eutavqfwyv5q6qk97mmyf6dtkdyzqlu2zv6h9j2ggclk7vn705q5u2phglpq7yw3dg5rwpdz"; - const BOB_SP_ADDRESS: &str = "tsp1qq2hlsgrj0gz8kcfkf9flqw5llz0u2vr04telqndku9mcqm6dl4fhvq60t8r78srrf56w9yr7w9e9dusc2wjqc30up6fjwnh9mw3e3veqegdmtf08"; - const TRANSACTION: &str = "4e6d03dec558e1b6624f813bf2da7cd8d8fb1c2296684c08cf38724dcfd8d10b"; - const ALICE_SHARED_SECRET: &str = - "ccf02d364c2641ca129a3fdf49de57b705896e233f7ba6d738991993ea7e2106"; - const BOB_SHARED_SECRET: &str = - "15ef3e377fb842e81de52dbaaea8ba30aeb051a81043ee19264afd27353da521"; - - #[test] - fn new_aes_empty_plaintext() { - let plaintext = Vec::new(); - let aes_enc = Aes256Encryption::new(Purpose::Login, plaintext); - - assert!(aes_enc.is_err()); - } - - #[test] - fn aes_encrypt_login_invalid_length() { - let plaintext = "example"; - let aes_enc_short = Aes256Encryption::new(Purpose::Login, plaintext.as_bytes().to_vec()); - - assert!(aes_enc_short.is_ok()); - - let cipher = aes_enc_short.unwrap().encrypt_with_aes_key(); - - assert!(cipher.is_err()); - - let plaintext = [1u8; 64]; - let aes_enc_long = Aes256Encryption::new(Purpose::Login, plaintext.to_vec()); - - assert!(aes_enc_long.is_ok()); - - let cipher = aes_enc_long.unwrap().encrypt_with_aes_key(); - - assert!(cipher.is_err()); - } - - #[test] - fn aes_encrypt_login() { - let plaintext = [1u8; HALFKEYSIZE]; - let aes_key = Aes256Gcm::generate_key(&mut thread_rng()); - let nonce = Aes256Gcm::generate_nonce(&mut thread_rng()); - let aes_enc = Aes256Encryption::import_key( - Purpose::Login, - plaintext.to_vec(), - aes_key.into(), - nonce.into(), - ); - - assert!(aes_enc.is_ok()); - - let cipher = aes_enc.unwrap().encrypt_with_aes_key(); - - assert!(cipher.is_ok()); - - let mut plain_key = [0u8; 32]; - plain_key.copy_from_slice(&aes_key.to_vec()); - - let aes_dec = - Aes256Decryption::new(Purpose::Login, cipher.unwrap(), plain_key.to_vec(), None); - - assert!(aes_dec.is_ok()); - } - - #[test] - fn aes_encrypt_key() { - let plaintext = [1u8; HALFKEYSIZE]; - let mut aes_enc = Aes256Encryption::new(Purpose::Login, plaintext.to_vec()).unwrap(); - - let mut shared_secrets: HashMap = HashMap::new(); - let mut sp_address2shared_secrets: HashMap = - HashMap::new(); - sp_address2shared_secrets.insert( - ALICE_SP_ADDRESS.try_into().unwrap(), - SharedSecret::from_str(ALICE_SHARED_SECRET).unwrap(), - ); - shared_secrets.insert( - Txid::from_str(TRANSACTION).unwrap(), - sp_address2shared_secrets, - ); - - aes_enc.set_shared_secret(shared_secrets); - - let sp_address2encrypted_keys = aes_enc.encrypt_keys_with_shared_secrets(); - - assert!(sp_address2encrypted_keys.is_ok()); - - let encrypted_key = sp_address2encrypted_keys - .unwrap() - .get(&ALICE_SP_ADDRESS.try_into().unwrap()) - .cloned(); - - let ciphertext = aes_enc.encrypt_with_aes_key(); - - assert!(ciphertext.is_ok()); - - let aes_dec = Aes256Decryption::new( - Purpose::Login, - ciphertext.unwrap(), - encrypted_key.unwrap(), - Some(SharedSecret::from_str(ALICE_SHARED_SECRET).unwrap()), - ); - - assert!(aes_dec.is_ok()); - - let retrieved_plain = aes_dec.unwrap().decrypt_with_key(); - - assert!(retrieved_plain.is_ok()); - - assert!(retrieved_plain.unwrap() == plaintext); - } - - #[test] - fn aes_encrypt_key_many() { - let plaintext = [1u8; THIRTYTWO]; - let mut aes_enc = - Aes256Encryption::new(Purpose::ThirtyTwoBytes, plaintext.to_vec()).unwrap(); - - let mut shared_secrets: HashMap = HashMap::new(); - let mut sp_address2shared_secrets: HashMap = - HashMap::new(); - sp_address2shared_secrets.insert( - ALICE_SP_ADDRESS.try_into().unwrap(), - SharedSecret::from_str(ALICE_SHARED_SECRET).unwrap(), - ); - sp_address2shared_secrets.insert( - BOB_SP_ADDRESS.try_into().unwrap(), - SharedSecret::from_str(BOB_SHARED_SECRET).unwrap(), - ); - shared_secrets.insert( - Txid::from_str(TRANSACTION).unwrap(), - sp_address2shared_secrets, - ); - - aes_enc.set_shared_secret(shared_secrets); - - let mut sp_address2encrypted_keys = aes_enc.encrypt_keys_with_shared_secrets(); - - assert!(sp_address2encrypted_keys.is_ok()); - - // Alice - let encrypted_key = sp_address2encrypted_keys - .as_mut() - .unwrap() - .get(&ALICE_SP_ADDRESS.try_into().unwrap()) - .cloned(); - - let ciphertext = aes_enc.encrypt_with_aes_key(); - - let aes_dec = Aes256Decryption::new( - Purpose::ThirtyTwoBytes, - ciphertext.unwrap(), - encrypted_key.unwrap(), - Some(SharedSecret::from_str(ALICE_SHARED_SECRET).unwrap()), - ); - - let retrieved_plain = aes_dec.unwrap().decrypt_with_key(); - - assert!(retrieved_plain.unwrap() == plaintext); - - // Bob - let encrypted_key = sp_address2encrypted_keys - .unwrap() - .get(&BOB_SP_ADDRESS.try_into().unwrap()) - .cloned(); - - let ciphertext = aes_enc.encrypt_with_aes_key(); - - let aes_dec = Aes256Decryption::new( - Purpose::ThirtyTwoBytes, - ciphertext.unwrap(), - encrypted_key.unwrap(), - Some(SharedSecret::from_str(BOB_SHARED_SECRET).unwrap()), - ); - - let retrieved_plain = aes_dec.unwrap().decrypt_with_key(); - - assert!(retrieved_plain.unwrap() == plaintext); - } -} diff --git a/crates/sp_client/src/lib.rs b/crates/sp_client/src/lib.rs index 95968cb..c0056a6 100644 --- a/crates/sp_client/src/lib.rs +++ b/crates/sp_client/src/lib.rs @@ -5,9 +5,7 @@ use std::sync::{Mutex, MutexGuard}; mod Prd_list; pub mod api; -mod crypto; mod images; -mod network; mod peers; mod process; mod silentpayments; diff --git a/crates/sp_client/src/network.rs b/crates/sp_client/src/network.rs deleted file mode 100644 index fdb16c6..0000000 --- a/crates/sp_client/src/network.rs +++ /dev/null @@ -1,94 +0,0 @@ -use anyhow::{Error, Result}; -use serde::{Deserialize, Serialize}; -use tsify::Tsify; - -const RAWTXTOPIC: &'static str = "rawtx"; -const RAWBLOCKTOPIC: &'static str = "rawblock"; - -#[derive(Debug, Serialize, Deserialize)] -pub enum BitcoinTopic { - RawTx, - RawBlock, -} - -impl BitcoinTopic { - pub fn as_str(&self) -> &str { - match self { - Self::RawTx => RAWTXTOPIC, - Self::RawBlock => RAWBLOCKTOPIC, - } - } -} - -#[derive(Debug, Serialize, Deserialize, Tsify)] -#[tsify(from_wasm_abi, into_wasm_abi)] -pub struct BitcoinNetworkMsg<'a> { - pub topic: BitcoinTopic, - pub data: &'a [u8], - pub sequence: &'a [u8], - pub addon: &'a [u8], -} - -impl<'a> BitcoinNetworkMsg<'a> { - pub fn new(raw_msg: &'a [u8]) -> Result { - let topic: BitcoinTopic; - let data: &[u8]; - let sequence: &[u8]; - let addon: &[u8]; - let addon_len: usize; - let raw_msg_len = raw_msg.len(); - - if raw_msg.starts_with(RAWTXTOPIC.as_bytes()) { - topic = BitcoinTopic::RawTx; - addon_len = 33; - } else if raw_msg.starts_with(RAWBLOCKTOPIC.as_bytes()) { - topic = BitcoinTopic::RawBlock; - addon_len = 0; - } else { - return Err(Error::msg("Unknown prefix")); - } - - data = &raw_msg[topic.as_str().as_bytes().len()..raw_msg_len - 4 - addon_len]; - sequence = &raw_msg[raw_msg_len - 4 - addon_len..]; - addon = &raw_msg[raw_msg_len - addon_len..]; - - Ok(Self { - topic, - data, - sequence, - addon, - }) - } -} - -#[derive(Debug)] -pub enum AnkTopic { - Faucet, -} - -impl AnkTopic { - pub fn as_str(&self) -> &str { - match self { - Self::Faucet => "faucet", - } - } -} - -#[derive(Debug)] -pub struct AnkNetworkMsg<'a> { - pub topic: AnkTopic, - pub content: &'a str, -} - -impl<'a> AnkNetworkMsg<'a> { - pub fn new(raw: &'a str) -> Result { - if raw.starts_with(AnkTopic::Faucet.as_str()) { - Ok(Self { - topic: AnkTopic::Faucet, - content: &raw[AnkTopic::Faucet.as_str().len()..], - }) - } else { - Err(Error::msg("Unknown 4nk message")) - } - } -} diff --git a/crates/sp_client/src/user.rs b/crates/sp_client/src/user.rs index 202ed8d..d1720c1 100644 --- a/crates/sp_client/src/user.rs +++ b/crates/sp_client/src/user.rs @@ -1,8 +1,3 @@ -use aes::cipher::generic_array::GenericArray; -use aes_gcm::aead::Aead; -use aes_gcm::AeadCore; -use aes_gcm::KeyInit; -use aes_gcm::{aead::Buffer, Aes256Gcm, Key}; use anyhow::{Error, Result}; use rand::{self, thread_rng, Rng, RngCore}; use serde::{Deserialize, Serialize}; @@ -29,10 +24,12 @@ use sp_backend::silentpayments::sending::SilentPaymentAddress; use sp_backend::spclient::SpendKey; use sp_backend::spclient::{OutputList, SpWallet}; -use crate::crypto::{Aes256Decryption, Aes256Encryption, HalfKey, Purpose}; use crate::peers::Peer; use crate::user; use crate::MutexExt; +use sdk_common::crypto::{ + AeadCore, Aes256Decryption, Aes256Encryption, Aes256Gcm, HalfKey, KeyInit, Purpose, +}; type PreId = String;