From ef311f7303c82e10ef56b79b14c01c360dd19be1 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Wed, 23 Oct 2024 12:27:42 +1100 Subject: [PATCH 01/45] Encryption and simple key management --- packages/ciphernode/Cargo.lock | 29 ++++ packages/ciphernode/Cargo.toml | 4 + packages/ciphernode/data/src/snapshot.rs | 2 +- packages/ciphernode/enclave/Cargo.toml | 9 +- packages/ciphernode/enclave/src/main.rs | 5 +- packages/ciphernode/keyshare/Cargo.toml | 13 +- .../ciphernode/keyshare/src/encryption.rs | 132 ++++++++++++++++++ packages/ciphernode/keyshare/src/keyshare.rs | 32 ++++- packages/ciphernode/keyshare/src/lib.rs | 2 + .../tests/test_aggregation_and_decryption.rs | 4 +- 10 files changed, 218 insertions(+), 14 deletions(-) create mode 100644 packages/ciphernode/keyshare/src/encryption.rs diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index 71aeb552..4d5089b5 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -823,6 +823,18 @@ version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + [[package]] name = "ark-ff" version = "0.3.0" @@ -2113,6 +2125,7 @@ dependencies = [ "actix", "actix-rt", "alloy", + "anyhow", "clap", "config", "enclave_node", @@ -3230,13 +3243,18 @@ name = "keyshare" version = "0.1.0" dependencies = [ "actix", + "aes-gcm", "anyhow", + "argon2", "async-trait", "data", "enclave-core", "fhe 0.1.0", + "proptest", + "rand", "serde", "tracing", + "zeroize", ] [[package]] @@ -4462,6 +4480,17 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" diff --git a/packages/ciphernode/Cargo.toml b/packages/ciphernode/Cargo.toml index 8541e9a6..ca522cc0 100644 --- a/packages/ciphernode/Cargo.toml +++ b/packages/ciphernode/Cargo.toml @@ -19,6 +19,7 @@ members = [ [workspace.dependencies] actix = "0.13.5" actix-rt = "2.10.0" +aes-gcm = "0.10.3" alloy = { version = "0.3.3", features = ["full"] } alloy-primitives = { version = "0.6", default-features = false, features = [ "rlp", @@ -27,6 +28,7 @@ alloy-primitives = { version = "0.6", default-features = false, features = [ ] } alloy-sol-types = { version = "0.6" } anyhow = "1.0.86" +argon2 = "0.5.3" async-std = { version = "1.12", features = ["attributes"] } async-trait = "0.1" bincode = "1.3.3" @@ -40,6 +42,7 @@ fhe-util = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-bet futures = "0.3.30" futures-util = "0.3" num = "0.4.3" +proptest = "1.5.0" rand_chacha = "0.3.1" rand = "0.8.5" serde = { version = "1.0.208", features = ["derive"] } @@ -62,3 +65,4 @@ libp2p = { version = "0.53.2", features = [ "gossipsub", "quic", ] } +zeroize = "1.8.1" diff --git a/packages/ciphernode/data/src/snapshot.rs b/packages/ciphernode/data/src/snapshot.rs index 814caaaf..cfd445a2 100644 --- a/packages/ciphernode/data/src/snapshot.rs +++ b/packages/ciphernode/data/src/snapshot.rs @@ -1,4 +1,4 @@ -use crate::{DataStore, Repository}; +use crate::Repository; use anyhow::Result; use async_trait::async_trait; use serde::{de::DeserializeOwned, Serialize}; diff --git a/packages/ciphernode/enclave/Cargo.toml b/packages/ciphernode/enclave/Cargo.toml index 0da57db0..edfdea76 100644 --- a/packages/ciphernode/enclave/Cargo.toml +++ b/packages/ciphernode/enclave/Cargo.toml @@ -8,12 +8,13 @@ repository = "https://github.com/gnosisguild/enclave/packages/ciphernode" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -enclave_node = { path = "../enclave_node" } -alloy = { workspace = true } -clap = { workspace = true } actix = { workspace = true } actix-rt = { workspace = true } -tokio = { workspace = true } +alloy = { workspace = true } +anyhow = { workspace = true } +clap = { workspace = true } config = "0.14.0" +enclave_node = { path = "../enclave_node" } +tokio = { workspace = true } tracing-subscriber = { workspace = true } tracing = { workspace = true } diff --git a/packages/ciphernode/enclave/src/main.rs b/packages/ciphernode/enclave/src/main.rs index 488a6a72..61869e5c 100644 --- a/packages/ciphernode/enclave/src/main.rs +++ b/packages/ciphernode/enclave/src/main.rs @@ -1,6 +1,8 @@ +use std::env; + use alloy::primitives::Address; use clap::Parser; -use enclave::load_config; +use enclave::{ensure_req_env, load_config}; use enclave_node::{listen_for_shutdown, MainCiphernode}; use tracing::info; @@ -39,6 +41,7 @@ async fn main() -> Result<(), Box> { let address = Address::parse_checksummed(&args.address, None).expect("Invalid address"); info!("LAUNCHING CIPHERNODE: ({})", address); let config = load_config(&args.config)?; + let (bus, handle) = MainCiphernode::attach(config, address, args.data_location.as_deref()).await?; diff --git a/packages/ciphernode/keyshare/Cargo.toml b/packages/ciphernode/keyshare/Cargo.toml index f4084acd..1704beed 100644 --- a/packages/ciphernode/keyshare/Cargo.toml +++ b/packages/ciphernode/keyshare/Cargo.toml @@ -4,11 +4,18 @@ version = "0.1.0" edition = "2021" [dependencies] +actix = { workspace = true } +aes-gcm = { workspace = true } +anyhow = { workspace = true } +argon2 = { workspace = true } +async-trait = { workspace = true } data = { path = "../data" } enclave-core = { path = "../core" } fhe = { path = "../fhe" } -actix = { workspace = true } -anyhow = { workspace = true } +rand = { workspace = true } serde = { workspace = true } -async-trait = { workspace = true } tracing = { workspace = true } +zeroize = { workspace = true } + +[dev-dependencies] +proptest = { workspace = true } diff --git a/packages/ciphernode/keyshare/src/encryption.rs b/packages/ciphernode/keyshare/src/encryption.rs new file mode 100644 index 00000000..a5aefc42 --- /dev/null +++ b/packages/ciphernode/keyshare/src/encryption.rs @@ -0,0 +1,132 @@ +use std::{env, ops::Deref}; + +use aes_gcm::{ + aead::{Aead, KeyInit}, + Aes256Gcm, Nonce, +}; +use anyhow::{anyhow, Result}; +use argon2::{Algorithm, Argon2, Params, Version}; +use rand::{rngs::OsRng, RngCore}; +use zeroize::{Zeroize, Zeroizing}; + +// ARGON2 PARAMS +const ARGON2_M_COST: u32 = 32 * 1024; // 32 MiB +const ARGON2_T_COST: u32 = 2; +const ARGON2_P_COST: u32 = 2; +const ARGON2_OUTPUT_LEN: usize = 32; +const ARGON2_ALGORITHM: Algorithm = Algorithm::Argon2id; +const ARGON2_VERSION: Version = Version::V0x13; + +// AES PARAMS +const AES_SALT_LEN: usize = 32; +const AES_NONCE_LEN: usize = 12; + +fn argon2_derive_key( + password_bytes: Zeroizing>, + salt: &[u8], +) -> Result>> { + let mut derived_key = Zeroizing::new(vec![0u8; ARGON2_OUTPUT_LEN]); + + let params = Params::new( + ARGON2_M_COST, + ARGON2_T_COST, + ARGON2_P_COST, + Some(ARGON2_OUTPUT_LEN), + ) + .map_err(|_| anyhow!("Could not create params"))?; + Argon2::new(ARGON2_ALGORITHM, ARGON2_VERSION, params) + .hash_password_into(&password_bytes, &salt, &mut derived_key) + .map_err(|_| anyhow!("Key derivation error"))?; + Ok(derived_key) +} + +pub fn encrypt_data(data: &mut Vec) -> Result> { + // Convert password to bytes in a zeroizing buffer + let password_bytes = Zeroizing::new(env::var("CIPHERNODE_SECRET")?.as_bytes().to_vec()); + + // Generate a random salt for Argon2 + let mut salt = [0u8; AES_SALT_LEN]; + OsRng.fill_bytes(&mut salt); + + // Generate a random nonce for AES-GCM + let mut nonce_bytes = [0u8; AES_NONCE_LEN]; + OsRng.fill_bytes(&mut nonce_bytes); + let nonce = Nonce::from_slice(&nonce_bytes); + + // Derive key using Argon2 + let derived_key = argon2_derive_key(password_bytes, &salt)?; + + // Create AES-GCM cipher + let cipher = Aes256Gcm::new_from_slice(&derived_key)?; + + // Encrypt the data + let ciphertext = cipher + .encrypt(nonce, data.as_ref()) + .map_err(|_| anyhow!("Could not AES Encrypt given plaintext."))?; + + data.zeroize(); // Zeroize sensitive input data + + // NOTE: password_bytes and derived_key will be automatically zeroized when dropped + + // Pack data + let mut output = Vec::with_capacity(salt.len() + nonce_bytes.len() + ciphertext.len()); + output.extend_from_slice(&salt); + output.extend_from_slice(&nonce_bytes); + output.extend_from_slice(&ciphertext); + + Ok(output) +} + +pub fn decrypt_data(encrypted_data: &[u8]) -> Result> { + const AES_HEADER_LEN: usize = AES_SALT_LEN + AES_NONCE_LEN; + if encrypted_data.len() < AES_HEADER_LEN { + return Err(anyhow!("Invalid encrypted data length")); + } + + let password_bytes = Zeroizing::new(env::var("CIPHERNODE_SECRET")?.as_bytes().to_vec()); + + // Extract salt and nonce + let salt = &encrypted_data[..AES_SALT_LEN]; + let nonce = Nonce::from_slice(&encrypted_data[AES_SALT_LEN..AES_HEADER_LEN]); + let ciphertext = &encrypted_data[AES_HEADER_LEN..]; + + // Derive key using Argon2 + let derived_key = argon2_derive_key(password_bytes, &salt)?; + + // Create cipher and decrypt + let cipher = Aes256Gcm::new_from_slice(&derived_key)?; + let plaintext = cipher + .decrypt(nonce, ciphertext) + .map_err(|_| anyhow!("Could not decrypt data"))?; + + // NOTE: password_bytes and derived_key will be automatically zeroized when dropped + + Ok(plaintext) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_encryption_decryption() { + use std::time::Instant; + println!("TESTING"); + env::set_var("CIPHERNODE_SECRET", "my_secure_password"); + let data = b"Hello, world!"; + + let start = Instant::now(); + let encrypted = encrypt_data(&mut data.to_vec()).unwrap(); + let encryption_time = start.elapsed(); + + let start = Instant::now(); + let decrypted = decrypt_data(&encrypted).unwrap(); + let decryption_time = start.elapsed(); + + println!("Encryption took: {:?}", encryption_time); + println!("Decryption took: {:?}", decryption_time); + println!("Total time: {:?}", encryption_time + decryption_time); + + assert_eq!(data, &decrypted[..]); + } +} diff --git a/packages/ciphernode/keyshare/src/keyshare.rs b/packages/ciphernode/keyshare/src/keyshare.rs index bca9c958..34288634 100644 --- a/packages/ciphernode/keyshare/src/keyshare.rs +++ b/packages/ciphernode/keyshare/src/keyshare.rs @@ -1,3 +1,4 @@ +use crate::{decrypt_data, encrypt_data}; use actix::prelude::*; use anyhow::{anyhow, Result}; use async_trait::async_trait; @@ -8,8 +9,10 @@ use enclave_core::{ }; use fhe::{DecryptCiphertext, Fhe}; use serde::{Deserialize, Serialize}; -use std::sync::Arc; +use std::env; +use std::{process, sync::Arc}; use tracing::warn; +use zeroize::Zeroizing; pub struct Keyshare { fhe: Arc, @@ -45,6 +48,21 @@ impl Keyshare { address: params.address, } } + + fn set_secret(&mut self, mut data: Vec) -> Result<()> { + let encrypted = encrypt_data(&mut data)?; + self.secret = Some(encrypted); + Ok(()) + } + + fn get_secret(&self) -> Result> { + let encrypted = self + .secret + .clone() + .ok_or(anyhow!("No secret share available on Keyshare"))?; + let decrypted = decrypt_data(&encrypted)?; + Ok(decrypted) + } } impl Snapshot for Keyshare { @@ -107,7 +125,13 @@ impl Handler for Keyshare { }; // Save secret on state - self.secret = Some(secret); + let Ok(()) = self.set_secret(secret) else { + self.bus.do_send(EnclaveEvent::from_error( + EnclaveErrorType::KeyGeneration, + anyhow!("Error encrypting Keyshare for {e3_id}"), + )); + return; + }; // Broadcast the KeyshareCreated message self.bus.do_send(EnclaveEvent::from(KeyshareCreated { @@ -134,10 +158,10 @@ impl Handler for Keyshare { ciphertext_output, } = event; - let Some(secret) = &self.secret else { + let Ok(secret) = &self.get_secret() else { self.bus.err( EnclaveErrorType::Decryption, - anyhow!("secret not found on Keyshare for e3_id {e3_id}"), + anyhow!("Secret not available for Keyshare for e3_id {e3_id}"), ); return; }; diff --git a/packages/ciphernode/keyshare/src/lib.rs b/packages/ciphernode/keyshare/src/lib.rs index 46e4b5c9..623035ce 100644 --- a/packages/ciphernode/keyshare/src/lib.rs +++ b/packages/ciphernode/keyshare/src/lib.rs @@ -1,2 +1,4 @@ +mod encryption; mod keyshare; +pub use encryption::*; pub use keyshare::*; diff --git a/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs b/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs index 015058d2..fa4fc8ca 100644 --- a/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs +++ b/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs @@ -24,7 +24,7 @@ use fhe_traits::{FheEncoder, FheEncrypter, Serialize}; use rand::Rng; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; -use std::{sync::Arc, time::Duration}; +use std::{env, sync::Arc, time::Duration}; use tokio::sync::Mutex; use tokio::{sync::mpsc::channel, time::sleep}; @@ -271,6 +271,7 @@ fn get_common_setup() -> Result<( #[actix::test] async fn test_public_key_aggregation_and_decryption() -> Result<()> { // Setup + env::set_var("CIPHERNODE_SECRET", "Don't tell anyone my secret"); let (bus, rng, seed, params, crpoly, e3_id) = get_common_setup()?; // Setup actual ciphernodes and dispatch add events @@ -376,6 +377,7 @@ async fn test_public_key_aggregation_and_decryption() -> Result<()> { #[actix::test] async fn test_stopped_keyshares_retain_state() -> Result<()> { + env::set_var("CIPHERNODE_SECRET", "Don't tell anyone my secret"); let (bus, rng, seed, params, crpoly, e3_id) = get_common_setup()?; let eth_addrs = create_random_eth_addrs(2); From c45c248119f51fc00007e2a68b193eec9731f676 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Wed, 23 Oct 2024 12:34:33 +1100 Subject: [PATCH 02/45] Remove old function --- packages/ciphernode/Cargo.lock | 1 - packages/ciphernode/enclave/src/main.rs | 2 +- packages/ciphernode/keyshare/Cargo.toml | 3 --- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index 4d5089b5..0db05c6b 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -3250,7 +3250,6 @@ dependencies = [ "data", "enclave-core", "fhe 0.1.0", - "proptest", "rand", "serde", "tracing", diff --git a/packages/ciphernode/enclave/src/main.rs b/packages/ciphernode/enclave/src/main.rs index 61869e5c..d81fd3d0 100644 --- a/packages/ciphernode/enclave/src/main.rs +++ b/packages/ciphernode/enclave/src/main.rs @@ -2,7 +2,7 @@ use std::env; use alloy::primitives::Address; use clap::Parser; -use enclave::{ensure_req_env, load_config}; +use enclave::load_config; use enclave_node::{listen_for_shutdown, MainCiphernode}; use tracing::info; diff --git a/packages/ciphernode/keyshare/Cargo.toml b/packages/ciphernode/keyshare/Cargo.toml index 1704beed..a9527043 100644 --- a/packages/ciphernode/keyshare/Cargo.toml +++ b/packages/ciphernode/keyshare/Cargo.toml @@ -16,6 +16,3 @@ rand = { workspace = true } serde = { workspace = true } tracing = { workspace = true } zeroize = { workspace = true } - -[dev-dependencies] -proptest = { workspace = true } From 6c7d28a3b0a16af6cb9a0486052e0023bd979743 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Wed, 23 Oct 2024 12:42:35 +1100 Subject: [PATCH 03/45] Add secret to integration test --- tests/basic_integration/test.sh | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/basic_integration/test.sh b/tests/basic_integration/test.sh index 57a5fdec..a88bbfbb 100755 --- a/tests/basic_integration/test.sh +++ b/tests/basic_integration/test.sh @@ -18,6 +18,7 @@ export RUST_LOG=info RPC_URL="ws://localhost:8545" PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" +CIPHERNODE_SECRET="We are the music makers and we are the dreamers of the dreams." # These contracts are based on the deterministic order of hardhat deploy # We _may_ wish to get these off the hardhat environment somehow? @@ -31,6 +32,7 @@ CIPHERNODE_ADDRESS_2="0xbDA5747bFD65F08deb54cb465eB87D40e51B197E" CIPHERNODE_ADDRESS_3="0xdD2FD4581271e230360230F9337D5c0430Bf44C0" CIPHERNODE_ADDRESS_4="0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199" + # Function to clean up background processes cleanup() { echo "Cleaning up processes..." @@ -78,8 +80,9 @@ waiton-files() { launch_ciphernode() { local address="$1" + local secret="$2" heading "Launch ciphernode $address" - yarn ciphernode:launch \ + CIPHERNODE_SECRET=$secret yarn ciphernode:launch \ --address "$address" \ --config "$SCRIPT_DIR/lib/ciphernode_config.yaml" \ --data-location "$SCRIPT_DIR/output/$address.db" & @@ -115,10 +118,10 @@ done # Launch 4 ciphernodes -launch_ciphernode "$CIPHERNODE_ADDRESS_1" -launch_ciphernode "$CIPHERNODE_ADDRESS_2" -launch_ciphernode "$CIPHERNODE_ADDRESS_3" -launch_ciphernode "$CIPHERNODE_ADDRESS_4" +launch_ciphernode "$CIPHERNODE_ADDRESS_1" "$CIPHERNODE_SECRET" +launch_ciphernode "$CIPHERNODE_ADDRESS_2" "$CIPHERNODE_SECRET" +launch_ciphernode "$CIPHERNODE_ADDRESS_3" "$CIPHERNODE_SECRET" +launch_ciphernode "$CIPHERNODE_ADDRESS_4" "$CIPHERNODE_SECRET" launch_aggregator "$PRIVATE_KEY" sleep 1 From 76461f5269bc0dc14a6125cf44213c52f0427f0b Mon Sep 17 00:00:00 2001 From: ktdlr Date: Wed, 23 Oct 2024 12:56:51 +1100 Subject: [PATCH 04/45] Pin vars and remove clone --- packages/ciphernode/keyshare/Cargo.toml | 8 ++++---- packages/ciphernode/keyshare/src/keyshare.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/ciphernode/keyshare/Cargo.toml b/packages/ciphernode/keyshare/Cargo.toml index a9527043..f6bec0f7 100644 --- a/packages/ciphernode/keyshare/Cargo.toml +++ b/packages/ciphernode/keyshare/Cargo.toml @@ -5,14 +5,14 @@ edition = "2021" [dependencies] actix = { workspace = true } -aes-gcm = { workspace = true } +aes-gcm = { workspace = true, version = "=0.10.3" } anyhow = { workspace = true } -argon2 = { workspace = true } +argon2 = { workspace = true, version = "=0.5.2" } async-trait = { workspace = true } data = { path = "../data" } enclave-core = { path = "../core" } fhe = { path = "../fhe" } -rand = { workspace = true } +rand = { workspace = true, version = "=0.8.5" } serde = { workspace = true } tracing = { workspace = true } -zeroize = { workspace = true } +zeroize = { workspace = true, version = "=1.6.0" } diff --git a/packages/ciphernode/keyshare/src/keyshare.rs b/packages/ciphernode/keyshare/src/keyshare.rs index 34288634..087b475a 100644 --- a/packages/ciphernode/keyshare/src/keyshare.rs +++ b/packages/ciphernode/keyshare/src/keyshare.rs @@ -158,7 +158,7 @@ impl Handler for Keyshare { ciphertext_output, } = event; - let Ok(secret) = &self.get_secret() else { + let Ok(secret) = self.get_secret() else { self.bus.err( EnclaveErrorType::Decryption, anyhow!("Secret not available for Keyshare for e3_id {e3_id}"), @@ -168,7 +168,7 @@ impl Handler for Keyshare { let Ok(decryption_share) = self.fhe.decrypt_ciphertext(DecryptCiphertext { ciphertext: ciphertext_output.clone(), - unsafe_secret: secret.to_vec(), + unsafe_secret: secret, }) else { self.bus.err( EnclaveErrorType::Decryption, From 42b54d24a13ef15cd2dc1ec8cb9b62687c48114b Mon Sep 17 00:00:00 2001 From: ktdlr Date: Wed, 23 Oct 2024 13:35:10 +1100 Subject: [PATCH 05/45] Add comprehensive tests --- .../ciphernode/keyshare/src/encryption.rs | 162 ++++++++++++++++-- packages/ciphernode/keyshare/src/keyshare.rs | 10 +- 2 files changed, 154 insertions(+), 18 deletions(-) diff --git a/packages/ciphernode/keyshare/src/encryption.rs b/packages/ciphernode/keyshare/src/encryption.rs index a5aefc42..cb3fc489 100644 --- a/packages/ciphernode/keyshare/src/encryption.rs +++ b/packages/ciphernode/keyshare/src/encryption.rs @@ -7,7 +7,7 @@ use aes_gcm::{ use anyhow::{anyhow, Result}; use argon2::{Algorithm, Argon2, Params, Version}; use rand::{rngs::OsRng, RngCore}; -use zeroize::{Zeroize, Zeroizing}; +use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; // ARGON2 PARAMS const ARGON2_M_COST: u32 = 32 * 1024; // 32 MiB @@ -22,7 +22,7 @@ const AES_SALT_LEN: usize = 32; const AES_NONCE_LEN: usize = 12; fn argon2_derive_key( - password_bytes: Zeroizing>, + password_bytes: &Zeroizing>, salt: &[u8], ) -> Result>> { let mut derived_key = Zeroizing::new(vec![0u8; ARGON2_OUTPUT_LEN]); @@ -40,10 +40,7 @@ fn argon2_derive_key( Ok(derived_key) } -pub fn encrypt_data(data: &mut Vec) -> Result> { - // Convert password to bytes in a zeroizing buffer - let password_bytes = Zeroizing::new(env::var("CIPHERNODE_SECRET")?.as_bytes().to_vec()); - +fn encrypt_data(password_bytes: &Zeroizing>, data: &mut Vec) -> Result> { // Generate a random salt for Argon2 let mut salt = [0u8; AES_SALT_LEN]; OsRng.fill_bytes(&mut salt); @@ -77,14 +74,12 @@ pub fn encrypt_data(data: &mut Vec) -> Result> { Ok(output) } -pub fn decrypt_data(encrypted_data: &[u8]) -> Result> { +fn decrypt_data(password_bytes: &Zeroizing>, encrypted_data: &[u8]) -> Result> { const AES_HEADER_LEN: usize = AES_SALT_LEN + AES_NONCE_LEN; if encrypted_data.len() < AES_HEADER_LEN { return Err(anyhow!("Invalid encrypted data length")); } - let password_bytes = Zeroizing::new(env::var("CIPHERNODE_SECRET")?.as_bytes().to_vec()); - // Extract salt and nonce let salt = &encrypted_data[..AES_SALT_LEN]; let nonce = Nonce::from_slice(&encrypted_data[AES_SALT_LEN..AES_HEADER_LEN]); @@ -104,23 +99,53 @@ pub fn decrypt_data(encrypted_data: &[u8]) -> Result> { Ok(plaintext) } +#[derive(ZeroizeOnDrop)] +pub struct Encryptor { + key: Zeroizing>, +} + +impl Encryptor { + pub fn new(mut secret: String) -> Self { + let key = Zeroizing::new(secret.as_bytes().to_vec()); + secret.zeroize(); + Self { key } + } + + pub fn from_env(key:&str) -> Result { + Ok(Self::new(env::var(key)?)) + } + + pub fn encrypt_data(&self, data: &mut Vec) -> Result> { + encrypt_data(&self.key, data) + } + + pub fn decrypt_data(&self, encrypted_data: &[u8]) -> Result> { + decrypt_data(&self.key, encrypted_data) + } +} + +impl Zeroize for Encryptor { + fn zeroize(&mut self) { + self.key.zeroize() + } +} + #[cfg(test)] mod tests { use super::*; + use std::time::Instant; #[test] - fn test_encryption_decryption() { - use std::time::Instant; - println!("TESTING"); - env::set_var("CIPHERNODE_SECRET", "my_secure_password"); + fn test_basic_encryption_decryption() { let data = b"Hello, world!"; let start = Instant::now(); - let encrypted = encrypt_data(&mut data.to_vec()).unwrap(); + let encryptor = Encryptor::new("my_secure_password".to_owned()); + let encrypted = encryptor.encrypt_data(&mut data.to_vec()).unwrap(); let encryption_time = start.elapsed(); let start = Instant::now(); - let decrypted = decrypt_data(&encrypted).unwrap(); + let decrypted = encryptor.decrypt_data(&encrypted).unwrap(); let decryption_time = start.elapsed(); println!("Encryption took: {:?}", encryption_time); @@ -129,4 +154,111 @@ mod tests { assert_eq!(data, &decrypted[..]); } + + #[test] + fn test_empty_data() { + let encryptor = Encryptor::new("test_password".to_owned()); + + let data = vec![]; + + let encrypted = encryptor.encrypt_data(&mut data.clone()).unwrap(); + let decrypted = encryptor.decrypt_data(&encrypted).unwrap(); + + assert_eq!(data, decrypted); + } + + #[test] + fn test_large_data() { + let encryptor = Encryptor::new("test_password".to_owned()); + let data = vec![0u8; 1024 * 1024]; // 1MB of data + + let start = Instant::now(); + let encrypted = encryptor.encrypt_data(&mut data.clone()).unwrap(); + let encryption_time = start.elapsed(); + + let start = Instant::now(); + let decrypted = encryptor.decrypt_data(&encrypted).unwrap(); + let decryption_time = start.elapsed(); + + println!("Large data encryption took: {:?}", encryption_time); + println!("Large data decryption took: {:?}", decryption_time); + + assert_eq!(data, decrypted); + } + + #[test] + fn test_different_passwords() { + // Encrypt with one password + let encryptor = Encryptor::new("password1".to_owned()); + + let data = b"Secret message"; + let encrypted = encryptor.encrypt_data(&mut data.to_vec()).unwrap(); + + // Try to decrypt with a different password + let encryptor = Encryptor::new("password2".to_owned()); + let result = encryptor.decrypt_data(&encrypted); + + assert!(result.is_err()); + } + + #[test] + fn test_binary_data() { + let encryptor = Encryptor::new("test_password".to_owned()); + + let data = vec![0xFF, 0x00, 0xAA, 0x55, 0x12, 0xED]; + + let encrypted = encryptor.encrypt_data(&mut data.clone()).unwrap(); + let decrypted = encryptor.decrypt_data(&encrypted).unwrap(); + + assert_eq!(data, decrypted); + } + + #[test] + fn test_unicode_data() { + let encryptor = Encryptor::new("test_password".to_owned()); + let data = "Hello 🌍 ΠΏΡ€ΠΈΠ²Π΅Ρ‚ δΈ–η•Œ".as_bytes().to_vec(); + + let encrypted = encryptor.encrypt_data(&mut data.clone()).unwrap(); + let decrypted = encryptor.decrypt_data(&encrypted).unwrap(); + + assert_eq!(data, decrypted); + } + + #[test] + #[should_panic(expected = "Invalid encrypted data length")] + fn test_invalid_encrypted_data() { + let encryptor = Encryptor::new("test_password".to_owned()); + let invalid_data = vec![0u8; 10]; // Too short to be valid encrypted data + encryptor.decrypt_data(&invalid_data).unwrap(); + } + + #[test] + fn test_multiple_encrypt_decrypt_cycles() { + let encryptor = Encryptor::new("test_password".to_owned()); + let original_data = b"Multiple encryption cycles test"; + + let mut data = original_data.to_vec(); + for _ in 0..5 { + data = encryptor.encrypt_data(&mut data).unwrap(); + data = encryptor.decrypt_data(&data).unwrap(); + } + + assert_eq!(original_data.to_vec(), data); + } + + #[test] + fn test_corrupted_data() { + let encryptor = Encryptor::new("test_password".to_owned()); + let data = b"Test corrupted data"; + + let mut encrypted = encryptor.encrypt_data(&mut data.to_vec()).unwrap(); + + // Corrupt the ciphertext portion (after salt and nonce) + if let Some(byte) = encrypted.get_mut(AES_SALT_LEN + AES_NONCE_LEN) { + *byte ^= 0xFF; + } + + let result = encryptor.decrypt_data(&encrypted); + assert!(result.is_err()); + } } diff --git a/packages/ciphernode/keyshare/src/keyshare.rs b/packages/ciphernode/keyshare/src/keyshare.rs index 087b475a..6a486c16 100644 --- a/packages/ciphernode/keyshare/src/keyshare.rs +++ b/packages/ciphernode/keyshare/src/keyshare.rs @@ -1,4 +1,4 @@ -use crate::{decrypt_data, encrypt_data}; +use crate::Encryptor; use actix::prelude::*; use anyhow::{anyhow, Result}; use async_trait::async_trait; @@ -50,8 +50,10 @@ impl Keyshare { } fn set_secret(&mut self, mut data: Vec) -> Result<()> { - let encrypted = encrypt_data(&mut data)?; + let encrypted = Encryptor::from_env("CIPHERNODE_SECRET")?.encrypt_data(&mut data)?; + self.secret = Some(encrypted); + Ok(()) } @@ -60,7 +62,9 @@ impl Keyshare { .secret .clone() .ok_or(anyhow!("No secret share available on Keyshare"))?; - let decrypted = decrypt_data(&encrypted)?; + + let decrypted = Encryptor::from_env("CIPHERNODE_SECRET")?.decrypt_data(&encrypted)?; + Ok(decrypted) } } From b06d96b610dc76e4ca4ba11bd6498fcc8f7a3026 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Wed, 23 Oct 2024 13:41:48 +1100 Subject: [PATCH 06/45] Update to use a vec of 1s --- packages/ciphernode/keyshare/src/encryption.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ciphernode/keyshare/src/encryption.rs b/packages/ciphernode/keyshare/src/encryption.rs index cb3fc489..6871f4e2 100644 --- a/packages/ciphernode/keyshare/src/encryption.rs +++ b/packages/ciphernode/keyshare/src/encryption.rs @@ -170,7 +170,7 @@ mod tests { #[test] fn test_large_data() { let encryptor = Encryptor::new("test_password".to_owned()); - let data = vec![0u8; 1024 * 1024]; // 1MB of data + let data = vec![1u8; 1024 * 1024]; // 1MB of data let start = Instant::now(); let encrypted = encryptor.encrypt_data(&mut data.clone()).unwrap(); From 95a1fb73aa981a6ca352d96484e1b962434fea9b Mon Sep 17 00:00:00 2001 From: ktdlr Date: Wed, 23 Oct 2024 13:54:03 +1100 Subject: [PATCH 07/45] Formatting --- packages/ciphernode/keyshare/src/encryption.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ciphernode/keyshare/src/encryption.rs b/packages/ciphernode/keyshare/src/encryption.rs index 6871f4e2..da344fdc 100644 --- a/packages/ciphernode/keyshare/src/encryption.rs +++ b/packages/ciphernode/keyshare/src/encryption.rs @@ -111,7 +111,7 @@ impl Encryptor { Self { key } } - pub fn from_env(key:&str) -> Result { + pub fn from_env(key: &str) -> Result { Ok(Self::new(env::var(key)?)) } From cc95e36f86c557373fb26f6ca8e58d9e88653cef Mon Sep 17 00:00:00 2001 From: ktdlr Date: Thu, 24 Oct 2024 15:58:16 +1100 Subject: [PATCH 08/45] Structure out Cipher and PasswordManager trait --- packages/ciphernode/Cargo.lock | 26 ++- packages/ciphernode/Cargo.toml | 7 +- packages/ciphernode/cipher/Cargo.toml | 11 ++ .../encryption.rs => cipher/src/cipher.rs} | 114 +++++++------ packages/ciphernode/cipher/src/lib.rs | 3 + .../ciphernode/cipher/src/password_manager.rs | 154 ++++++++++++++++++ packages/ciphernode/keyshare/Cargo.toml | 5 +- packages/ciphernode/keyshare/src/keyshare.rs | 8 +- packages/ciphernode/keyshare/src/lib.rs | 2 - 9 files changed, 254 insertions(+), 76 deletions(-) create mode 100644 packages/ciphernode/cipher/Cargo.toml rename packages/ciphernode/{keyshare/src/encryption.rs => cipher/src/cipher.rs} (66%) create mode 100644 packages/ciphernode/cipher/src/lib.rs create mode 100644 packages/ciphernode/cipher/src/password_manager.rs diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index 0db05c6b..60486c5c 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -91,7 +91,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures", ] @@ -103,7 +103,7 @@ checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", - "cipher", + "cipher 0.4.4", "ctr", "ghash", "subtle", @@ -1567,7 +1567,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures", ] @@ -1579,11 +1579,22 @@ checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ "aead", "chacha20", - "cipher", + "cipher 0.4.4", "poly1305", "zeroize", ] +[[package]] +name = "cipher" +version = "0.1.0" +dependencies = [ + "aes-gcm", + "anyhow", + "argon2", + "rand", + "zeroize", +] + [[package]] name = "cipher" version = "0.4.4" @@ -1826,7 +1837,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -3243,17 +3254,14 @@ name = "keyshare" version = "0.1.0" dependencies = [ "actix", - "aes-gcm", "anyhow", - "argon2", "async-trait", + "cipher 0.1.0", "data", "enclave-core", "fhe 0.1.0", - "rand", "serde", "tracing", - "zeroize", ] [[package]] diff --git a/packages/ciphernode/Cargo.toml b/packages/ciphernode/Cargo.toml index ca522cc0..375a7ded 100644 --- a/packages/ciphernode/Cargo.toml +++ b/packages/ciphernode/Cargo.toml @@ -14,6 +14,7 @@ members = [ "test_helpers", "logger", "tests", + "cipher", ] [workspace.dependencies] @@ -27,22 +28,20 @@ alloy-primitives = { version = "0.6", default-features = false, features = [ "std", ] } alloy-sol-types = { version = "0.6" } +argon2 = "0.5.2" anyhow = "1.0.86" -argon2 = "0.5.3" async-std = { version = "1.12", features = ["attributes"] } async-trait = "0.1" bincode = "1.3.3" bs58 = "0.5.1" base64 = "0.22.1" clap = { version = "4.5.17", features = ["derive"] } -enclave_node = { path = "../enclave_node" } fhe_rs = { package = "fhe", git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } fhe-traits = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } fhe-util = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } futures = "0.3.30" futures-util = "0.3" num = "0.4.3" -proptest = "1.5.0" rand_chacha = "0.3.1" rand = "0.8.5" serde = { version = "1.0.208", features = ["derive"] } @@ -65,4 +64,4 @@ libp2p = { version = "0.53.2", features = [ "gossipsub", "quic", ] } -zeroize = "1.8.1" +zeroize = "1.6.0" diff --git a/packages/ciphernode/cipher/Cargo.toml b/packages/ciphernode/cipher/Cargo.toml new file mode 100644 index 00000000..9a2e91ac --- /dev/null +++ b/packages/ciphernode/cipher/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "cipher" +version = "0.1.0" +edition = "2021" + +[dependencies] +aes-gcm = { workspace = true, version = "=0.10.3" } +argon2 = { workspace = true, version = "=0.5.2" } +rand = { workspace = true, version = "=0.8.5" } +zeroize = { workspace = true, version = "=1.6.0" } +anyhow = { workspace = true } diff --git a/packages/ciphernode/keyshare/src/encryption.rs b/packages/ciphernode/cipher/src/cipher.rs similarity index 66% rename from packages/ciphernode/keyshare/src/encryption.rs rename to packages/ciphernode/cipher/src/cipher.rs index da344fdc..826c38f5 100644 --- a/packages/ciphernode/keyshare/src/encryption.rs +++ b/packages/ciphernode/cipher/src/cipher.rs @@ -1,5 +1,3 @@ -use std::{env, ops::Deref}; - use aes_gcm::{ aead::{Aead, KeyInit}, Aes256Gcm, Nonce, @@ -7,7 +5,9 @@ use aes_gcm::{ use anyhow::{anyhow, Result}; use argon2::{Algorithm, Argon2, Params, Version}; use rand::{rngs::OsRng, RngCore}; -use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; +use zeroize::{Zeroize, Zeroizing}; + +use crate::password_manager::{EnvPasswordManager, InMemPasswordManager, PasswordManager}; // ARGON2 PARAMS const ARGON2_M_COST: u32 = 32 * 1024; // 32 MiB @@ -63,8 +63,6 @@ fn encrypt_data(password_bytes: &Zeroizing>, data: &mut Vec) -> Resu data.zeroize(); // Zeroize sensitive input data - // NOTE: password_bytes and derived_key will be automatically zeroized when dropped - // Pack data let mut output = Vec::with_capacity(salt.len() + nonce_bytes.len() + ciphertext.len()); output.extend_from_slice(&salt); @@ -94,25 +92,29 @@ fn decrypt_data(password_bytes: &Zeroizing>, encrypted_data: &[u8]) -> R .decrypt(nonce, ciphertext) .map_err(|_| anyhow!("Could not decrypt data"))?; - // NOTE: password_bytes and derived_key will be automatically zeroized when dropped - Ok(plaintext) } -#[derive(ZeroizeOnDrop)] -pub struct Encryptor { +pub struct Cipher { key: Zeroizing>, } -impl Encryptor { - pub fn new(mut secret: String) -> Self { - let key = Zeroizing::new(secret.as_bytes().to_vec()); - secret.zeroize(); - Self { key } +impl Cipher { + pub fn new

(pm: P) -> Result + where + P: PasswordManager, + { + // Get the key from the password manager when created + let key = pm.get_key()?; + Ok(Self { key }) } - pub fn from_env(key: &str) -> Result { - Ok(Self::new(env::var(key)?)) + pub fn from_password(value: &str) -> Result { + Ok(Self::new(InMemPasswordManager::from_str(value))?) + } + + pub fn from_env(value: &str) -> Result { + Ok(Self::new(EnvPasswordManager::new(value)?)?) } pub fn encrypt_data(&self, data: &mut Vec) -> Result> { @@ -124,28 +126,31 @@ impl Encryptor { } } -impl Zeroize for Encryptor { +impl Zeroize for Cipher { fn zeroize(&mut self) { - self.key.zeroize() + self.key.zeroize(); } } #[cfg(test)] mod tests { + use super::*; + use anyhow::*; use std::time::Instant; #[test] - fn test_basic_encryption_decryption() { + fn test_basic_encryption_decryption() -> Result<()> { let data = b"Hello, world!"; let start = Instant::now(); - let encryptor = Encryptor::new("my_secure_password".to_owned()); - let encrypted = encryptor.encrypt_data(&mut data.to_vec()).unwrap(); + + let cipher = Cipher::from_password("test_password")?; + let encrypted = cipher.encrypt_data(&mut data.to_vec()).unwrap(); let encryption_time = start.elapsed(); let start = Instant::now(); - let decrypted = encryptor.decrypt_data(&encrypted).unwrap(); + let decrypted = cipher.decrypt_data(&encrypted).unwrap(); let decryption_time = start.elapsed(); println!("Encryption took: {:?}", encryption_time); @@ -153,94 +158,99 @@ mod tests { println!("Total time: {:?}", encryption_time + decryption_time); assert_eq!(data, &decrypted[..]); + Ok(()) } #[test] - fn test_empty_data() { - let encryptor = Encryptor::new("test_password".to_owned()); - + fn test_empty_data() -> Result<()> { + let cipher = Cipher::from_password("test_password")?; let data = vec![]; - let encrypted = encryptor.encrypt_data(&mut data.clone()).unwrap(); - let decrypted = encryptor.decrypt_data(&encrypted).unwrap(); + let encrypted = cipher.encrypt_data(&mut data.clone()).unwrap(); + let decrypted = cipher.decrypt_data(&encrypted).unwrap(); assert_eq!(data, decrypted); + Ok(()) } #[test] - fn test_large_data() { - let encryptor = Encryptor::new("test_password".to_owned()); + fn test_large_data() -> Result<()> { + let cipher = Cipher::from_password("test_password")?; let data = vec![1u8; 1024 * 1024]; // 1MB of data let start = Instant::now(); - let encrypted = encryptor.encrypt_data(&mut data.clone()).unwrap(); + let encrypted = cipher.encrypt_data(&mut data.clone()).unwrap(); let encryption_time = start.elapsed(); let start = Instant::now(); - let decrypted = encryptor.decrypt_data(&encrypted).unwrap(); + let decrypted = cipher.decrypt_data(&encrypted).unwrap(); let decryption_time = start.elapsed(); println!("Large data encryption took: {:?}", encryption_time); println!("Large data decryption took: {:?}", decryption_time); assert_eq!(data, decrypted); + Ok(()) } #[test] - fn test_different_passwords() { + fn test_different_passwords() -> Result<()> { // Encrypt with one password - let encryptor = Encryptor::new("password1".to_owned()); + let cipher = Cipher::from_password("password1")?; let data = b"Secret message"; - let encrypted = encryptor.encrypt_data(&mut data.to_vec()).unwrap(); + let encrypted = cipher.encrypt_data(&mut data.to_vec()).unwrap(); // Try to decrypt with a different password - let encryptor = Encryptor::new("password2".to_owned()); - let result = encryptor.decrypt_data(&encrypted); + let cipher = Cipher::from_password("password2")?; + let result = cipher.decrypt_data(&encrypted); assert!(result.is_err()); + Ok(()) } #[test] - fn test_binary_data() { - let encryptor = Encryptor::new("test_password".to_owned()); + fn test_binary_data() -> Result<()> { + let cipher = Cipher::from_password("test_password")?; let data = vec![0xFF, 0x00, 0xAA, 0x55, 0x12, 0xED]; - let encrypted = encryptor.encrypt_data(&mut data.clone()).unwrap(); - let decrypted = encryptor.decrypt_data(&encrypted).unwrap(); + let encrypted = cipher.encrypt_data(&mut data.clone()).unwrap(); + let decrypted = cipher.decrypt_data(&encrypted).unwrap(); assert_eq!(data, decrypted); + Ok(()) } #[test] - fn test_unicode_data() { - let encryptor = Encryptor::new("test_password".to_owned()); + fn test_unicode_data() -> Result<()> { + let cipher = Cipher::from_password("test_password")?; let data = "Hello 🌍 ΠΏΡ€ΠΈΠ²Π΅Ρ‚ δΈ–η•Œ".as_bytes().to_vec(); - let encrypted = encryptor.encrypt_data(&mut data.clone()).unwrap(); - let decrypted = encryptor.decrypt_data(&encrypted).unwrap(); + let encrypted = cipher.encrypt_data(&mut data.clone()).unwrap(); + let decrypted = cipher.decrypt_data(&encrypted).unwrap(); assert_eq!(data, decrypted); + Ok(()) } #[test] #[should_panic(expected = "Invalid encrypted data length")] fn test_invalid_encrypted_data() { - let encryptor = Encryptor::new("test_password".to_owned()); + let cipher = Cipher::from_password("test_password").unwrap(); let invalid_data = vec![0u8; 10]; // Too short to be valid encrypted data - encryptor.decrypt_data(&invalid_data).unwrap(); + cipher.decrypt_data(&invalid_data).unwrap(); } #[test] fn test_multiple_encrypt_decrypt_cycles() { - let encryptor = Encryptor::new("test_password".to_owned()); + let cipher = Cipher::from_password("test_password").unwrap(); let original_data = b"Multiple encryption cycles test"; let mut data = original_data.to_vec(); for _ in 0..5 { - data = encryptor.encrypt_data(&mut data).unwrap(); - data = encryptor.decrypt_data(&data).unwrap(); + data = cipher.encrypt_data(&mut data).unwrap(); + data = cipher.decrypt_data(&data).unwrap(); } assert_eq!(original_data.to_vec(), data); @@ -248,17 +258,17 @@ mod tests { #[test] fn test_corrupted_data() { - let encryptor = Encryptor::new("test_password".to_owned()); + let cipher = Cipher::from_password("test_password").unwrap(); let data = b"Test corrupted data"; - let mut encrypted = encryptor.encrypt_data(&mut data.to_vec()).unwrap(); + let mut encrypted = cipher.encrypt_data(&mut data.to_vec()).unwrap(); // Corrupt the ciphertext portion (after salt and nonce) if let Some(byte) = encrypted.get_mut(AES_SALT_LEN + AES_NONCE_LEN) { *byte ^= 0xFF; } - let result = encryptor.decrypt_data(&encrypted); + let result = cipher.decrypt_data(&encrypted); assert!(result.is_err()); } } diff --git a/packages/ciphernode/cipher/src/lib.rs b/packages/ciphernode/cipher/src/lib.rs new file mode 100644 index 00000000..00f94842 --- /dev/null +++ b/packages/ciphernode/cipher/src/lib.rs @@ -0,0 +1,3 @@ +mod cipher; +mod password_manager; +pub use cipher::Cipher; diff --git a/packages/ciphernode/cipher/src/password_manager.rs b/packages/ciphernode/cipher/src/password_manager.rs new file mode 100644 index 00000000..05dfa408 --- /dev/null +++ b/packages/ciphernode/cipher/src/password_manager.rs @@ -0,0 +1,154 @@ +use anyhow::*; +use std::{ + env, + fs::{self, OpenOptions, Permissions}, + io::Write, + os::unix::fs::{OpenOptionsExt, PermissionsExt}, + path::{Path, PathBuf}, +}; +use zeroize::Zeroizing; + +pub trait PasswordManager { + fn get_key(&self) -> Result>>; + fn delete_key(&mut self) -> Result<()>; + fn set_key(&mut self, contents: Zeroizing>) -> Result<()>; +} + +pub struct InMemPasswordManager(pub Option>>); + +impl InMemPasswordManager { + pub fn new(value: Zeroizing>) -> Self { + Self(Some(value)) + } + + pub fn from_str(value: &str) -> Self { + Self::new(Zeroizing::new(value.as_bytes().to_vec())) + } +} + +pub struct EnvPasswordManager(pub Option>>); + +impl EnvPasswordManager { + pub fn new(value: &str) -> Result { + let env_string = env::var(value)?.as_bytes().into(); + env::remove_var(value); + Ok(Self(Some(Zeroizing::new(env_string)))) + } +} + +impl PasswordManager for EnvPasswordManager { + fn get_key(&self) -> Result>> { + if let Some(key) = self.0.clone() { + return Ok(key); + } + Err(anyhow!("No key found")) + } + fn set_key(&mut self, contents: Zeroizing>) -> Result<()> { + self.0 = Some(contents); + Ok(()) + } + + fn delete_key(&mut self) -> Result<()> { + self.0 = None; + Ok(()) + } +} + +impl PasswordManager for InMemPasswordManager { + fn get_key(&self) -> Result>> { + if let Some(key) = self.0.clone() { + return Ok(key); + } + Err(anyhow!("No key found")) + } + fn set_key(&mut self, contents: Zeroizing>) -> Result<()> { + self.0 = Some(contents); + Ok(()) + } + + fn delete_key(&mut self) -> Result<()> { + self.0 = None; + Ok(()) + } +} + +pub struct FilePasswordManager { + path: PathBuf, +} + +impl FilePasswordManager { + pub fn new(path: impl AsRef) -> Self { + Self { + path: path.as_ref().to_owned(), + } + } +} + +impl PasswordManager for FilePasswordManager { + fn get_key(&self) -> Result>> { + let path = &self.path; + + ensure_file_permissions(path, 0o400)?; + + let bytes = fs::read(&self.path).context("Failed to access keyfile")?; + + Ok(Zeroizing::new(bytes)) + } + + fn delete_key(&mut self) -> Result<()> { + let path = &self.path; + + ensure_file_permissions(path, 0o600)?; + + fs::remove_file(path).context("Failed to remove keyfile")?; + Ok(()) + } + + fn set_key(&mut self, contents: Zeroizing>) -> Result<()> { + let path = &self.path; + + // Check if file exists + if path.exists() { + bail!("Keyfile already exists. Refusing to overwrite."); + } + + // Create new file with restrictive permissions from the start + let mut file = OpenOptions::new() + .write(true) + .create_new(true) + .mode(0o600) + .open(path) + .context("Failed to create keyfile")?; + + // Write the contents + file.write_all(&contents) + .context("Failed to write data to keyfile")?; + + file.flush().context("Failed to flush data to keyfile")?; + + // Close the file handle explicitly + drop(file); + + // Set to read-only (400) + fs::set_permissions(path, Permissions::from_mode(0o400)) + .context("Failed to set permissions on keyfile")?; + + Ok(()) + } +} + +fn ensure_file_permissions(path: &PathBuf, perms: u32) -> Result<()> { + // Get current permissions + let metadata = fs::metadata(path).context("Failed to get metadata for keyfile")?; + + let current_mode = metadata.permissions().mode() & 0o777; + + // Check if permissions are already 400 + if current_mode != perms { + // Set permissions to 400 + fs::set_permissions(path, Permissions::from_mode(perms)) + .context("Failed to set permissions for keyfile")?; + } + + Ok(()) +} diff --git a/packages/ciphernode/keyshare/Cargo.toml b/packages/ciphernode/keyshare/Cargo.toml index f6bec0f7..bc05ba0b 100644 --- a/packages/ciphernode/keyshare/Cargo.toml +++ b/packages/ciphernode/keyshare/Cargo.toml @@ -5,14 +5,11 @@ edition = "2021" [dependencies] actix = { workspace = true } -aes-gcm = { workspace = true, version = "=0.10.3" } anyhow = { workspace = true } -argon2 = { workspace = true, version = "=0.5.2" } async-trait = { workspace = true } data = { path = "../data" } +cipher = { path = "../cipher" } enclave-core = { path = "../core" } fhe = { path = "../fhe" } -rand = { workspace = true, version = "=0.8.5" } serde = { workspace = true } tracing = { workspace = true } -zeroize = { workspace = true, version = "=1.6.0" } diff --git a/packages/ciphernode/keyshare/src/keyshare.rs b/packages/ciphernode/keyshare/src/keyshare.rs index 6a486c16..c5b40355 100644 --- a/packages/ciphernode/keyshare/src/keyshare.rs +++ b/packages/ciphernode/keyshare/src/keyshare.rs @@ -1,7 +1,7 @@ -use crate::Encryptor; use actix::prelude::*; use anyhow::{anyhow, Result}; use async_trait::async_trait; +use cipher::Cipher; use data::{Checkpoint, FromSnapshotWithParams, Repository, Snapshot}; use enclave_core::{ BusError, CiphernodeSelected, CiphertextOutputPublished, DecryptionshareCreated, Die, @@ -9,10 +9,8 @@ use enclave_core::{ }; use fhe::{DecryptCiphertext, Fhe}; use serde::{Deserialize, Serialize}; -use std::env; use std::{process, sync::Arc}; use tracing::warn; -use zeroize::Zeroizing; pub struct Keyshare { fhe: Arc, @@ -50,7 +48,7 @@ impl Keyshare { } fn set_secret(&mut self, mut data: Vec) -> Result<()> { - let encrypted = Encryptor::from_env("CIPHERNODE_SECRET")?.encrypt_data(&mut data)?; + let encrypted = Cipher::from_env("CIPHERNODE_SECRET")?.encrypt_data(&mut data)?; self.secret = Some(encrypted); @@ -63,7 +61,7 @@ impl Keyshare { .clone() .ok_or(anyhow!("No secret share available on Keyshare"))?; - let decrypted = Encryptor::from_env("CIPHERNODE_SECRET")?.decrypt_data(&encrypted)?; + let decrypted = Cipher::from_env("CIPHERNODE_SECRET")?.decrypt_data(&encrypted)?; Ok(decrypted) } diff --git a/packages/ciphernode/keyshare/src/lib.rs b/packages/ciphernode/keyshare/src/lib.rs index 623035ce..46e4b5c9 100644 --- a/packages/ciphernode/keyshare/src/lib.rs +++ b/packages/ciphernode/keyshare/src/lib.rs @@ -1,4 +1,2 @@ -mod encryption; mod keyshare; -pub use encryption::*; pub use keyshare::*; From f4cc7433d05f30f96220f59766fbde7312345e5e Mon Sep 17 00:00:00 2001 From: ktdlr Date: Thu, 24 Oct 2024 21:25:43 +1100 Subject: [PATCH 09/45] restructure config --- packages/ciphernode/Cargo.lock | 294 +++++++----------- packages/ciphernode/Cargo.toml | 4 +- .../ciphernode/cipher/src/password_manager.rs | 1 - packages/ciphernode/config/Cargo.toml | 10 + packages/ciphernode/config/src/app_config.rs | 91 ++++++ packages/ciphernode/config/src/lib.rs | 2 + packages/ciphernode/core/src/eventbus.rs | 21 +- packages/ciphernode/enclave/Cargo.toml | 2 +- .../ciphernode/enclave/src/bin/aggregator.rs | 6 +- packages/ciphernode/enclave/src/lib.rs | 13 - packages/ciphernode/enclave/src/main.rs | 7 +- packages/ciphernode/enclave_node/Cargo.toml | 1 + .../ciphernode/enclave_node/src/aggregator.rs | 2 +- .../ciphernode/enclave_node/src/app_config.rs | 21 -- .../ciphernode/enclave_node/src/ciphernode.rs | 2 +- packages/ciphernode/enclave_node/src/lib.rs | 2 - packages/ciphernode/keyshare/src/keyshare.rs | 9 +- .../tests/test_aggregation_and_decryption.rs | 11 +- 18 files changed, 258 insertions(+), 241 deletions(-) create mode 100644 packages/ciphernode/config/Cargo.toml create mode 100644 packages/ciphernode/config/src/app_config.rs create mode 100644 packages/ciphernode/config/src/lib.rs delete mode 100644 packages/ciphernode/enclave/src/lib.rs delete mode 100644 packages/ciphernode/enclave_node/src/app_config.rs diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index 60486c5c..58fbe22e 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -1307,6 +1307,15 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "atomic" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" +dependencies = [ + "bytemuck", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -1438,9 +1447,6 @@ name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -dependencies = [ - "serde", -] [[package]] name = "bitvec" @@ -1518,6 +1524,12 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "bytemuck" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" + [[package]] name = "byteorder" version = "1.5.0" @@ -1663,22 +1675,12 @@ dependencies = [ [[package]] name = "config" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be" +version = "0.1.0" dependencies = [ - "async-trait", - "convert_case 0.6.0", - "json5", - "lazy_static", - "nom", - "pathdiff", - "ron", - "rust-ini", + "anyhow", + "dirs", + "figment", "serde", - "serde_json", - "toml", - "yaml-rust", ] [[package]] @@ -1700,41 +1702,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom", - "once_cell", - "tiny-keccak", -] - [[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "core-foundation" version = "0.9.4" @@ -1875,7 +1848,7 @@ checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ "cfg-if", "crossbeam-utils", - "hashbrown 0.14.5", + "hashbrown", "lock_api", "once_cell", "parking_lot_core 0.9.10", @@ -1972,7 +1945,7 @@ version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ - "convert_case 0.4.0", + "convert_case", "proc-macro2", "quote", "rustc_version 0.4.0", @@ -2021,6 +1994,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -2032,15 +2026,6 @@ dependencies = [ "syn 2.0.72", ] -[[package]] -name = "dlv-list" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" -dependencies = [ - "const-random", -] - [[package]] name = "doc-comment" version = "0.3.3" @@ -2174,6 +2159,7 @@ dependencies = [ "bfv", "bincode", "clap", + "config", "data", "enclave-core", "evm", @@ -2401,6 +2387,21 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "figment" +version = "0.10.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +dependencies = [ + "atomic", + "parking_lot 0.12.3", + "serde", + "serde_yaml", + "tempfile", + "uncased", + "version_check", +] + [[package]] name = "fixed-hash" version = "0.8.0" @@ -2722,12 +2723,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" - [[package]] name = "hashbrown" version = "0.14.5" @@ -3101,7 +3096,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown", ] [[package]] @@ -3214,17 +3209,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "json5" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" -dependencies = [ - "pest", - "pest_derive", - "serde", -] - [[package]] name = "k256" version = "0.13.3" @@ -3851,6 +3835,16 @@ dependencies = [ "yamux 0.13.3", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -3904,7 +3898,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "hashbrown 0.14.5", + "hashbrown", ] [[package]] @@ -4376,14 +4370,10 @@ dependencies = [ ] [[package]] -name = "ordered-multimap" -version = "0.6.0" +name = "option-ext" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e" -dependencies = [ - "dlv-list", - "hashbrown 0.13.2", -] +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "overload" @@ -4504,12 +4494,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - [[package]] name = "pem" version = "3.0.4" @@ -4537,40 +4521,6 @@ dependencies = [ "ucd-trie", ] -[[package]] -name = "pest_derive" -version = "2.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.72", -] - -[[package]] -name = "pest_meta" -version = "2.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" -dependencies = [ - "once_cell", - "pest", - "sha2", -] - [[package]] name = "petgraph" version = "0.6.5" @@ -5088,6 +5038,17 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.10.6" @@ -5231,18 +5192,6 @@ dependencies = [ "rustc-hex", ] -[[package]] -name = "ron" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" -dependencies = [ - "base64 0.21.7", - "bitflags 2.6.0", - "serde", - "serde_derive", -] - [[package]] name = "router" version = "0.1.0" @@ -5307,16 +5256,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" -[[package]] -name = "rust-ini" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -5590,24 +5529,28 @@ dependencies = [ ] [[package]] -name = "serde_spanned" -version = "0.6.8" +name = "serde_urlencoded" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ + "form_urlencoded", + "itoa", + "ryu", "serde", ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "serde_yaml" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "form_urlencoded", + "indexmap", "itoa", "ryu", "serde", + "unsafe-libyaml", ] [[package]] @@ -6179,26 +6122,11 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] [[package]] name = "toml_edit" @@ -6207,8 +6135,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap", - "serde", - "serde_spanned", "toml_datetime", "winnow", ] @@ -6371,6 +6297,15 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.15" @@ -6392,12 +6327,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - [[package]] name = "unicode-xid" version = "0.2.5" @@ -6414,6 +6343,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "unsigned-varint" version = "0.7.2" @@ -6936,15 +6871,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "yamux" version = "0.12.1" diff --git a/packages/ciphernode/Cargo.toml b/packages/ciphernode/Cargo.toml index 375a7ded..82ae8e2c 100644 --- a/packages/ciphernode/Cargo.toml +++ b/packages/ciphernode/Cargo.toml @@ -14,7 +14,7 @@ members = [ "test_helpers", "logger", "tests", - "cipher", + "cipher", "config", ] [workspace.dependencies] @@ -36,6 +36,8 @@ bincode = "1.3.3" bs58 = "0.5.1" base64 = "0.22.1" clap = { version = "4.5.17", features = ["derive"] } +dirs = "5.0.1" +figment = { version = "0.10.19", features = ["yaml", "test"] } fhe_rs = { package = "fhe", git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } fhe-traits = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } fhe-util = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } diff --git a/packages/ciphernode/cipher/src/password_manager.rs b/packages/ciphernode/cipher/src/password_manager.rs index 05dfa408..ea1b98e6 100644 --- a/packages/ciphernode/cipher/src/password_manager.rs +++ b/packages/ciphernode/cipher/src/password_manager.rs @@ -31,7 +31,6 @@ pub struct EnvPasswordManager(pub Option>>); impl EnvPasswordManager { pub fn new(value: &str) -> Result { let env_string = env::var(value)?.as_bytes().into(); - env::remove_var(value); Ok(Self(Some(Zeroizing::new(env_string)))) } } diff --git a/packages/ciphernode/config/Cargo.toml b/packages/ciphernode/config/Cargo.toml new file mode 100644 index 00000000..548449bb --- /dev/null +++ b/packages/ciphernode/config/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "config" +version = "0.1.0" +edition = "2021" + +[dependencies] +dirs = { workspace = true } +anyhow = { workspace = true } +serde = { workspace = true } +figment = { workspace = true } diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs new file mode 100644 index 00000000..eb431443 --- /dev/null +++ b/packages/ciphernode/config/src/app_config.rs @@ -0,0 +1,91 @@ +use std::path::PathBuf; + +use anyhow::Result; +use figment::{ + providers::{Format, Serialized, Yaml}, + Figment, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize)] +pub struct ContractAddresses { + pub enclave: String, + pub ciphernode_registry: String, + pub filter_registry: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct ChainConfig { + pub enabled: Option, + pub name: String, + pub rpc_url: String, // We may need multiple per chain for redundancy at a later point + pub contracts: ContractAddresses, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct AppConfig { + /// The chains config + pub chains: Vec, + /// The name for the keyfile + pub keyfile: String, + /// The base folder for enclave configuration defaults to `~/.config/enclave` on linux + pub config_dir: PathBuf, +} + +impl Default for AppConfig { + fn default() -> Self { + Self { + chains: vec![], + keyfile: "keyfile".to_string(), // ~/.config/enclave/pw + config_dir: dirs::config_dir().unwrap().join("enclave"), + } + } +} + +impl AppConfig { + pub fn get_keyfile(&self) -> PathBuf { + self.config_dir.join(&self.keyfile) + } +} + +pub fn load_config(config_path: &str) -> Result { + let config: AppConfig = Figment::from(Serialized::defaults(AppConfig::default())) + .merge(Yaml::file(config_path)) + .extract()?; + Ok(config) +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use super::*; + use figment::Jail; + + #[test] + fn test_config() { + Jail::expect_with(|jail| { + jail.set_env("HOME", "/home/testuser"); + jail.create_file( + "config.yaml", + r#" +chains: + - name: "hardhat" + rpc_url: "ws://localhost:8545" + contracts: + enclave: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" + ciphernode_registry: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" + filter_registry: "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" +"#, + )?; + + let config: AppConfig = load_config("config.yaml").map_err(|err| err.to_string())?; + assert_eq!( + config.get_keyfile(), + PathBuf::from_str("/home/testuser/.config/enclave/keyfile") + .map_err(|e| e.to_string())? + ); + Ok(()) + }); + } +} diff --git a/packages/ciphernode/config/src/lib.rs b/packages/ciphernode/config/src/lib.rs new file mode 100644 index 00000000..ba182a1b --- /dev/null +++ b/packages/ciphernode/config/src/lib.rs @@ -0,0 +1,2 @@ +mod app_config; +pub use app_config::*; diff --git a/packages/ciphernode/core/src/eventbus.rs b/packages/ciphernode/core/src/eventbus.rs index 82bb41cb..5ece9e57 100644 --- a/packages/ciphernode/core/src/eventbus.rs +++ b/packages/ciphernode/core/src/eventbus.rs @@ -1,7 +1,7 @@ use actix::prelude::*; use std::collections::{HashMap, HashSet}; -use crate::EnclaveErrorType; +use crate::{EnclaveError, EnclaveErrorType}; use super::events::{EnclaveEvent, EventId, FromError}; @@ -25,6 +25,10 @@ impl Subscribe { #[rtype(result = "Vec")] pub struct GetHistory; +#[derive(Message)] +#[rtype(result = "Vec")] +pub struct GetErrors; + #[derive(Message)] #[rtype(result = "()")] pub struct ResetHistory; @@ -79,6 +83,21 @@ impl Handler for EventBus { self.history.clone() } } + +impl Handler for EventBus { + type Result = Vec; + + fn handle(&mut self, _: GetErrors, _: &mut Context) -> Vec { + self.history.iter().filter_map(|evt| { + match evt { + EnclaveEvent::EnclaveError {data, .. } => Some(data), + _ => None + } + }).cloned().collect() + } +} + + impl Handler for EventBus { type Result = (); diff --git a/packages/ciphernode/enclave/Cargo.toml b/packages/ciphernode/enclave/Cargo.toml index edfdea76..dad96ac4 100644 --- a/packages/ciphernode/enclave/Cargo.toml +++ b/packages/ciphernode/enclave/Cargo.toml @@ -13,8 +13,8 @@ actix-rt = { workspace = true } alloy = { workspace = true } anyhow = { workspace = true } clap = { workspace = true } -config = "0.14.0" enclave_node = { path = "../enclave_node" } tokio = { workspace = true } tracing-subscriber = { workspace = true } tracing = { workspace = true } +config = { path = "../config" } diff --git a/packages/ciphernode/enclave/src/bin/aggregator.rs b/packages/ciphernode/enclave/src/bin/aggregator.rs index 37eac82a..d053a02b 100644 --- a/packages/ciphernode/enclave/src/bin/aggregator.rs +++ b/packages/ciphernode/enclave/src/bin/aggregator.rs @@ -1,5 +1,5 @@ use clap::Parser; -use enclave::load_config; +use config::load_config; use enclave_node::{listen_for_shutdown, MainAggregator}; use tracing::info; @@ -24,9 +24,9 @@ async fn main() -> Result<(), Box> { tracing_subscriber::fmt::init(); let args = Args::parse(); info!("LAUNCHING AGGREGATOR"); - let config = load_config(&args.config)?; + let conf = load_config(&args.config)?; let (bus, handle) = MainAggregator::attach( - config, + conf, args.pubkey_write_path.as_deref(), args.plaintext_write_path.as_deref(), args.data_location.as_deref(), diff --git a/packages/ciphernode/enclave/src/lib.rs b/packages/ciphernode/enclave/src/lib.rs deleted file mode 100644 index 6a439030..00000000 --- a/packages/ciphernode/enclave/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -use config::{Config, ConfigError, File}; -use enclave_node::AppConfig; - -pub fn load_config(config_path: &str) -> Result { - let config_builder = Config::builder() - .add_source(File::with_name(&config_path).required(true)) - .build()?; - - // TODO: How do we ensure things like eth addresses are in a valid format? - let config: AppConfig = config_builder.try_deserialize()?; - - Ok(config) -} diff --git a/packages/ciphernode/enclave/src/main.rs b/packages/ciphernode/enclave/src/main.rs index d81fd3d0..152fd7d1 100644 --- a/packages/ciphernode/enclave/src/main.rs +++ b/packages/ciphernode/enclave/src/main.rs @@ -1,8 +1,9 @@ use std::env; use alloy::primitives::Address; +use anyhow::{Context, Result}; use clap::Parser; -use enclave::load_config; +use config::load_config; use enclave_node::{listen_for_shutdown, MainCiphernode}; use tracing::info; @@ -33,12 +34,12 @@ pub struct Args { } #[actix_rt::main] -async fn main() -> Result<(), Box> { +async fn main() -> Result<()> { tracing_subscriber::fmt::init(); println!("\n\n\n\n\n{}", OWO); println!("\n\n\n\n"); let args = Args::parse(); - let address = Address::parse_checksummed(&args.address, None).expect("Invalid address"); + let address = Address::parse_checksummed(&args.address, None).context("Invalid address")?; info!("LAUNCHING CIPHERNODE: ({})", address); let config = load_config(&args.config)?; diff --git a/packages/ciphernode/enclave_node/Cargo.toml b/packages/ciphernode/enclave_node/Cargo.toml index 44ba409a..4f378644 100644 --- a/packages/ciphernode/enclave_node/Cargo.toml +++ b/packages/ciphernode/enclave_node/Cargo.toml @@ -16,6 +16,7 @@ alloy-primitives = { workspace = true } anyhow = { workspace = true } bfv = { path = "../bfv" } bincode = { workspace = true } +config = { path = "../config" } clap = { workspace = true } data = { path = "../data" } enclave-core = { path = "../core" } diff --git a/packages/ciphernode/enclave_node/src/aggregator.rs b/packages/ciphernode/enclave_node/src/aggregator.rs index 979115f5..d8242e15 100644 --- a/packages/ciphernode/enclave_node/src/aggregator.rs +++ b/packages/ciphernode/enclave_node/src/aggregator.rs @@ -1,5 +1,6 @@ use actix::{Actor, Addr, Context}; use anyhow::Result; +use config::AppConfig; use data::{DataStore, InMemStore, SledStore}; use enclave_core::EventBus; use evm::{ @@ -18,7 +19,6 @@ use std::sync::{Arc, Mutex}; use test_helpers::{PlaintextWriter, PublicKeyWriter}; use tokio::task::JoinHandle; -use crate::app_config::AppConfig; /// Main Ciphernode Actor /// Suprvises all children diff --git a/packages/ciphernode/enclave_node/src/app_config.rs b/packages/ciphernode/enclave_node/src/app_config.rs deleted file mode 100644 index 8d1bdef2..00000000 --- a/packages/ciphernode/enclave_node/src/app_config.rs +++ /dev/null @@ -1,21 +0,0 @@ -use serde::Deserialize; - -#[derive(Debug, Deserialize)] -pub struct ContractAddresses { - pub enclave: String, - pub ciphernode_registry: String, - pub filter_registry: String, -} - -#[derive(Debug, Deserialize)] -pub struct ChainConfig { - pub enabled: Option, - pub name: String, - pub rpc_url: String, // We may need multiple per chain for redundancy at a later point - pub contracts: ContractAddresses, -} - -#[derive(Debug, Deserialize)] -pub struct AppConfig { - pub chains: Vec, -} diff --git a/packages/ciphernode/enclave_node/src/ciphernode.rs b/packages/ciphernode/enclave_node/src/ciphernode.rs index 21ca449e..e81b006f 100644 --- a/packages/ciphernode/enclave_node/src/ciphernode.rs +++ b/packages/ciphernode/enclave_node/src/ciphernode.rs @@ -1,6 +1,7 @@ use actix::{Actor, Addr, Context}; use alloy::primitives::Address; use anyhow::Result; +use config::AppConfig; use data::{DataStore, InMemStore, SledStore}; use enclave_core::EventBus; use evm::{CiphernodeRegistrySol, EnclaveSolReader}; @@ -15,7 +16,6 @@ use sortition::Sortition; use std::sync::{Arc, Mutex}; use tokio::task::JoinHandle; -use crate::app_config::AppConfig; /// Main Ciphernode Actor /// Suprvises all children diff --git a/packages/ciphernode/enclave_node/src/lib.rs b/packages/ciphernode/enclave_node/src/lib.rs index 533fe309..69b04e25 100644 --- a/packages/ciphernode/enclave_node/src/lib.rs +++ b/packages/ciphernode/enclave_node/src/lib.rs @@ -1,9 +1,7 @@ mod aggregator; -mod app_config; mod ciphernode; mod shutdown; pub use aggregator::*; -pub use app_config::*; pub use ciphernode::*; pub use shutdown::*; diff --git a/packages/ciphernode/keyshare/src/keyshare.rs b/packages/ciphernode/keyshare/src/keyshare.rs index c5b40355..ccb395d1 100644 --- a/packages/ciphernode/keyshare/src/keyshare.rs +++ b/packages/ciphernode/keyshare/src/keyshare.rs @@ -9,7 +9,7 @@ use enclave_core::{ }; use fhe::{DecryptCiphertext, Fhe}; use serde::{Deserialize, Serialize}; -use std::{process, sync::Arc}; +use std::sync::Arc; use tracing::warn; pub struct Keyshare { @@ -127,12 +127,11 @@ impl Handler for Keyshare { }; // Save secret on state - let Ok(()) = self.set_secret(secret) else { + if let Err(err) = self.set_secret(secret) { self.bus.do_send(EnclaveEvent::from_error( EnclaveErrorType::KeyGeneration, - anyhow!("Error encrypting Keyshare for {e3_id}"), - )); - return; + err, + )) }; // Broadcast the KeyshareCreated message diff --git a/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs b/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs index fa4fc8ca..0db90213 100644 --- a/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs +++ b/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs @@ -1,8 +1,6 @@ use data::{DataStore, InMemStore}; use enclave_core::{ - CiphernodeAdded, CiphernodeSelected, CiphertextOutputPublished, DecryptionshareCreated, - E3RequestComplete, E3Requested, E3id, EnclaveEvent, EventBus, GetHistory, KeyshareCreated, - OrderedSet, PlaintextAggregated, PublicKeyAggregated, ResetHistory, Seed, Shutdown, + CiphernodeAdded, CiphernodeSelected, CiphertextOutputPublished, DecryptionshareCreated, E3RequestComplete, E3Requested, E3id, EnclaveEvent, EventBus, GetErrors, GetHistory, KeyshareCreated, OrderedSet, PlaintextAggregated, PublicKeyAggregated, ResetHistory, Seed, Shutdown }; use fhe::{setup_crp_params, ParamsWithCrp, SharedRng}; use logger::SimpleLogger; @@ -400,13 +398,18 @@ async fn test_stopped_keyshares_retain_state() -> Result<()> { .await?; let history = bus.send(GetHistory).await?; + let errors = bus.send(GetErrors).await?; + + println!("{:?}", errors); + + assert_eq!(errors.len(), 0); // SEND SHUTDOWN! bus.send(EnclaveEvent::from(Shutdown)).await?; // Reset history bus.send(ResetHistory).await?; - + // Check event count is correct assert_eq!(history.len(), 7); From 9a5585adb81a25f2f81feeaf8233a019987c0573 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Thu, 24 Oct 2024 21:37:00 +1100 Subject: [PATCH 10/45] Remove use statement --- packages/ciphernode/enclave/src/main.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/ciphernode/enclave/src/main.rs b/packages/ciphernode/enclave/src/main.rs index 152fd7d1..8b90f0b9 100644 --- a/packages/ciphernode/enclave/src/main.rs +++ b/packages/ciphernode/enclave/src/main.rs @@ -1,5 +1,3 @@ -use std::env; - use alloy::primitives::Address; use anyhow::{Context, Result}; use clap::Parser; From 642db8c137a0253664521f060cf85672621ee837 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Fri, 25 Oct 2024 19:25:58 +1100 Subject: [PATCH 11/45] Restructure CLI --- packages/ciphernode/config/src/app_config.rs | 205 ++++++++++++++++-- .../ciphernode/enclave/src/bin/aggregator.rs | 41 ---- packages/ciphernode/enclave/src/cli.rs | 35 +++ .../enclave/src/commands/aggregator/mod.rs | 32 +++ .../enclave/src/commands/aggregator/start.rs | 30 +++ .../ciphernode/enclave/src/commands/mod.rs | 28 +++ .../enclave/src/commands/password/mod.rs | 25 +++ .../enclave/src/commands/password/set.rs | 3 + .../ciphernode/enclave/src/commands/start.rs | 25 +++ packages/ciphernode/enclave/src/lib.rs | 24 ++ packages/ciphernode/enclave/src/main.rs | 52 +---- .../ciphernode/enclave_node/src/aggregator.rs | 10 +- .../ciphernode/enclave_node/src/ciphernode.rs | 11 +- .../config.yaml} | 2 + tests/basic_integration/lib/ag/key | 1 + tests/basic_integration/lib/cn1/config.yaml | 9 + tests/basic_integration/lib/cn1/key | 1 + tests/basic_integration/lib/cn2/config.yaml | 9 + tests/basic_integration/lib/cn2/key | 1 + tests/basic_integration/lib/cn3/config.yaml | 9 + tests/basic_integration/lib/cn3/key | 1 + tests/basic_integration/test.sh | 38 +++- 22 files changed, 467 insertions(+), 125 deletions(-) delete mode 100644 packages/ciphernode/enclave/src/bin/aggregator.rs create mode 100644 packages/ciphernode/enclave/src/cli.rs create mode 100644 packages/ciphernode/enclave/src/commands/aggregator/mod.rs create mode 100644 packages/ciphernode/enclave/src/commands/aggregator/start.rs create mode 100644 packages/ciphernode/enclave/src/commands/mod.rs create mode 100644 packages/ciphernode/enclave/src/commands/password/mod.rs create mode 100644 packages/ciphernode/enclave/src/commands/password/set.rs create mode 100644 packages/ciphernode/enclave/src/commands/start.rs create mode 100644 packages/ciphernode/enclave/src/lib.rs rename tests/basic_integration/lib/{ciphernode_config.yaml => ag/config.yaml} (80%) create mode 100644 tests/basic_integration/lib/ag/key create mode 100644 tests/basic_integration/lib/cn1/config.yaml create mode 100644 tests/basic_integration/lib/cn1/key create mode 100644 tests/basic_integration/lib/cn2/config.yaml create mode 100644 tests/basic_integration/lib/cn2/key create mode 100644 tests/basic_integration/lib/cn3/config.yaml create mode 100644 tests/basic_integration/lib/cn3/key diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index eb431443..82eeda57 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -1,11 +1,13 @@ -use std::path::PathBuf; - use anyhow::Result; use figment::{ providers::{Format, Serialized, Yaml}, Figment, }; use serde::{Deserialize, Serialize}; +use std::{ + env, + path::{Path, PathBuf}, +}; #[derive(Debug, Deserialize, Serialize)] pub struct ContractAddresses { @@ -25,49 +27,209 @@ pub struct ChainConfig { #[derive(Debug, Deserialize, Serialize)] pub struct AppConfig { /// The chains config - pub chains: Vec, + chains: Vec, /// The name for the keyfile - pub keyfile: String, + key_file: PathBuf, /// The base folder for enclave configuration defaults to `~/.config/enclave` on linux - pub config_dir: PathBuf, + config_dir: PathBuf, + /// The name for the database + db_file: PathBuf, + /// Config file name + config_file: PathBuf, + /// Used for testing if required + cwd: PathBuf, + /// The default config dir for the operating system this should not be changed + default_dir: PathBuf, } impl Default for AppConfig { fn default() -> Self { + let default_dir = dirs::config_dir().unwrap().join("enclave"); // ~/.config/enclave Self { chains: vec![], - keyfile: "keyfile".to_string(), // ~/.config/enclave/pw - config_dir: dirs::config_dir().unwrap().join("enclave"), + key_file: PathBuf::from("key"), // ~/.config/enclave/key + db_file: PathBuf::from("db"), // ~/.config/enclave/db + config_dir: default_dir.clone(), // ~/.config/enclave + config_file: PathBuf::from("config.yaml"), // ~/.config/enclave/config.yaml + cwd: env::current_dir().unwrap_or_default(), + default_dir, // ~/.config/enclave } } } impl AppConfig { - pub fn get_keyfile(&self) -> PathBuf { - self.config_dir.join(&self.keyfile) + fn ensure_full_path(&self, file: &PathBuf) -> PathBuf { + normalize_path({ + // If this is absolute return it + if file.is_absolute() || file.to_string_lossy().starts_with("~") { + return file.clone(); + } + + // We have to find where it should be relative from + // Assume it should be the config_dir + self.config_dir().join(file) + }) + } + + fn config_dir_impl(&self) -> PathBuf { + let config_dir = &self.config_dir; + + if config_dir.is_relative() { + // ConfigDir is relative and the config file is absolute then use the location of the + // config file. That way all paths are relative to the config file + if self.config_file.is_absolute() { + self.config_file + .parent() + .map_or_else(|| config_dir.clone(), |p| p.join(config_dir)) + } else { + // If the config_file is not set but there are relative paths use the default dir use the default dir + self.default_dir.join(config_dir) + } + } else { + // Use the absolute config_dir + config_dir.to_owned() + } + } + + pub fn use_in_mem_store(&self) -> bool { + false + } + + pub fn config_dir(&self) -> PathBuf { + normalize_path(self.config_dir_impl()) + } + + pub fn chains(&self) -> &Vec { + &self.chains + } + + pub fn key_file(&self) -> PathBuf { + self.ensure_full_path(&self.key_file) + } + + pub fn db_file(&self) -> PathBuf { + self.ensure_full_path(&self.db_file) + } + + pub fn config_file(&self) -> PathBuf { + self.ensure_full_path(&self.config_file) + } + + pub fn cwd(&self) -> PathBuf { + self.cwd.to_owned() } } -pub fn load_config(config_path: &str) -> Result { - let config: AppConfig = Figment::from(Serialized::defaults(AppConfig::default())) - .merge(Yaml::file(config_path)) +/// Load the config at the config_file or the default location if not provided +pub fn load_config(config_file: Option<&str>) -> Result { + let mut defaults = AppConfig::default(); + if let Some(file) = config_file { + defaults.config_file = file.into(); + } + + let config = Figment::from(Serialized::defaults(&defaults)) + .merge(Yaml::file(defaults.config_file())) .extract()?; + Ok(config) } +/// Utility to normalize paths +fn normalize_path(path: impl AsRef) -> PathBuf { + let path = path.as_ref(); + let mut components = Vec::new(); + + for component in path.components() { + match component { + std::path::Component::ParentDir => { + components.pop(); + } + std::path::Component::Normal(name) => { + components.push(name); + } + std::path::Component::RootDir => { + components.clear(); + components.push(component.as_os_str()); + } + std::path::Component::Prefix(prefix) => { + components.push(prefix.as_os_str()); + } + std::path::Component::CurDir => {} + } + } + + let mut result = PathBuf::new(); + for component in components { + result.push(component); + } + result +} + #[cfg(test)] mod tests { - use std::str::FromStr; - use super::*; use figment::Jail; #[test] - fn test_config() { + fn test_ensure_relative_path() { Jail::expect_with(|jail| { jail.set_env("HOME", "/home/testuser"); + + let config = AppConfig { + config_file: "/home/testuser/docs/myconfig.yaml".into(), + config_dir: "../foo".into(), + ..AppConfig::default() + }; + + assert_eq!(config.key_file(), PathBuf::from("/home/testuser/foo/key")); + assert_eq!(config.db_file(), PathBuf::from("/home/testuser/foo/db")); + + Ok(()) + }); + } + + #[test] + fn test_defaults() { + Jail::expect_with(|jail| { + jail.set_env("HOME", "/home/testuser"); + + let config = AppConfig::default(); + + assert_eq!( + config.key_file(), + PathBuf::from("/home/testuser/.config/enclave/key") + ); + + assert_eq!( + config.db_file(), + PathBuf::from("/home/testuser/.config/enclave/db") + ); + + assert_eq!( + config.config_file(), + PathBuf::from("/home/testuser/.config/enclave/config.yaml") + ); + + assert_eq!( + config.config_dir(), + PathBuf::from("/home/testuser/.config/enclave/") + ); + + Ok(()) + }); + } + + #[test] + fn test_config() { + Jail::expect_with(|jail| { + let home = format!("{}", jail.directory().to_string_lossy()); + let filename = format!("{}/.config/enclave/config.yaml", home); + let filedir = format!("{}/.config/enclave", home); + + jail.create_dir(filedir)?; + jail.set_env("HOME", &home); jail.create_file( - "config.yaml", + filename, r#" chains: - name: "hardhat" @@ -79,12 +241,15 @@ chains: "#, )?; - let config: AppConfig = load_config("config.yaml").map_err(|err| err.to_string())?; + let config: AppConfig = load_config(None).map_err(|err| err.to_string())?; + let chain = config.chains().first().unwrap(); + assert_eq!(chain.name, "hardhat"); + assert_eq!(chain.rpc_url, "ws://localhost:8545"); assert_eq!( - config.get_keyfile(), - PathBuf::from_str("/home/testuser/.config/enclave/keyfile") - .map_err(|e| e.to_string())? + chain.contracts.enclave, + "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" ); + Ok(()) }); } diff --git a/packages/ciphernode/enclave/src/bin/aggregator.rs b/packages/ciphernode/enclave/src/bin/aggregator.rs deleted file mode 100644 index d053a02b..00000000 --- a/packages/ciphernode/enclave/src/bin/aggregator.rs +++ /dev/null @@ -1,41 +0,0 @@ -use clap::Parser; -use config::load_config; -use enclave_node::{listen_for_shutdown, MainAggregator}; -use tracing::info; - -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -struct Args { - #[arg(short, long)] - pub config: String, - - // These are for testing and may be removed later - // or put under a compile flag - #[arg(short = 'k', long = "pubkey-write-path")] - pub pubkey_write_path: Option, - #[arg(short, long = "plaintext-write-path")] - pub plaintext_write_path: Option, - #[arg(short, long = "data-location")] - pub data_location: Option, -} - -#[actix_rt::main] -async fn main() -> Result<(), Box> { - tracing_subscriber::fmt::init(); - let args = Args::parse(); - info!("LAUNCHING AGGREGATOR"); - let conf = load_config(&args.config)?; - let (bus, handle) = MainAggregator::attach( - conf, - args.pubkey_write_path.as_deref(), - args.plaintext_write_path.as_deref(), - args.data_location.as_deref(), - ) - .await?; - - tokio::spawn(listen_for_shutdown(bus.into(), handle)); - - std::future::pending::<()>().await; - - Ok(()) -} diff --git a/packages/ciphernode/enclave/src/cli.rs b/packages/ciphernode/enclave/src/cli.rs new file mode 100644 index 00000000..374e1d74 --- /dev/null +++ b/packages/ciphernode/enclave/src/cli.rs @@ -0,0 +1,35 @@ +use crate::commands::{aggregator, password, start, Commands}; +use anyhow::*; +use clap::Parser; + +#[derive(Parser)] +#[command(name = "mycli")] +#[command(about = "A CLI application", long_about = None)] +pub struct Cli { + /// Path to config file + #[arg(long, global = true)] + config: Option, + + #[command(subcommand)] + command: Commands, +} + +impl Cli { + pub async fn execute(self) -> Result<()> { + let config_path = self.config.as_deref(); + + match self.command { + Commands::Start { address } => { + start::execute(config_path, &address).await? + } + Commands::Password { command } => { + password::execute(command, config_path).await? + } + Commands::Aggregator { command } => { + aggregator::execute(command, config_path).await? + } + } + + Ok(()) + } +} diff --git a/packages/ciphernode/enclave/src/commands/aggregator/mod.rs b/packages/ciphernode/enclave/src/commands/aggregator/mod.rs new file mode 100644 index 00000000..14c22b59 --- /dev/null +++ b/packages/ciphernode/enclave/src/commands/aggregator/mod.rs @@ -0,0 +1,32 @@ +mod start; +use anyhow::*; +use clap::Subcommand; + +#[derive(Subcommand)] +pub enum AggregatorCommands { + Start { + #[arg(short = 'k', long = "pubkey-write-path")] + pubkey_write_path: Option, + #[arg(short, long = "plaintext-write-path")] + plaintext_write_path: Option, + }, +} + +pub async fn execute(command: AggregatorCommands, config_path: Option<&str>) -> Result<()> { + if let Some(path) = config_path { + println!("Using config from: {}", path); + } + + match command { + AggregatorCommands::Start { + pubkey_write_path, + plaintext_write_path, + } => start::execute( + config_path, + pubkey_write_path.as_deref(), + plaintext_write_path.as_deref(), + ).await? + }; + + Ok(()) +} diff --git a/packages/ciphernode/enclave/src/commands/aggregator/start.rs b/packages/ciphernode/enclave/src/commands/aggregator/start.rs new file mode 100644 index 00000000..da451b72 --- /dev/null +++ b/packages/ciphernode/enclave/src/commands/aggregator/start.rs @@ -0,0 +1,30 @@ +use anyhow::*; +use config::load_config; +use enclave_node::{listen_for_shutdown, MainAggregator}; +use tracing::info; + +use crate::owo; + +pub async fn execute( + config_path:Option<&str>, + pubkey_write_path: Option<&str>, + plaintext_write_path: Option<&str>, +) -> Result<()> { + owo(); + + info!("LAUNCHING AGGREGATOR"); + + let conf = load_config(config_path)?; + let (bus, handle) = MainAggregator::attach( + conf, + pubkey_write_path, + plaintext_write_path, + ) + .await?; + + tokio::spawn(listen_for_shutdown(bus.into(), handle)); + + std::future::pending::<()>().await; + + Ok(()) +} diff --git a/packages/ciphernode/enclave/src/commands/mod.rs b/packages/ciphernode/enclave/src/commands/mod.rs new file mode 100644 index 00000000..ebe0c0fd --- /dev/null +++ b/packages/ciphernode/enclave/src/commands/mod.rs @@ -0,0 +1,28 @@ +pub mod start; +pub mod password; +pub mod aggregator; + +use aggregator::AggregatorCommands; +use clap::Subcommand; +use self::password::PasswordCommands; + +#[derive(Subcommand)] +pub enum Commands { + /// Start the application + Start { + #[arg(long)] + address: String, + }, + + Aggregator { + #[command(subcommand)] + command: AggregatorCommands, + }, + + /// Password management commands + Password { + #[command(subcommand)] + command: PasswordCommands, + }, +} + diff --git a/packages/ciphernode/enclave/src/commands/password/mod.rs b/packages/ciphernode/enclave/src/commands/password/mod.rs new file mode 100644 index 00000000..1caa5906 --- /dev/null +++ b/packages/ciphernode/enclave/src/commands/password/mod.rs @@ -0,0 +1,25 @@ +mod set; +use anyhow::*; +use clap::Subcommand; + +#[derive(Subcommand)] +pub enum PasswordCommands { + /// Set a new password + Set { + /// The new password + #[arg(short, long)] + value: String, + }, +} + +pub async fn execute(command: PasswordCommands, config_path: Option<&str>) -> Result<()> { + if let Some(path) = config_path { + println!("Using config from: {}", path); + } + + match command { + PasswordCommands::Set { value } => set::execute(value) + }; + + Ok(()) +} diff --git a/packages/ciphernode/enclave/src/commands/password/set.rs b/packages/ciphernode/enclave/src/commands/password/set.rs new file mode 100644 index 00000000..d5676d4b --- /dev/null +++ b/packages/ciphernode/enclave/src/commands/password/set.rs @@ -0,0 +1,3 @@ +pub fn execute(value:String) { + println!("password set to {}", value); +} diff --git a/packages/ciphernode/enclave/src/commands/start.rs b/packages/ciphernode/enclave/src/commands/start.rs new file mode 100644 index 00000000..2e1ea162 --- /dev/null +++ b/packages/ciphernode/enclave/src/commands/start.rs @@ -0,0 +1,25 @@ +use alloy::primitives::Address; +use anyhow::{Context, Result}; +use config::load_config; +use enclave_node::{listen_for_shutdown, MainCiphernode}; +use tracing::info; + +use crate::owo; + +pub async fn execute(config_path:Option<&str>, address:&str) -> Result<()> { + + owo(); + + let address = Address::parse_checksummed(&address, None).context("Invalid address")?; + info!("LAUNCHING CIPHERNODE: ({})", address); + let config = load_config(config_path)?; + + let (bus, handle) = MainCiphernode::attach(config, address).await?; + + tokio::spawn(listen_for_shutdown(bus.into(), handle)); + + std::future::pending::<()>().await; + + Ok(()) +} + diff --git a/packages/ciphernode/enclave/src/lib.rs b/packages/ciphernode/enclave/src/lib.rs new file mode 100644 index 00000000..e85f7b22 --- /dev/null +++ b/packages/ciphernode/enclave/src/lib.rs @@ -0,0 +1,24 @@ +pub mod commands; +pub mod cli; + +const OWO: &str = r#" + ___ ___ ___ ___ ___ + /\__\ /\ \ /\__\ /\ \ ___ /\__\ + /:/ _/_ \:\ \ /:/ / /::\ \ /\ \ /:/ _/_ + /:/ /\__\ \:\ \ /:/ / /:/\:\ \ \:\ \ /:/ /\__\ + /:/ /:/ _/_ _____\:\ \ /:/ / ___ ___ ___ /:/ /::\ \ \:\ \ /:/ /:/ _/_ + /:/_/:/ /\__\ /::::::::\__\ /:/__/ /\__\ /\ \ /\__\ /:/_/:/\:\__\ ___ \:\__\ /:/_/:/ /\__\ + \:\/:/ /:/ / \:\~~\~~\/__/ \:\ \ /:/ / \:\ \ /:/ / \:\/:/ \/__/ /\ \ |:| | \:\/:/ /:/ / + \::/_/:/ / \:\ \ \:\ /:/ / \:\ /:/ / \::/__/ \:\ \|:| | \::/_/:/ / + \:\/:/ / \:\ \ \:\/:/ / \:\/:/ / \:\ \ \:\__|:|__| \:\/:/ / + \::/ / \:\__\ \::/ / \::/ / \:\__\ \::::/__/ \::/ / + \/__/ \/__/ \/__/ \/__/ \/__/ ~~~~ \/__/ + +"#; + +pub fn owo() { + println!("\n\n\n\n\n{}", OWO); + println!("\n\n\n\n"); +} + + diff --git a/packages/ciphernode/enclave/src/main.rs b/packages/ciphernode/enclave/src/main.rs index 8b90f0b9..7c88e9e2 100644 --- a/packages/ciphernode/enclave/src/main.rs +++ b/packages/ciphernode/enclave/src/main.rs @@ -1,52 +1,14 @@ -use alloy::primitives::Address; -use anyhow::{Context, Result}; use clap::Parser; -use config::load_config; -use enclave_node::{listen_for_shutdown, MainCiphernode}; -use tracing::info; - -const OWO: &str = r#" - ___ ___ ___ ___ ___ - /\__\ /\ \ /\__\ /\ \ ___ /\__\ - /:/ _/_ \:\ \ /:/ / /::\ \ /\ \ /:/ _/_ - /:/ /\__\ \:\ \ /:/ / /:/\:\ \ \:\ \ /:/ /\__\ - /:/ /:/ _/_ _____\:\ \ /:/ / ___ ___ ___ /:/ /::\ \ \:\ \ /:/ /:/ _/_ - /:/_/:/ /\__\ /::::::::\__\ /:/__/ /\__\ /\ \ /\__\ /:/_/:/\:\__\ ___ \:\__\ /:/_/:/ /\__\ - \:\/:/ /:/ / \:\~~\~~\/__/ \:\ \ /:/ / \:\ \ /:/ / \:\/:/ \/__/ /\ \ |:| | \:\/:/ /:/ / - \::/_/:/ / \:\ \ \:\ /:/ / \:\ /:/ / \::/__/ \:\ \|:| | \::/_/:/ / - \:\/:/ / \:\ \ \:\/:/ / \:\/:/ / \:\ \ \:\__|:|__| \:\/:/ / - \::/ / \:\__\ \::/ / \::/ / \:\__\ \::::/__/ \::/ / - \/__/ \/__/ \/__/ \/__/ \/__/ ~~~~ \/__/ - -"#; - -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -pub struct Args { - #[arg(short, long)] - pub address: String, - #[arg(short, long)] - pub config: String, - #[arg(short, long = "data-location")] - pub data_location: Option, -} +use enclave::cli::Cli; #[actix_rt::main] -async fn main() -> Result<()> { +pub async fn main() { tracing_subscriber::fmt::init(); - println!("\n\n\n\n\n{}", OWO); - println!("\n\n\n\n"); - let args = Args::parse(); - let address = Address::parse_checksummed(&args.address, None).context("Invalid address")?; - info!("LAUNCHING CIPHERNODE: ({})", address); - let config = load_config(&args.config)?; - - let (bus, handle) = - MainCiphernode::attach(config, address, args.data_location.as_deref()).await?; - - tokio::spawn(listen_for_shutdown(bus.into(), handle)); - std::future::pending::<()>().await; + let cli = Cli::parse(); - Ok(()) + match cli.execute().await { + Ok(_) => (), + Err(_) => println!("There was a problem running. Goodbye"), + } } diff --git a/packages/ciphernode/enclave_node/src/aggregator.rs b/packages/ciphernode/enclave_node/src/aggregator.rs index d8242e15..28a182e1 100644 --- a/packages/ciphernode/enclave_node/src/aggregator.rs +++ b/packages/ciphernode/enclave_node/src/aggregator.rs @@ -49,23 +49,23 @@ impl MainAggregator { config: AppConfig, pubkey_write_path: Option<&str>, plaintext_write_path: Option<&str>, - data_location: Option<&str>, ) -> Result<(Addr, JoinHandle<()>)> { let bus = EventBus::new(true).start(); let rng = Arc::new(Mutex::new( rand_chacha::ChaCha20Rng::from_rng(OsRng).expect("Failed to create RNG"), )); - let store: DataStore = match data_location { - Some(loc) => (&SledStore::new(&bus, loc)?.start()).into(), - None => (&InMemStore::new(true).start()).into(), + let store: DataStore = if !config.use_in_mem_store() { + (&SledStore::new(&bus, &config.db_file().to_string_lossy())?.start()).into() + } else { + (&InMemStore::new(true).start()).into() }; let repositories = store.repositories(); let sortition = Sortition::attach(&bus, repositories.sortition()); let signer = pull_eth_signer_from_env("PRIVATE_KEY").await?; for chain in config - .chains + .chains() .iter() .filter(|chain| chain.enabled.unwrap_or(true)) { diff --git a/packages/ciphernode/enclave_node/src/ciphernode.rs b/packages/ciphernode/enclave_node/src/ciphernode.rs index e81b006f..2c99a718 100644 --- a/packages/ciphernode/enclave_node/src/ciphernode.rs +++ b/packages/ciphernode/enclave_node/src/ciphernode.rs @@ -16,7 +16,6 @@ use sortition::Sortition; use std::sync::{Arc, Mutex}; use tokio::task::JoinHandle; - /// Main Ciphernode Actor /// Suprvises all children // TODO: add supervision logic @@ -54,16 +53,16 @@ impl MainCiphernode { pub async fn attach( config: AppConfig, address: Address, - data_location: Option<&str>, ) -> Result<(Addr, JoinHandle<()>)> { let rng = Arc::new(Mutex::new( rand_chacha::ChaCha20Rng::from_rng(OsRng).expect("Failed to create RNG"), )); let bus = EventBus::new(true).start(); - let store: DataStore = match data_location { - Some(loc) => (&SledStore::new(&bus, loc)?.start()).into(), - None => (&InMemStore::new(true).start()).into(), + let store: DataStore = if !config.use_in_mem_store() { + (&SledStore::new(&bus, &config.db_file().to_string_lossy())?.start()).into() + } else { + (&InMemStore::new(true).start()).into() }; let repositories = store.repositories(); @@ -72,7 +71,7 @@ impl MainCiphernode { let selector = CiphernodeSelector::attach(&bus, &sortition, &address.to_string()); for chain in config - .chains + .chains() .iter() .filter(|chain| chain.enabled.unwrap_or(true)) { diff --git a/tests/basic_integration/lib/ciphernode_config.yaml b/tests/basic_integration/lib/ag/config.yaml similarity index 80% rename from tests/basic_integration/lib/ciphernode_config.yaml rename to tests/basic_integration/lib/ag/config.yaml index b8f6e9ce..adc6e6f9 100644 --- a/tests/basic_integration/lib/ciphernode_config.yaml +++ b/tests/basic_integration/lib/ag/config.yaml @@ -1,3 +1,5 @@ +config_dir: . +address: 0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199 chains: - name: "hardhat" rpc_url: "ws://localhost:8545" diff --git a/tests/basic_integration/lib/ag/key b/tests/basic_integration/lib/ag/key new file mode 100644 index 00000000..0970f941 --- /dev/null +++ b/tests/basic_integration/lib/ag/key @@ -0,0 +1 @@ +We are the music makers and we are the dreamers of the dreams. diff --git a/tests/basic_integration/lib/cn1/config.yaml b/tests/basic_integration/lib/cn1/config.yaml new file mode 100644 index 00000000..4c856c8a --- /dev/null +++ b/tests/basic_integration/lib/cn1/config.yaml @@ -0,0 +1,9 @@ +config_dir: . +address: 0x2546BcD3c84621e976D8185a91A922aE77ECEc30 +chains: + - name: "hardhat" + rpc_url: "ws://localhost:8545" + contracts: + enclave: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" + ciphernode_registry: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" + filter_registry: "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" diff --git a/tests/basic_integration/lib/cn1/key b/tests/basic_integration/lib/cn1/key new file mode 100644 index 00000000..0970f941 --- /dev/null +++ b/tests/basic_integration/lib/cn1/key @@ -0,0 +1 @@ +We are the music makers and we are the dreamers of the dreams. diff --git a/tests/basic_integration/lib/cn2/config.yaml b/tests/basic_integration/lib/cn2/config.yaml new file mode 100644 index 00000000..d57b9879 --- /dev/null +++ b/tests/basic_integration/lib/cn2/config.yaml @@ -0,0 +1,9 @@ +config_dir: . +address: 0xbDA5747bFD65F08deb54cb465eB87D40e51B197E +chains: + - name: "hardhat" + rpc_url: "ws://localhost:8545" + contracts: + enclave: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" + ciphernode_registry: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" + filter_registry: "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" diff --git a/tests/basic_integration/lib/cn2/key b/tests/basic_integration/lib/cn2/key new file mode 100644 index 00000000..0970f941 --- /dev/null +++ b/tests/basic_integration/lib/cn2/key @@ -0,0 +1 @@ +We are the music makers and we are the dreamers of the dreams. diff --git a/tests/basic_integration/lib/cn3/config.yaml b/tests/basic_integration/lib/cn3/config.yaml new file mode 100644 index 00000000..6a3aa585 --- /dev/null +++ b/tests/basic_integration/lib/cn3/config.yaml @@ -0,0 +1,9 @@ +config_dir: . +address: 0xdD2FD4581271e230360230F9337D5c0430Bf44C0 +chains: + - name: "hardhat" + rpc_url: "ws://localhost:8545" + contracts: + enclave: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" + ciphernode_registry: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" + filter_registry: "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" diff --git a/tests/basic_integration/lib/cn3/key b/tests/basic_integration/lib/cn3/key new file mode 100644 index 00000000..0970f941 --- /dev/null +++ b/tests/basic_integration/lib/cn3/key @@ -0,0 +1 @@ +We are the music makers and we are the dreamers of the dreams. diff --git a/tests/basic_integration/test.sh b/tests/basic_integration/test.sh index a88bbfbb..252883a3 100755 --- a/tests/basic_integration/test.sh +++ b/tests/basic_integration/test.sh @@ -78,16 +78,33 @@ waiton-files() { done } +# This is temporary until we write the command api +set_password() { + local name="$1" + local password="$2" + local config_dir="$SCRIPT_DIR/lib/$name" + mkdir -p $config_dir + echo "$password" > "$config_dir/key" + chmod 400 "$config_dir/key" +} + launch_ciphernode() { - local address="$1" - local secret="$2" - heading "Launch ciphernode $address" - CIPHERNODE_SECRET=$secret yarn ciphernode:launch \ - --address "$address" \ - --config "$SCRIPT_DIR/lib/ciphernode_config.yaml" \ - --data-location "$SCRIPT_DIR/output/$address.db" & + local name="$1" + heading "Launch ciphernode $name" + yarn ciphernode:launch \ + --config "$SCRIPT_DIR/lib/$name/config.yaml" } + +# XXX: WE NEED TO NOW ADD THE PRIVATE KEY TO BE STORED ON THE DATASTORE +# - Setup command files one for each command +# - Setup cli parsing for the commands that defer to the commands +# - We must have access to the config +# +# enclave set-password +# +# +# enclave set-private-key launch_aggregator() { local private_key="$1" PRIVATE_KEY=$private_key yarn ciphernode:aggregator \ @@ -116,8 +133,13 @@ until curl -f -s "http://localhost:8545" > /dev/null; do sleep 1 done -# Launch 4 ciphernodes +# Set the password for all ciphernodes +set_password cn1 "$CIPHERNODE_SECRET" +set_password cn2 "$CIPHERNODE_SECRET" +set_password cn3 "$CIPHERNODE_SECRET" +set_password ag "$CIPHERNODE_SECRET" +# Launch 4 ciphernodes launch_ciphernode "$CIPHERNODE_ADDRESS_1" "$CIPHERNODE_SECRET" launch_ciphernode "$CIPHERNODE_ADDRESS_2" "$CIPHERNODE_SECRET" launch_ciphernode "$CIPHERNODE_ADDRESS_3" "$CIPHERNODE_SECRET" From 9b138480ca46b8d4a30967d7508817caf4fe0ef2 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Fri, 25 Oct 2024 19:30:26 +1100 Subject: [PATCH 12/45] Tidy up docs --- packages/ciphernode/enclave/src/cli.rs | 4 ++-- packages/ciphernode/enclave/src/commands/mod.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/ciphernode/enclave/src/cli.rs b/packages/ciphernode/enclave/src/cli.rs index 374e1d74..6d25d882 100644 --- a/packages/ciphernode/enclave/src/cli.rs +++ b/packages/ciphernode/enclave/src/cli.rs @@ -3,8 +3,8 @@ use anyhow::*; use clap::Parser; #[derive(Parser)] -#[command(name = "mycli")] -#[command(about = "A CLI application", long_about = None)] +#[command(name = "enclave")] +#[command(about = "A CLI for interacting with Enclave the open-source protocol for Encrypted Execution Environments (E3)", long_about = None)] pub struct Cli { /// Path to config file #[arg(long, global = true)] diff --git a/packages/ciphernode/enclave/src/commands/mod.rs b/packages/ciphernode/enclave/src/commands/mod.rs index ebe0c0fd..f30f4d98 100644 --- a/packages/ciphernode/enclave/src/commands/mod.rs +++ b/packages/ciphernode/enclave/src/commands/mod.rs @@ -14,6 +14,7 @@ pub enum Commands { address: String, }, + /// Aggregator node management commands Aggregator { #[command(subcommand)] command: AggregatorCommands, From 90cb67760b792ef779775c6b55d8921b5a48ff81 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Fri, 25 Oct 2024 19:44:55 +1100 Subject: [PATCH 13/45] Fix up redundant files --- package.json | 2 +- packages/ciphernode/enclave/src/cli.rs | 35 ------------- .../enclave/src/commands/aggregator/mod.rs | 21 ++++---- packages/ciphernode/enclave/src/lib.rs | 24 --------- packages/ciphernode/enclave/src/main.rs | 50 ++++++++++++++++++- packages/ciphernode/scripts/aggregator.sh | 3 -- tests/basic_integration/test.sh | 4 +- 7 files changed, 64 insertions(+), 75 deletions(-) delete mode 100644 packages/ciphernode/enclave/src/cli.rs delete mode 100644 packages/ciphernode/enclave/src/lib.rs delete mode 100755 packages/ciphernode/scripts/aggregator.sh diff --git a/package.json b/package.json index 902e691c..064f021d 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "test": "yarn evm:test && yarn ciphernode:test", "test:integration": "./tests/basic_integration/test.sh", "coverage": "yarn evm:coverage", - "ciphernode:launch": "cd packages/ciphernode && ./scripts/launch.sh", + "enclave": "cd packages/ciphernode && ./scripts/launch.sh", "ciphernode:lint": "cd packages/ciphernode && cargo fmt -- --check", "ciphernode:add": "cd packages/evm && yarn ciphernode:add", "ciphernode:remove": "cd packages/evm && yarn ciphernode:remove", diff --git a/packages/ciphernode/enclave/src/cli.rs b/packages/ciphernode/enclave/src/cli.rs deleted file mode 100644 index 6d25d882..00000000 --- a/packages/ciphernode/enclave/src/cli.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::commands::{aggregator, password, start, Commands}; -use anyhow::*; -use clap::Parser; - -#[derive(Parser)] -#[command(name = "enclave")] -#[command(about = "A CLI for interacting with Enclave the open-source protocol for Encrypted Execution Environments (E3)", long_about = None)] -pub struct Cli { - /// Path to config file - #[arg(long, global = true)] - config: Option, - - #[command(subcommand)] - command: Commands, -} - -impl Cli { - pub async fn execute(self) -> Result<()> { - let config_path = self.config.as_deref(); - - match self.command { - Commands::Start { address } => { - start::execute(config_path, &address).await? - } - Commands::Password { command } => { - password::execute(command, config_path).await? - } - Commands::Aggregator { command } => { - aggregator::execute(command, config_path).await? - } - } - - Ok(()) - } -} diff --git a/packages/ciphernode/enclave/src/commands/aggregator/mod.rs b/packages/ciphernode/enclave/src/commands/aggregator/mod.rs index 14c22b59..bfa97c04 100644 --- a/packages/ciphernode/enclave/src/commands/aggregator/mod.rs +++ b/packages/ciphernode/enclave/src/commands/aggregator/mod.rs @@ -4,28 +4,31 @@ use clap::Subcommand; #[derive(Subcommand)] pub enum AggregatorCommands { + /// Start the application as an aggregator Start { + /// Testing only: A path to write the latest pubkey to #[arg(short = 'k', long = "pubkey-write-path")] pubkey_write_path: Option, + + /// Testing only: A path to write the latest plaintexts to #[arg(short, long = "plaintext-write-path")] plaintext_write_path: Option, }, } pub async fn execute(command: AggregatorCommands, config_path: Option<&str>) -> Result<()> { - if let Some(path) = config_path { - println!("Using config from: {}", path); - } - match command { AggregatorCommands::Start { pubkey_write_path, plaintext_write_path, - } => start::execute( - config_path, - pubkey_write_path.as_deref(), - plaintext_write_path.as_deref(), - ).await? + } => { + start::execute( + config_path, + pubkey_write_path.as_deref(), + plaintext_write_path.as_deref(), + ) + .await? + } }; Ok(()) diff --git a/packages/ciphernode/enclave/src/lib.rs b/packages/ciphernode/enclave/src/lib.rs deleted file mode 100644 index e85f7b22..00000000 --- a/packages/ciphernode/enclave/src/lib.rs +++ /dev/null @@ -1,24 +0,0 @@ -pub mod commands; -pub mod cli; - -const OWO: &str = r#" - ___ ___ ___ ___ ___ - /\__\ /\ \ /\__\ /\ \ ___ /\__\ - /:/ _/_ \:\ \ /:/ / /::\ \ /\ \ /:/ _/_ - /:/ /\__\ \:\ \ /:/ / /:/\:\ \ \:\ \ /:/ /\__\ - /:/ /:/ _/_ _____\:\ \ /:/ / ___ ___ ___ /:/ /::\ \ \:\ \ /:/ /:/ _/_ - /:/_/:/ /\__\ /::::::::\__\ /:/__/ /\__\ /\ \ /\__\ /:/_/:/\:\__\ ___ \:\__\ /:/_/:/ /\__\ - \:\/:/ /:/ / \:\~~\~~\/__/ \:\ \ /:/ / \:\ \ /:/ / \:\/:/ \/__/ /\ \ |:| | \:\/:/ /:/ / - \::/_/:/ / \:\ \ \:\ /:/ / \:\ /:/ / \::/__/ \:\ \|:| | \::/_/:/ / - \:\/:/ / \:\ \ \:\/:/ / \:\/:/ / \:\ \ \:\__|:|__| \:\/:/ / - \::/ / \:\__\ \::/ / \::/ / \:\__\ \::::/__/ \::/ / - \/__/ \/__/ \/__/ \/__/ \/__/ ~~~~ \/__/ - -"#; - -pub fn owo() { - println!("\n\n\n\n\n{}", OWO); - println!("\n\n\n\n"); -} - - diff --git a/packages/ciphernode/enclave/src/main.rs b/packages/ciphernode/enclave/src/main.rs index 7c88e9e2..b094551f 100644 --- a/packages/ciphernode/enclave/src/main.rs +++ b/packages/ciphernode/enclave/src/main.rs @@ -1,6 +1,54 @@ +use anyhow::Result; use clap::Parser; -use enclave::cli::Cli; +use commands::{aggregator, password, start, Commands}; +pub mod cli; +pub mod commands; +const OWO: &str = r#" + ___ ___ ___ ___ ___ + /\__\ /\ \ /\__\ /\ \ ___ /\__\ + /:/ _/_ \:\ \ /:/ / /::\ \ /\ \ /:/ _/_ + /:/ /\__\ \:\ \ /:/ / /:/\:\ \ \:\ \ /:/ /\__\ + /:/ /:/ _/_ _____\:\ \ /:/ / ___ ___ ___ /:/ /::\ \ \:\ \ /:/ /:/ _/_ + /:/_/:/ /\__\ /::::::::\__\ /:/__/ /\__\ /\ \ /\__\ /:/_/:/\:\__\ ___ \:\__\ /:/_/:/ /\__\ + \:\/:/ /:/ / \:\~~\~~\/__/ \:\ \ /:/ / \:\ \ /:/ / \:\/:/ \/__/ /\ \ |:| | \:\/:/ /:/ / + \::/_/:/ / \:\ \ \:\ /:/ / \:\ /:/ / \::/__/ \:\ \|:| | \::/_/:/ / + \:\/:/ / \:\ \ \:\/:/ / \:\/:/ / \:\ \ \:\__|:|__| \:\/:/ / + \::/ / \:\__\ \::/ / \::/ / \:\__\ \::::/__/ \::/ / + \/__/ \/__/ \/__/ \/__/ \/__/ ~~~~ \/__/ + +"#; + +pub fn owo() { + println!("\n\n\n\n\n{}", OWO); + println!("\n\n\n\n"); +} + +#[derive(Parser)] +#[command(name = "enclave")] +#[command(about = "A CLI for interacting with Enclave the open-source protocol for Encrypted Execution Environments (E3)", long_about = None)] +pub struct Cli { + /// Path to config file + #[arg(long, global = true)] + config: Option, + + #[command(subcommand)] + command: Commands, +} + +impl Cli { + pub async fn execute(self) -> Result<()> { + let config_path = self.config.as_deref(); + + match self.command { + Commands::Start { address } => start::execute(config_path, &address).await?, + Commands::Password { command } => password::execute(command, config_path).await?, + Commands::Aggregator { command } => aggregator::execute(command, config_path).await?, + } + + Ok(()) + } +} #[actix_rt::main] pub async fn main() { tracing_subscriber::fmt::init(); diff --git a/packages/ciphernode/scripts/aggregator.sh b/packages/ciphernode/scripts/aggregator.sh deleted file mode 100755 index 6f113156..00000000 --- a/packages/ciphernode/scripts/aggregator.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -RUSTFLAGS="-A warnings" cargo run --bin aggregator -- "$@" diff --git a/tests/basic_integration/test.sh b/tests/basic_integration/test.sh index 252883a3..fe7d9002 100755 --- a/tests/basic_integration/test.sh +++ b/tests/basic_integration/test.sh @@ -97,8 +97,8 @@ launch_ciphernode() { # XXX: WE NEED TO NOW ADD THE PRIVATE KEY TO BE STORED ON THE DATASTORE -# - Setup command files one for each command -# - Setup cli parsing for the commands that defer to the commands +# - [x] Setup command files one for each command +# - [x] Setup cli parsing for the commands that defer to the commands # - We must have access to the config # # enclave set-password From 00440d7cc20d72e00d529a51c56ca684f8df7d14 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Sat, 26 Oct 2024 21:43:55 +1100 Subject: [PATCH 14/45] Save encrypted private key --- packages/ciphernode/Cargo.lock | 79 +++++++++++ packages/ciphernode/cipher/Cargo.toml | 3 + packages/ciphernode/cipher/src/cipher.rs | 84 +++++++----- packages/ciphernode/cipher/src/lib.rs | 1 + .../ciphernode/cipher/src/password_manager.rs | 29 ++-- packages/ciphernode/data/src/sled_store.rs | 66 ++++++--- packages/ciphernode/enclave/Cargo.toml | 11 +- .../enclave/src/commands/aggregator/mod.rs | 5 +- .../enclave/src/commands/aggregator/start.rs | 14 +- .../ciphernode/enclave/src/commands/mod.rs | 8 ++ .../enclave/src/commands/password/create.rs | 47 +++++++ .../enclave/src/commands/password/delete.rs | 57 ++++++++ .../enclave/src/commands/password/mod.rs | 31 +++-- .../src/commands/password/overwrite.rs | 11 ++ .../enclave/src/commands/password/set.rs | 3 - .../ciphernode/enclave/src/commands/start.rs | 12 +- .../enclave/src/commands/wallet/mod.rs | 24 ++++ .../enclave/src/commands/wallet/set.rs | 23 ++++ packages/ciphernode/enclave/src/main.rs | 19 ++- packages/ciphernode/enclave_node/Cargo.toml | 1 + .../ciphernode/enclave_node/src/aggregator.rs | 119 ++++++---------- .../ciphernode/enclave_node/src/ciphernode.rs | 127 ++++++------------ .../ciphernode/enclave_node/src/datastore.rs | 21 +++ packages/ciphernode/enclave_node/src/lib.rs | 2 + packages/ciphernode/evm/Cargo.toml | 1 + .../ciphernode/evm/src/enclave_sol_writer.rs | 2 + packages/ciphernode/keyshare/src/keyshare.rs | 8 +- packages/ciphernode/router/Cargo.toml | 1 + packages/ciphernode/router/src/hooks.rs | 12 +- .../ciphernode/router/src/keyshare_feature.rs | 0 .../ciphernode/router/src/repositories.rs | 4 + packages/ciphernode/tests/Cargo.toml | 1 + .../tests/test_aggregation_and_decryption.rs | 31 +++-- tests/basic_integration/test.sh | 3 +- 34 files changed, 566 insertions(+), 294 deletions(-) create mode 100644 packages/ciphernode/enclave/src/commands/password/create.rs create mode 100644 packages/ciphernode/enclave/src/commands/password/delete.rs create mode 100644 packages/ciphernode/enclave/src/commands/password/overwrite.rs delete mode 100644 packages/ciphernode/enclave/src/commands/password/set.rs create mode 100644 packages/ciphernode/enclave/src/commands/wallet/mod.rs create mode 100644 packages/ciphernode/enclave/src/commands/wallet/set.rs create mode 100644 packages/ciphernode/enclave_node/src/datastore.rs create mode 100644 packages/ciphernode/router/src/keyshare_feature.rs diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index 58fbe22e..1b7f634f 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -1603,7 +1603,10 @@ dependencies = [ "aes-gcm", "anyhow", "argon2", + "async-trait", + "config", "rand", + "tokio", "zeroize", ] @@ -1683,6 +1686,19 @@ dependencies = [ "serde", ] +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + [[package]] name = "const-hex" version = "1.12.0" @@ -1973,6 +1989,19 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "shell-words", + "tempfile", + "thiserror", + "zeroize", +] + [[package]] name = "digest" version = "0.9.0" @@ -2122,12 +2151,19 @@ dependencies = [ "actix-rt", "alloy", "anyhow", + "cipher 0.1.0", "clap", "config", + "data", + "dialoguer", + "enclave-core", "enclave_node", + "router", + "rpassword", "tokio", "tracing", "tracing-subscriber", + "zeroize", ] [[package]] @@ -2158,6 +2194,7 @@ dependencies = [ "anyhow", "bfv", "bincode", + "cipher 0.1.0", "clap", "config", "data", @@ -2177,6 +2214,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "enum-as-inner" version = "0.6.0" @@ -2257,6 +2300,7 @@ dependencies = [ "alloy", "alloy-primitives 0.6.4", "anyhow", + "data", "enclave-core", "futures-util", "sortition", @@ -5201,6 +5245,7 @@ dependencies = [ "anyhow", "async-trait", "bincode", + "cipher 0.1.0", "data", "enclave-core", "fhe 0.1.0", @@ -5210,6 +5255,17 @@ dependencies = [ "tracing", ] +[[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 = "rtnetlink" version = "0.10.1" @@ -5226,6 +5282,16 @@ dependencies = [ "tokio", ] +[[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 = "ruint" version = "1.12.3" @@ -5594,6 +5660,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -5918,6 +5990,7 @@ dependencies = [ "base64 0.22.1", "bfv", "bincode", + "cipher 0.1.0", "clap", "data", "enclave-core", @@ -6327,6 +6400,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "unicode-xid" version = "0.2.5" diff --git a/packages/ciphernode/cipher/Cargo.toml b/packages/ciphernode/cipher/Cargo.toml index 9a2e91ac..4bbf0f0f 100644 --- a/packages/ciphernode/cipher/Cargo.toml +++ b/packages/ciphernode/cipher/Cargo.toml @@ -9,3 +9,6 @@ argon2 = { workspace = true, version = "=0.5.2" } rand = { workspace = true, version = "=0.8.5" } zeroize = { workspace = true, version = "=1.6.0" } anyhow = { workspace = true } +tokio = { workspace = true } +config = { path = "../config" } +async-trait = { workspace = true } diff --git a/packages/ciphernode/cipher/src/cipher.rs b/packages/ciphernode/cipher/src/cipher.rs index 826c38f5..ffe00bb5 100644 --- a/packages/ciphernode/cipher/src/cipher.rs +++ b/packages/ciphernode/cipher/src/cipher.rs @@ -1,13 +1,19 @@ +use std::path::Path; + use aes_gcm::{ aead::{Aead, KeyInit}, Aes256Gcm, Nonce, }; use anyhow::{anyhow, Result}; use argon2::{Algorithm, Argon2, Params, Version}; +use config::AppConfig; use rand::{rngs::OsRng, RngCore}; use zeroize::{Zeroize, Zeroizing}; -use crate::password_manager::{EnvPasswordManager, InMemPasswordManager, PasswordManager}; +use crate::{ + password_manager::{EnvPasswordManager, InMemPasswordManager, PasswordManager}, + FilePasswordManager, +}; // ARGON2 PARAMS const ARGON2_M_COST: u32 = 32 * 1024; // 32 MiB @@ -100,21 +106,29 @@ pub struct Cipher { } impl Cipher { - pub fn new

(pm: P) -> Result + pub async fn new

(pm: P) -> Result where P: PasswordManager, { // Get the key from the password manager when created - let key = pm.get_key()?; + let key = pm.get_key().await?; Ok(Self { key }) } - pub fn from_password(value: &str) -> Result { - Ok(Self::new(InMemPasswordManager::from_str(value))?) + pub async fn from_password(value: &str) -> Result { + Ok(Self::new(InMemPasswordManager::from_str(value)).await?) + } + + pub async fn from_env(value: &str) -> Result { + Ok(Self::new(EnvPasswordManager::new(value)?).await?) + } + + pub async fn from_file(value: impl AsRef) -> Result { + Ok(Self::new(FilePasswordManager::new(value)).await?) } - pub fn from_env(value: &str) -> Result { - Ok(Self::new(EnvPasswordManager::new(value)?)?) + pub async fn from_config(config: &AppConfig) -> Result { + Ok(Self::new(FilePasswordManager::new(config.key_file())).await?) } pub fn encrypt_data(&self, data: &mut Vec) -> Result> { @@ -139,13 +153,13 @@ mod tests { use anyhow::*; use std::time::Instant; - #[test] - fn test_basic_encryption_decryption() -> Result<()> { + #[tokio::test] + async fn test_basic_encryption_decryption() -> Result<()> { let data = b"Hello, world!"; let start = Instant::now(); - let cipher = Cipher::from_password("test_password")?; + let cipher = Cipher::from_password("test_password").await?; let encrypted = cipher.encrypt_data(&mut data.to_vec()).unwrap(); let encryption_time = start.elapsed(); @@ -161,9 +175,9 @@ mod tests { Ok(()) } - #[test] - fn test_empty_data() -> Result<()> { - let cipher = Cipher::from_password("test_password")?; + #[tokio::test] + async fn test_empty_data() -> Result<()> { + let cipher = Cipher::from_password("test_password").await?; let data = vec![]; let encrypted = cipher.encrypt_data(&mut data.clone()).unwrap(); @@ -173,9 +187,9 @@ mod tests { Ok(()) } - #[test] - fn test_large_data() -> Result<()> { - let cipher = Cipher::from_password("test_password")?; + #[tokio::test] + async fn test_large_data() -> Result<()> { + let cipher = Cipher::from_password("test_password").await?; let data = vec![1u8; 1024 * 1024]; // 1MB of data let start = Instant::now(); @@ -193,25 +207,25 @@ mod tests { Ok(()) } - #[test] - fn test_different_passwords() -> Result<()> { + #[tokio::test] + async fn test_different_passwords() -> Result<()> { // Encrypt with one password - let cipher = Cipher::from_password("password1")?; + let cipher = Cipher::from_password("password1").await?; let data = b"Secret message"; let encrypted = cipher.encrypt_data(&mut data.to_vec()).unwrap(); // Try to decrypt with a different password - let cipher = Cipher::from_password("password2")?; + let cipher = Cipher::from_password("password2").await?; let result = cipher.decrypt_data(&encrypted); assert!(result.is_err()); Ok(()) } - #[test] - fn test_binary_data() -> Result<()> { - let cipher = Cipher::from_password("test_password")?; + #[tokio::test] + async fn test_binary_data() -> Result<()> { + let cipher = Cipher::from_password("test_password").await?; let data = vec![0xFF, 0x00, 0xAA, 0x55, 0x12, 0xED]; @@ -222,9 +236,9 @@ mod tests { Ok(()) } - #[test] - fn test_unicode_data() -> Result<()> { - let cipher = Cipher::from_password("test_password")?; + #[tokio::test] + async fn test_unicode_data() -> Result<()> { + let cipher = Cipher::from_password("test_password").await?; let data = "Hello 🌍 ΠΏΡ€ΠΈΠ²Π΅Ρ‚ δΈ–η•Œ".as_bytes().to_vec(); let encrypted = cipher.encrypt_data(&mut data.clone()).unwrap(); @@ -234,17 +248,17 @@ mod tests { Ok(()) } - #[test] + #[tokio::test] #[should_panic(expected = "Invalid encrypted data length")] - fn test_invalid_encrypted_data() { - let cipher = Cipher::from_password("test_password").unwrap(); + async fn test_invalid_encrypted_data() { + let cipher = Cipher::from_password("test_password").await.unwrap(); let invalid_data = vec![0u8; 10]; // Too short to be valid encrypted data cipher.decrypt_data(&invalid_data).unwrap(); } - #[test] - fn test_multiple_encrypt_decrypt_cycles() { - let cipher = Cipher::from_password("test_password").unwrap(); + #[tokio::test] + async fn test_multiple_encrypt_decrypt_cycles() { + let cipher = Cipher::from_password("test_password").await.unwrap(); let original_data = b"Multiple encryption cycles test"; let mut data = original_data.to_vec(); @@ -256,9 +270,9 @@ mod tests { assert_eq!(original_data.to_vec(), data); } - #[test] - fn test_corrupted_data() { - let cipher = Cipher::from_password("test_password").unwrap(); + #[tokio::test] + async fn test_corrupted_data() { + let cipher = Cipher::from_password("test_password").await.unwrap(); let data = b"Test corrupted data"; let mut encrypted = cipher.encrypt_data(&mut data.to_vec()).unwrap(); diff --git a/packages/ciphernode/cipher/src/lib.rs b/packages/ciphernode/cipher/src/lib.rs index 00f94842..10736c6b 100644 --- a/packages/ciphernode/cipher/src/lib.rs +++ b/packages/ciphernode/cipher/src/lib.rs @@ -1,3 +1,4 @@ mod cipher; mod password_manager; pub use cipher::Cipher; +pub use password_manager::*; diff --git a/packages/ciphernode/cipher/src/password_manager.rs b/packages/ciphernode/cipher/src/password_manager.rs index ea1b98e6..80170ea2 100644 --- a/packages/ciphernode/cipher/src/password_manager.rs +++ b/packages/ciphernode/cipher/src/password_manager.rs @@ -1,4 +1,5 @@ use anyhow::*; +use async_trait::async_trait; use std::{ env, fs::{self, OpenOptions, Permissions}, @@ -8,10 +9,11 @@ use std::{ }; use zeroize::Zeroizing; +#[async_trait] pub trait PasswordManager { - fn get_key(&self) -> Result>>; - fn delete_key(&mut self) -> Result<()>; - fn set_key(&mut self, contents: Zeroizing>) -> Result<()>; + async fn get_key(&self) -> Result>>; + async fn delete_key(&mut self) -> Result<()>; + async fn set_key(&mut self, contents: Zeroizing>) -> Result<()>; } pub struct InMemPasswordManager(pub Option>>); @@ -35,37 +37,39 @@ impl EnvPasswordManager { } } +#[async_trait] impl PasswordManager for EnvPasswordManager { - fn get_key(&self) -> Result>> { + async fn get_key(&self) -> Result>> { if let Some(key) = self.0.clone() { return Ok(key); } Err(anyhow!("No key found")) } - fn set_key(&mut self, contents: Zeroizing>) -> Result<()> { + async fn set_key(&mut self, contents: Zeroizing>) -> Result<()> { self.0 = Some(contents); Ok(()) } - fn delete_key(&mut self) -> Result<()> { + async fn delete_key(&mut self) -> Result<()> { self.0 = None; Ok(()) } } +#[async_trait] impl PasswordManager for InMemPasswordManager { - fn get_key(&self) -> Result>> { + async fn get_key(&self) -> Result>> { if let Some(key) = self.0.clone() { return Ok(key); } Err(anyhow!("No key found")) } - fn set_key(&mut self, contents: Zeroizing>) -> Result<()> { + async fn set_key(&mut self, contents: Zeroizing>) -> Result<()> { self.0 = Some(contents); Ok(()) } - fn delete_key(&mut self) -> Result<()> { + async fn delete_key(&mut self) -> Result<()> { self.0 = None; Ok(()) } @@ -83,8 +87,9 @@ impl FilePasswordManager { } } +#[async_trait] impl PasswordManager for FilePasswordManager { - fn get_key(&self) -> Result>> { + async fn get_key(&self) -> Result>> { let path = &self.path; ensure_file_permissions(path, 0o400)?; @@ -94,7 +99,7 @@ impl PasswordManager for FilePasswordManager { Ok(Zeroizing::new(bytes)) } - fn delete_key(&mut self) -> Result<()> { + async fn delete_key(&mut self) -> Result<()> { let path = &self.path; ensure_file_permissions(path, 0o600)?; @@ -103,7 +108,7 @@ impl PasswordManager for FilePasswordManager { Ok(()) } - fn set_key(&mut self, contents: Zeroizing>) -> Result<()> { + async fn set_key(&mut self, contents: Zeroizing>) -> Result<()> { let path = &self.path; // Check if file exists diff --git a/packages/ciphernode/data/src/sled_store.rs b/packages/ciphernode/data/src/sled_store.rs index b140be9d..8869049e 100644 --- a/packages/ciphernode/data/src/sled_store.rs +++ b/packages/ciphernode/data/src/sled_store.rs @@ -1,11 +1,13 @@ +use std::path::{Path, PathBuf}; + use crate::{Get, Insert}; use actix::{Actor, Addr, Handler}; use anyhow::{Context, Result}; use enclave_core::{BusError, EnclaveErrorType, EventBus}; -use sled::{Db, IVec}; +use sled::Db; pub struct SledStore { - db: Db, + db: SledDb, bus: Addr, } @@ -14,25 +16,27 @@ impl Actor for SledStore { } impl SledStore { - pub fn new(bus: &Addr, path: &str) -> Result { - let db = sled::open(path) - .with_context(|| format!("Could not open database at path '{}'", path))?; + pub fn new(bus: &Addr, path: &PathBuf) -> Result { + let db = SledDb::new(path)?; Ok(Self { db, bus: bus.clone(), }) } + + pub fn from_db(db: SledDb) -> Result { + Ok(Self { + db, + bus: EventBus::new(false).start(), + }) + } } impl Handler for SledStore { type Result = (); fn handle(&mut self, event: Insert, _: &mut Self::Context) -> Self::Result { - match self - .db - .insert(event.key(), event.value().to_vec()) - .context("Could not insert data into db") - { + match self.db.insert(event) { Err(err) => self.bus.err(EnclaveErrorType::Data, err), _ => (), } @@ -43,15 +47,8 @@ impl Handler for SledStore { type Result = Option>; fn handle(&mut self, event: Get, _: &mut Self::Context) -> Self::Result { - let key = event.key(); - let str_key = String::from_utf8_lossy(&key).into_owned(); - let res: Result> = self - .db - .get(key) - .context(format!("Failed to fetch {}", str_key)); - - return match res { - Ok(value) => value.map(|v| v.to_vec()), + return match self.db.get(event) { + Ok(v) => v, Err(err) => { self.bus.err(EnclaveErrorType::Data, err); None @@ -59,3 +56,34 @@ impl Handler for SledStore { }; } } + +pub struct SledDb { + db: Db, +} + +impl SledDb { + pub fn new(path: &PathBuf) -> Result { + let db = sled::open(path) + .with_context(|| format!("Could not open database at path '{}'", path.to_string_lossy()))?; + Ok(Self { db }) + } + + pub fn insert(&mut self, msg: Insert) -> Result<()> { + self.db + .insert(msg.key(), msg.value().to_vec()) + .context("Could not insert data into db")?; + + Ok(()) + } + + pub fn get(&mut self, event: Get) -> Result>> { + let key = event.key(); + let str_key = String::from_utf8_lossy(&key).into_owned(); + let res = self + .db + .get(key) + .context(format!("Failed to fetch {}", str_key))?; + + Ok(res.map(|v| v.to_vec())) + } +} diff --git a/packages/ciphernode/enclave/Cargo.toml b/packages/ciphernode/enclave/Cargo.toml index dad96ac4..7703f586 100644 --- a/packages/ciphernode/enclave/Cargo.toml +++ b/packages/ciphernode/enclave/Cargo.toml @@ -12,9 +12,16 @@ actix = { workspace = true } actix-rt = { workspace = true } alloy = { workspace = true } anyhow = { workspace = true } +cipher = { path = "../cipher" } clap = { workspace = true } +config = { path = "../config" } +enclave-core = { path = "../core" } +data = { path = "../data" } +router = { path = "../router" } +dialoguer = "0.11.0" enclave_node = { path = "../enclave_node" } +rpassword = "7.3.1" tokio = { workspace = true } -tracing-subscriber = { workspace = true } tracing = { workspace = true } -config = { path = "../config" } +tracing-subscriber = { workspace = true } +zeroize = { workspace = true } diff --git a/packages/ciphernode/enclave/src/commands/aggregator/mod.rs b/packages/ciphernode/enclave/src/commands/aggregator/mod.rs index bfa97c04..58a12474 100644 --- a/packages/ciphernode/enclave/src/commands/aggregator/mod.rs +++ b/packages/ciphernode/enclave/src/commands/aggregator/mod.rs @@ -1,6 +1,7 @@ mod start; use anyhow::*; use clap::Subcommand; +use config::AppConfig; #[derive(Subcommand)] pub enum AggregatorCommands { @@ -16,14 +17,14 @@ pub enum AggregatorCommands { }, } -pub async fn execute(command: AggregatorCommands, config_path: Option<&str>) -> Result<()> { +pub async fn execute(command: AggregatorCommands, config: AppConfig) -> Result<()> { match command { AggregatorCommands::Start { pubkey_write_path, plaintext_write_path, } => { start::execute( - config_path, + config, pubkey_write_path.as_deref(), plaintext_write_path.as_deref(), ) diff --git a/packages/ciphernode/enclave/src/commands/aggregator/start.rs b/packages/ciphernode/enclave/src/commands/aggregator/start.rs index da451b72..b4b130d9 100644 --- a/packages/ciphernode/enclave/src/commands/aggregator/start.rs +++ b/packages/ciphernode/enclave/src/commands/aggregator/start.rs @@ -1,12 +1,12 @@ use anyhow::*; -use config::load_config; -use enclave_node::{listen_for_shutdown, MainAggregator}; +use config::AppConfig; +use enclave_node::{listen_for_shutdown, setup_aggregator}; use tracing::info; use crate::owo; pub async fn execute( - config_path:Option<&str>, + config: AppConfig, pubkey_write_path: Option<&str>, plaintext_write_path: Option<&str>, ) -> Result<()> { @@ -14,13 +14,7 @@ pub async fn execute( info!("LAUNCHING AGGREGATOR"); - let conf = load_config(config_path)?; - let (bus, handle) = MainAggregator::attach( - conf, - pubkey_write_path, - plaintext_write_path, - ) - .await?; + let (bus, handle) = setup_aggregator(config, pubkey_write_path, plaintext_write_path).await?; tokio::spawn(listen_for_shutdown(bus.into(), handle)); diff --git a/packages/ciphernode/enclave/src/commands/mod.rs b/packages/ciphernode/enclave/src/commands/mod.rs index f30f4d98..6bd68378 100644 --- a/packages/ciphernode/enclave/src/commands/mod.rs +++ b/packages/ciphernode/enclave/src/commands/mod.rs @@ -1,9 +1,11 @@ pub mod start; pub mod password; pub mod aggregator; +pub mod wallet; use aggregator::AggregatorCommands; use clap::Subcommand; +use wallet::WalletCommands; use self::password::PasswordCommands; #[derive(Subcommand)] @@ -25,5 +27,11 @@ pub enum Commands { #[command(subcommand)] command: PasswordCommands, }, + + /// Wallet management commands + Wallet { + #[command(subcommand)] + command: WalletCommands + } } diff --git a/packages/ciphernode/enclave/src/commands/password/create.rs b/packages/ciphernode/enclave/src/commands/password/create.rs new file mode 100644 index 00000000..9630f933 --- /dev/null +++ b/packages/ciphernode/enclave/src/commands/password/create.rs @@ -0,0 +1,47 @@ +use anyhow::Result; +use cipher::{FilePasswordManager, PasswordManager}; +use config::AppConfig; +use rpassword::prompt_password; +use zeroize::{Zeroize, Zeroizing}; + +fn get_zeroizing_pw_vec(input: Option) -> Result>> { + if let Some(mut pw_str) = input { + let pw = Zeroizing::new(pw_str.trim().as_bytes().to_owned()); + pw_str.zeroize(); + return Ok(pw); + } + + // First password entry + let mut pw_str = prompt_password("\n\nPlease enter a new password: ")?; + // Second password entry for confirmation + let mut confirm_pw_str = prompt_password("Please confirm your password: ")?; + + // Check if passwords match + if pw_str.trim() != confirm_pw_str.trim() { + // Clean up sensitive data + pw_str.zeroize(); + confirm_pw_str.zeroize(); + return Err(anyhow::anyhow!("Passwords do not match")); + } + + let pw = Zeroizing::new(pw_str.trim().as_bytes().to_owned()); + + // Clean up sensitive data + pw_str.zeroize(); + confirm_pw_str.zeroize(); + + Ok(pw) +} + +pub async fn execute(config: &AppConfig, input: Option) -> Result<()> { + let key_file = config.key_file(); + let mut pm = FilePasswordManager::new(key_file); + let pw = get_zeroizing_pw_vec(input)?; + + match pm.set_key(pw).await { + Ok(_) => println!("Password sucessfully set."), + Err(err) => println!("{}", err), + }; + + Ok(()) +} diff --git a/packages/ciphernode/enclave/src/commands/password/delete.rs b/packages/ciphernode/enclave/src/commands/password/delete.rs new file mode 100644 index 00000000..bffdf6a0 --- /dev/null +++ b/packages/ciphernode/enclave/src/commands/password/delete.rs @@ -0,0 +1,57 @@ +use anyhow::*; +use cipher::{FilePasswordManager, PasswordManager}; +use config::AppConfig; +use dialoguer::{theme::ColorfulTheme, Confirm}; +use rpassword::prompt_password; +use zeroize::Zeroize; + +pub enum DeleteMode { + Delete, + Overwrite, +} + +impl DeleteMode { + fn to_string(&self) -> String { + match self { + DeleteMode::Delete => "delete".to_owned(), + DeleteMode::Overwrite => "overwrite".to_owned(), + } + } +} + +pub async fn prompt_delete(config: &AppConfig, delete_mode: DeleteMode) -> Result { + let mode = delete_mode.to_string(); + let proceed = Confirm::with_theme(&ColorfulTheme::default()) + .with_prompt(format!( + "Are you sure you want to {mode} the key? This action cannot be undone." + )) + .default(false) + .interact()?; + + if proceed { + let mut pw_str = prompt_password("\n\nPlease enter the current password: ")?; + let key_file = config.key_file(); + let mut pm = FilePasswordManager::new(key_file); + let mut cur_pw = pm.get_key().await?; + + if pw_str != String::from_utf8_lossy(&cur_pw) { + // Clean up sensitive data + pw_str.zeroize(); + cur_pw.zeroize(); + return Err(anyhow::anyhow!("Incorrect password")); + } + pm.delete_key().await?; + } else { + return Ok(false); + } + Ok(true) +} + +pub async fn execute(config: &AppConfig) -> Result<()> { + if prompt_delete(config, DeleteMode::Delete).await? { + println!("Key successfully deleted."); + } else { + println!("Operation cancelled."); + } + Ok(()) +} diff --git a/packages/ciphernode/enclave/src/commands/password/mod.rs b/packages/ciphernode/enclave/src/commands/password/mod.rs index 1caa5906..aa85ec9a 100644 --- a/packages/ciphernode/enclave/src/commands/password/mod.rs +++ b/packages/ciphernode/enclave/src/commands/password/mod.rs @@ -1,24 +1,35 @@ -mod set; +mod delete; +mod overwrite; +mod create; use anyhow::*; use clap::Subcommand; +use config::AppConfig; #[derive(Subcommand)] pub enum PasswordCommands { - /// Set a new password - Set { + /// Create a new password + Create { /// The new password #[arg(short, long)] - value: String, + password: Option, }, -} -pub async fn execute(command: PasswordCommands, config_path: Option<&str>) -> Result<()> { - if let Some(path) = config_path { - println!("Using config from: {}", path); - } + /// Delete the current password + Delete, + + /// Overwrite the current password + Overwrite { + /// The new password + #[arg(short, long)] + password: Option, + }, +} +pub async fn execute(command: PasswordCommands, config: AppConfig) -> Result<()> { match command { - PasswordCommands::Set { value } => set::execute(value) + PasswordCommands::Create { password } => create::execute(&config, password).await?, + PasswordCommands::Delete => delete::execute(&config).await?, + PasswordCommands::Overwrite { password } => overwrite::execute(&config, password).await?, }; Ok(()) diff --git a/packages/ciphernode/enclave/src/commands/password/overwrite.rs b/packages/ciphernode/enclave/src/commands/password/overwrite.rs new file mode 100644 index 00000000..5c868a2b --- /dev/null +++ b/packages/ciphernode/enclave/src/commands/password/overwrite.rs @@ -0,0 +1,11 @@ +use super::delete::{prompt_delete, DeleteMode}; +use super::create::execute as set_password; +use anyhow::Result; +use config::AppConfig; + +pub async fn execute(config: &AppConfig, input: Option) -> Result<()> { + if prompt_delete(config, DeleteMode::Overwrite).await? { + set_password(config, input).await?; + } + Ok(()) +} diff --git a/packages/ciphernode/enclave/src/commands/password/set.rs b/packages/ciphernode/enclave/src/commands/password/set.rs deleted file mode 100644 index d5676d4b..00000000 --- a/packages/ciphernode/enclave/src/commands/password/set.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn execute(value:String) { - println!("password set to {}", value); -} diff --git a/packages/ciphernode/enclave/src/commands/start.rs b/packages/ciphernode/enclave/src/commands/start.rs index 2e1ea162..a41252a1 100644 --- a/packages/ciphernode/enclave/src/commands/start.rs +++ b/packages/ciphernode/enclave/src/commands/start.rs @@ -1,20 +1,19 @@ use alloy::primitives::Address; use anyhow::{Context, Result}; -use config::load_config; -use enclave_node::{listen_for_shutdown, MainCiphernode}; +use config::AppConfig; +use enclave_node::{listen_for_shutdown, setup_ciphernode}; use tracing::info; use crate::owo; -pub async fn execute(config_path:Option<&str>, address:&str) -> Result<()> { - +pub async fn execute(config: AppConfig, address: &str) -> Result<()> { owo(); let address = Address::parse_checksummed(&address, None).context("Invalid address")?; + info!("LAUNCHING CIPHERNODE: ({})", address); - let config = load_config(config_path)?; - let (bus, handle) = MainCiphernode::attach(config, address).await?; + let (bus, handle) = setup_ciphernode(config, address).await?; tokio::spawn(listen_for_shutdown(bus.into(), handle)); @@ -22,4 +21,3 @@ pub async fn execute(config_path:Option<&str>, address:&str) -> Result<()> { Ok(()) } - diff --git a/packages/ciphernode/enclave/src/commands/wallet/mod.rs b/packages/ciphernode/enclave/src/commands/wallet/mod.rs new file mode 100644 index 00000000..519ad329 --- /dev/null +++ b/packages/ciphernode/enclave/src/commands/wallet/mod.rs @@ -0,0 +1,24 @@ +mod set; +use anyhow::*; +use clap::Subcommand; +use config::AppConfig; + +#[derive(Subcommand)] +pub enum WalletCommands { + /// Set a new Wallet Private Key + Set { + /// The new private key + #[arg(long = "private-key")] + private_key: String, + }, + // /// Delete the current wallet + // Delete, +} + +pub async fn execute(command: WalletCommands, config: AppConfig) -> Result<()> { + match command { + WalletCommands::Set { private_key } => set::execute(&config, private_key).await?, + }; + + Ok(()) +} diff --git a/packages/ciphernode/enclave/src/commands/wallet/set.rs b/packages/ciphernode/enclave/src/commands/wallet/set.rs new file mode 100644 index 00000000..08bf33a9 --- /dev/null +++ b/packages/ciphernode/enclave/src/commands/wallet/set.rs @@ -0,0 +1,23 @@ +use actix::Actor; +use anyhow::Result; +use cipher::Cipher; +use config::AppConfig; +use enclave_core::{EventBus, GetErrors}; +use enclave_node::get_repositories; + +pub async fn execute(config: &AppConfig, input: String) -> Result<()> { + let cipher = Cipher::from_config(config).await?; + let mut vec_input = input.as_bytes().to_vec(); + let encrypted = cipher.encrypt_data(&mut vec_input)?; + let bus = EventBus::new(true).start(); + let repositories = get_repositories(&config, &bus)?; + repositories.eth_private_key().write(&encrypted); + let errors = bus.send(GetErrors).await?; + for error in errors.iter() { + println!("{error}"); + } + if errors.len() == 0 { + println!("WalletKey key has been successfully encrypted."); + } + Ok(()) +} diff --git a/packages/ciphernode/enclave/src/main.rs b/packages/ciphernode/enclave/src/main.rs index b094551f..d6849203 100644 --- a/packages/ciphernode/enclave/src/main.rs +++ b/packages/ciphernode/enclave/src/main.rs @@ -1,7 +1,7 @@ use anyhow::Result; use clap::Parser; -use commands::{aggregator, password, start, Commands}; -pub mod cli; +use commands::{aggregator, password, start, wallet, Commands}; +use config::load_config; pub mod commands; const OWO: &str = r#" @@ -40,15 +40,19 @@ impl Cli { pub async fn execute(self) -> Result<()> { let config_path = self.config.as_deref(); + let config = load_config(config_path)?; + match self.command { - Commands::Start { address } => start::execute(config_path, &address).await?, - Commands::Password { command } => password::execute(command, config_path).await?, - Commands::Aggregator { command } => aggregator::execute(command, config_path).await?, + Commands::Start { address } => start::execute(config, &address).await?, + Commands::Password { command } => password::execute(command, config).await?, + Commands::Aggregator { command } => aggregator::execute(command, config).await?, + Commands::Wallet { command } => wallet::execute(command, config).await?, } Ok(()) } } + #[actix_rt::main] pub async fn main() { tracing_subscriber::fmt::init(); @@ -57,6 +61,9 @@ pub async fn main() { match cli.execute().await { Ok(_) => (), - Err(_) => println!("There was a problem running. Goodbye"), + Err(err) => { + println!("{}", err); + println!("There was a problem running. Goodbye") + } } } diff --git a/packages/ciphernode/enclave_node/Cargo.toml b/packages/ciphernode/enclave_node/Cargo.toml index 4f378644..efabffa1 100644 --- a/packages/ciphernode/enclave_node/Cargo.toml +++ b/packages/ciphernode/enclave_node/Cargo.toml @@ -18,6 +18,7 @@ bfv = { path = "../bfv" } bincode = { workspace = true } config = { path = "../config" } clap = { workspace = true } +cipher = { path = "../cipher" } data = { path = "../data" } enclave-core = { path = "../core" } evm = { path = "../evm" } diff --git a/packages/ciphernode/enclave_node/src/aggregator.rs b/packages/ciphernode/enclave_node/src/aggregator.rs index 28a182e1..273a671e 100644 --- a/packages/ciphernode/enclave_node/src/aggregator.rs +++ b/packages/ciphernode/enclave_node/src/aggregator.rs @@ -1,4 +1,4 @@ -use actix::{Actor, Addr, Context}; +use actix::{Actor, Addr}; use anyhow::Result; use config::AppConfig; use data::{DataStore, InMemStore, SledStore}; @@ -9,7 +9,7 @@ use evm::{ use logger::SimpleLogger; use p2p::P2p; use rand::SeedableRng; -use rand_chacha::rand_core::OsRng; +use rand_chacha::{rand_core::OsRng, ChaCha20Rng}; use router::{ E3RequestRouter, FheFeature, PlaintextAggregatorFeature, PublicKeyAggregatorFeature, RepositoriesFactory, @@ -19,89 +19,50 @@ use std::sync::{Arc, Mutex}; use test_helpers::{PlaintextWriter, PublicKeyWriter}; use tokio::task::JoinHandle; +use crate::setup_datastore; -/// Main Ciphernode Actor -/// Suprvises all children -// TODO: add supervision logic -pub struct MainAggregator { - e3_manager: Addr, - bus: Addr, - sortition: Addr, - p2p: Addr, -} - -impl MainAggregator { - pub fn new( - bus: &Addr, - sortition: Addr, - p2p: Addr, - e3_manager: Addr, - ) -> Self { - Self { - e3_manager, - bus: bus.clone(), - sortition, - p2p, - } +pub async fn setup_aggregator( + config: AppConfig, + pubkey_write_path: Option<&str>, + plaintext_write_path: Option<&str>, +) -> Result<(Addr, JoinHandle<()>)> { + let bus = EventBus::new(true).start(); + let rng = Arc::new(Mutex::new( + ChaCha20Rng::from_rng(OsRng).expect("Failed to create RNG"), + )); + let store = setup_datastore(&config, &bus)?; + let repositories = store.repositories(); + let sortition = Sortition::attach(&bus, repositories.sortition()); + let signer = pull_eth_signer_from_env("PRIVATE_KEY").await?; + for chain in config + .chains() + .iter() + .filter(|chain| chain.enabled.unwrap_or(true)) + { + let rpc_url = &chain.rpc_url; + EnclaveSol::attach(&bus, rpc_url, &chain.contracts.enclave, &signer).await?; + RegistryFilterSol::attach(&bus, rpc_url, &chain.contracts.filter_registry, &signer).await?; + CiphernodeRegistrySol::attach(&bus, rpc_url, &chain.contracts.ciphernode_registry).await?; } - pub async fn attach( - config: AppConfig, - pubkey_write_path: Option<&str>, - plaintext_write_path: Option<&str>, - ) -> Result<(Addr, JoinHandle<()>)> { - let bus = EventBus::new(true).start(); - let rng = Arc::new(Mutex::new( - rand_chacha::ChaCha20Rng::from_rng(OsRng).expect("Failed to create RNG"), - )); - - let store: DataStore = if !config.use_in_mem_store() { - (&SledStore::new(&bus, &config.db_file().to_string_lossy())?.start()).into() - } else { - (&InMemStore::new(true).start()).into() - }; - - let repositories = store.repositories(); - let sortition = Sortition::attach(&bus, repositories.sortition()); - let signer = pull_eth_signer_from_env("PRIVATE_KEY").await?; - for chain in config - .chains() - .iter() - .filter(|chain| chain.enabled.unwrap_or(true)) - { - let rpc_url = &chain.rpc_url; - EnclaveSol::attach(&bus, rpc_url, &chain.contracts.enclave, &signer).await?; - RegistryFilterSol::attach(&bus, rpc_url, &chain.contracts.filter_registry, &signer) - .await?; - CiphernodeRegistrySol::attach(&bus, rpc_url, &chain.contracts.ciphernode_registry) - .await?; - } - - let e3_manager = E3RequestRouter::builder(&bus, store) - .add_feature(FheFeature::create(&bus, &rng)) - .add_feature(PublicKeyAggregatorFeature::create(&bus, &sortition)) - .add_feature(PlaintextAggregatorFeature::create(&bus, &sortition)) - .build() - .await?; + E3RequestRouter::builder(&bus, store) + .add_feature(FheFeature::create(&bus, &rng)) + .add_feature(PublicKeyAggregatorFeature::create(&bus, &sortition)) + .add_feature(PlaintextAggregatorFeature::create(&bus, &sortition)) + .build() + .await?; - let (p2p_addr, join_handle) = - P2p::spawn_libp2p(bus.clone()).expect("Failed to setup libp2p"); + let (_, join_handle) = P2p::spawn_libp2p(bus.clone()).expect("Failed to setup libp2p"); - if let Some(path) = pubkey_write_path { - PublicKeyWriter::attach(path, bus.clone()); - } - - if let Some(path) = plaintext_write_path { - PlaintextWriter::attach(path, bus.clone()); - } - - SimpleLogger::attach("AGG", bus.clone()); + if let Some(path) = pubkey_write_path { + PublicKeyWriter::attach(path, bus.clone()); + } - MainAggregator::new(&bus, sortition, p2p_addr, e3_manager).start(); - Ok((bus, join_handle)) + if let Some(path) = plaintext_write_path { + PlaintextWriter::attach(path, bus.clone()); } -} -impl Actor for MainAggregator { - type Context = Context; + SimpleLogger::attach("AGG", bus.clone()); + + Ok((bus, join_handle)) } diff --git a/packages/ciphernode/enclave_node/src/ciphernode.rs b/packages/ciphernode/enclave_node/src/ciphernode.rs index 2c99a718..73660e97 100644 --- a/packages/ciphernode/enclave_node/src/ciphernode.rs +++ b/packages/ciphernode/enclave_node/src/ciphernode.rs @@ -1,6 +1,7 @@ -use actix::{Actor, Addr, Context}; +use actix::{Actor, Addr}; use alloy::primitives::Address; use anyhow::Result; +use cipher::Cipher; use config::AppConfig; use data::{DataStore, InMemStore, SledStore}; use enclave_core::EventBus; @@ -16,97 +17,45 @@ use sortition::Sortition; use std::sync::{Arc, Mutex}; use tokio::task::JoinHandle; -/// Main Ciphernode Actor -/// Suprvises all children -// TODO: add supervision logic -pub struct MainCiphernode { - addr: Address, - bus: Addr, - data: DataStore, - sortition: Addr, - selector: Addr, - e3_manager: Addr, - p2p: Addr, -} - -impl MainCiphernode { - pub fn new( - addr: Address, - bus: Addr, - data: DataStore, - sortition: Addr, - selector: Addr, - p2p: Addr, - e3_manager: Addr, - ) -> Self { - Self { - addr, - bus, - data, - sortition, - selector, - e3_manager, - p2p, - } +use crate::setup_datastore; + +pub async fn setup_ciphernode( + config: AppConfig, + address: Address, +) -> Result<(Addr, JoinHandle<()>)> { + let rng = Arc::new(Mutex::new( + rand_chacha::ChaCha20Rng::from_rng(OsRng).expect("Failed to create RNG"), + )); + let bus = EventBus::new(true).start(); + let cipher = Arc::new(Cipher::from_file(config.key_file()).await?); + let store = setup_datastore(&config, &bus)?; + + let repositories = store.repositories(); + + let sortition = Sortition::attach(&bus, repositories.sortition()); + CiphernodeSelector::attach(&bus, &sortition, &address.to_string()); + + for chain in config + .chains() + .iter() + .filter(|chain| chain.enabled.unwrap_or(true)) + { + let rpc_url = &chain.rpc_url; + + EnclaveSolReader::attach(&bus, rpc_url, &chain.contracts.enclave).await?; + CiphernodeRegistrySol::attach(&bus, rpc_url, &chain.contracts.ciphernode_registry).await?; } - pub async fn attach( - config: AppConfig, - address: Address, - ) -> Result<(Addr, JoinHandle<()>)> { - let rng = Arc::new(Mutex::new( - rand_chacha::ChaCha20Rng::from_rng(OsRng).expect("Failed to create RNG"), - )); - let bus = EventBus::new(true).start(); - - let store: DataStore = if !config.use_in_mem_store() { - (&SledStore::new(&bus, &config.db_file().to_string_lossy())?.start()).into() - } else { - (&InMemStore::new(true).start()).into() - }; - - let repositories = store.repositories(); - - let sortition = Sortition::attach(&bus, repositories.sortition()); - let selector = CiphernodeSelector::attach(&bus, &sortition, &address.to_string()); + E3RequestRouter::builder(&bus, store.clone()) + .add_feature(FheFeature::create(&bus, &rng)) + .add_feature(KeyshareFeature::create(&bus, &address.to_string(), &cipher)) + .build() + .await?; - for chain in config - .chains() - .iter() - .filter(|chain| chain.enabled.unwrap_or(true)) - { - let rpc_url = &chain.rpc_url; + let (_, join_handle) = P2p::spawn_libp2p(bus.clone()).expect("Failed to setup libp2p"); - EnclaveSolReader::attach(&bus, rpc_url, &chain.contracts.enclave).await?; - CiphernodeRegistrySol::attach(&bus, rpc_url, &chain.contracts.ciphernode_registry) - .await?; - } - - let e3_manager = E3RequestRouter::builder(&bus, store.clone()) - .add_feature(FheFeature::create(&bus, &rng)) - .add_feature(KeyshareFeature::create(&bus, &address.to_string())) - .build() - .await?; - - let (p2p_addr, join_handle) = - P2p::spawn_libp2p(bus.clone()).expect("Failed to setup libp2p"); - - let nm = format!("CIPHER({})", &address.to_string()[0..5]); - SimpleLogger::attach(&nm, bus.clone()); - MainCiphernode::new( - address, - bus.clone(), - store, - sortition, - selector, - p2p_addr, - e3_manager, - ) - .start(); - Ok((bus, join_handle)) - } -} + let nm = format!("CIPHER({})", &address.to_string()[0..5]); + SimpleLogger::attach(&nm, bus.clone()); -impl Actor for MainCiphernode { - type Context = Context; + Ok((bus, join_handle)) } diff --git a/packages/ciphernode/enclave_node/src/datastore.rs b/packages/ciphernode/enclave_node/src/datastore.rs new file mode 100644 index 00000000..3617a338 --- /dev/null +++ b/packages/ciphernode/enclave_node/src/datastore.rs @@ -0,0 +1,21 @@ +use actix::{Actor, Addr}; +use anyhow::Result; +use config::AppConfig; +use data::{DataStore, InMemStore, SledStore}; +use enclave_core::EventBus; +use router::{Repositories, RepositoriesFactory}; + +pub fn setup_datastore(config: &AppConfig, bus: &Addr) -> Result { + let store: DataStore = if !config.use_in_mem_store() { + (&SledStore::new(&bus, &config.db_file())?.start()).into() + } else { + (&InMemStore::new(true).start()).into() + }; + Ok(store) +} + +pub fn get_repositories(config: &AppConfig, bus:&Addr) -> Result { + let store = setup_datastore(config, &bus)?; + Ok(store.repositories()) +} + diff --git a/packages/ciphernode/enclave_node/src/lib.rs b/packages/ciphernode/enclave_node/src/lib.rs index 69b04e25..40c439e0 100644 --- a/packages/ciphernode/enclave_node/src/lib.rs +++ b/packages/ciphernode/enclave_node/src/lib.rs @@ -1,7 +1,9 @@ mod aggregator; mod ciphernode; mod shutdown; +mod datastore; pub use aggregator::*; pub use ciphernode::*; pub use shutdown::*; +pub use datastore::*; diff --git a/packages/ciphernode/evm/Cargo.toml b/packages/ciphernode/evm/Cargo.toml index 8e0164b0..994f3e60 100644 --- a/packages/ciphernode/evm/Cargo.toml +++ b/packages/ciphernode/evm/Cargo.toml @@ -9,6 +9,7 @@ alloy = { workspace = true } alloy-primitives = { workspace = true } anyhow = { workspace = true } enclave-core = { path = "../core" } +data = { path = "../data" } futures-util = { workspace = true } sortition = { path = "../sortition" } tokio = { workspace = true } diff --git a/packages/ciphernode/evm/src/enclave_sol_writer.rs b/packages/ciphernode/evm/src/enclave_sol_writer.rs index 1c99277c..689c1e6d 100644 --- a/packages/ciphernode/evm/src/enclave_sol_writer.rs +++ b/packages/ciphernode/evm/src/enclave_sol_writer.rs @@ -12,6 +12,7 @@ use alloy::{ rpc::types::TransactionReceipt, }; use anyhow::Result; +use data::DataStore; use enclave_core::Shutdown; use enclave_core::{BusError, E3id, EnclaveErrorType, PlaintextAggregated, Subscribe}; use enclave_core::{EnclaveEvent, EventBus}; @@ -37,6 +38,7 @@ impl EnclaveSolWriter { contract_address: Address, signer: &Arc, ) -> Result { + Ok(Self { provider: create_provider_with_signer(&ensure_http_rpc(rpc_url), signer).await?, contract_address, diff --git a/packages/ciphernode/keyshare/src/keyshare.rs b/packages/ciphernode/keyshare/src/keyshare.rs index ccb395d1..c170763f 100644 --- a/packages/ciphernode/keyshare/src/keyshare.rs +++ b/packages/ciphernode/keyshare/src/keyshare.rs @@ -18,6 +18,7 @@ pub struct Keyshare { bus: Addr, secret: Option>, address: String, + cipher: Arc, } impl Actor for Keyshare { @@ -29,6 +30,7 @@ pub struct KeyshareParams { pub store: Repository, pub fhe: Arc, pub address: String, + pub cipher: Arc, } #[derive(Serialize, Deserialize)] @@ -44,11 +46,12 @@ impl Keyshare { store: params.store, secret: None, address: params.address, + cipher: params.cipher, } } fn set_secret(&mut self, mut data: Vec) -> Result<()> { - let encrypted = Cipher::from_env("CIPHERNODE_SECRET")?.encrypt_data(&mut data)?; + let encrypted = self.cipher.encrypt_data(&mut data)?; self.secret = Some(encrypted); @@ -61,7 +64,7 @@ impl Keyshare { .clone() .ok_or(anyhow!("No secret share available on Keyshare"))?; - let decrypted = Cipher::from_env("CIPHERNODE_SECRET")?.decrypt_data(&encrypted)?; + let decrypted = self.cipher.decrypt_data(&encrypted)?; Ok(decrypted) } @@ -93,6 +96,7 @@ impl FromSnapshotWithParams for Keyshare { store: params.store, secret: snapshot.secret, address: params.address, + cipher: params.cipher, }) } } diff --git a/packages/ciphernode/router/Cargo.toml b/packages/ciphernode/router/Cargo.toml index b9eba0bf..ce85d444 100644 --- a/packages/ciphernode/router/Cargo.toml +++ b/packages/ciphernode/router/Cargo.toml @@ -13,6 +13,7 @@ keyshare = { path = "../keyshare" } aggregator = { path = "../aggregator" } anyhow = { workspace = true } serde = { workspace = true } +cipher = { path = "../cipher" } bincode = { workspace = true } async-trait = { workspace = true } tracing = { workspace = true } diff --git a/packages/ciphernode/router/src/hooks.rs b/packages/ciphernode/router/src/hooks.rs index 4f36d799..3ba881c9 100644 --- a/packages/ciphernode/router/src/hooks.rs +++ b/packages/ciphernode/router/src/hooks.rs @@ -6,13 +6,13 @@ use aggregator::{ }; use anyhow::{anyhow, Result}; use async_trait::async_trait; +use cipher::Cipher; use data::{FromSnapshotWithParams, Snapshot}; use enclave_core::{BusError, E3Requested, EnclaveErrorType, EnclaveEvent, EventBus}; use fhe::{Fhe, SharedRng}; use keyshare::{Keyshare, KeyshareParams}; use sortition::Sortition; -use std::sync::{mpsc::Receiver, Arc}; -use tracing::error; +use std::sync::Arc; /// TODO: move these to each package with access on MyStruct::launcher() pub struct FheFeature { @@ -87,13 +87,15 @@ impl E3Feature for FheFeature { pub struct KeyshareFeature { bus: Addr, address: String, + cipher: Arc } impl KeyshareFeature { - pub fn create(bus: &Addr, address: &str) -> Box { + pub fn create(bus: &Addr, address: &str, cipher:&Arc) -> Box { Box::new(Self { bus: bus.clone(), address: address.to_owned(), + cipher: cipher.to_owned() }) } } @@ -119,12 +121,13 @@ impl E3Feature for KeyshareFeature { let e3_id = data.clone().e3_id; - let _ = ctx.set_keyshare( + ctx.set_keyshare( Keyshare::new(KeyshareParams { bus: self.bus.clone(), store: ctx.repositories().keyshare(&e3_id), fhe: fhe.clone(), address: self.address.clone(), + cipher: self.cipher.clone() }) .start(), ); @@ -163,6 +166,7 @@ impl E3Feature for KeyshareFeature { bus: self.bus.clone(), store, address: self.address.clone(), + cipher: self.cipher.clone() }, snap, ) diff --git a/packages/ciphernode/router/src/keyshare_feature.rs b/packages/ciphernode/router/src/keyshare_feature.rs new file mode 100644 index 00000000..e69de29b diff --git a/packages/ciphernode/router/src/repositories.rs b/packages/ciphernode/router/src/repositories.rs index 4a155da8..0d5f703a 100644 --- a/packages/ciphernode/router/src/repositories.rs +++ b/packages/ciphernode/router/src/repositories.rs @@ -68,6 +68,10 @@ impl Repositories { pub fn sortition(&self) -> Repository { Repository::new(self.store.scope(format!("//sortition"))) } + + pub fn eth_private_key(&self) -> Repository> { + Repository::new(self.store.scope(format!("//eth_private_key"))) + } } pub trait RepositoriesFactory { diff --git a/packages/ciphernode/tests/Cargo.toml b/packages/ciphernode/tests/Cargo.toml index cd179984..34c59b5a 100644 --- a/packages/ciphernode/tests/Cargo.toml +++ b/packages/ciphernode/tests/Cargo.toml @@ -11,6 +11,7 @@ enclave-core = { path = "../core" } evm = { path = "../evm" } logger = { path = "../logger" } fhe = { path = "../fhe" } +cipher = { path = "../cipher" } data = { path = "../data" } keyshare = { path = "../keyshare" } aggregator = { path = "../aggregator" } diff --git a/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs b/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs index 0db90213..1ac603bd 100644 --- a/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs +++ b/packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs @@ -1,6 +1,10 @@ +use cipher::Cipher; use data::{DataStore, InMemStore}; use enclave_core::{ - CiphernodeAdded, CiphernodeSelected, CiphertextOutputPublished, DecryptionshareCreated, E3RequestComplete, E3Requested, E3id, EnclaveEvent, EventBus, GetErrors, GetHistory, KeyshareCreated, OrderedSet, PlaintextAggregated, PublicKeyAggregated, ResetHistory, Seed, Shutdown + CiphernodeAdded, CiphernodeSelected, CiphertextOutputPublished, DecryptionshareCreated, + E3RequestComplete, E3Requested, E3id, EnclaveEvent, EventBus, GetErrors, GetHistory, + KeyshareCreated, OrderedSet, PlaintextAggregated, PublicKeyAggregated, ResetHistory, Seed, + Shutdown, }; use fhe::{setup_crp_params, ParamsWithCrp, SharedRng}; use logger::SimpleLogger; @@ -22,7 +26,7 @@ use fhe_traits::{FheEncoder, FheEncrypter, Serialize}; use rand::Rng; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; -use std::{env, sync::Arc, time::Duration}; +use std::{env, path::Path, sync::Arc, time::Duration}; use tokio::sync::Mutex; use tokio::{sync::mpsc::channel, time::sleep}; @@ -41,12 +45,12 @@ async fn setup_local_ciphernode( logging: bool, addr: &str, data: Option>, + cipher: &Arc, ) -> Result { // create data actor for saving data let data_actor = data.unwrap_or_else(|| InMemStore::new(logging).start()); let store = DataStore::from(&data_actor); let repositories = store.repositories(); - // create ciphernode actor for managing ciphernode flow let sortition = Sortition::attach(&bus, repositories.sortition()); CiphernodeSelector::attach(&bus, &sortition, addr); @@ -55,7 +59,7 @@ async fn setup_local_ciphernode( .add_feature(FheFeature::create(&bus, &rng)) .add_feature(PublicKeyAggregatorFeature::create(&bus, &sortition)) .add_feature(PlaintextAggregatorFeature::create(&bus, &sortition)) - .add_feature(KeyshareFeature::create(&bus, addr)) + .add_feature(KeyshareFeature::create(&bus, addr, &cipher)) .build() .await?; @@ -152,11 +156,12 @@ async fn create_local_ciphernodes( bus: &Addr, rng: &SharedRng, count: u32, + cipher: &Arc, ) -> Result> { let eth_addrs = create_random_eth_addrs(count); let mut result = vec![]; for addr in ð_addrs { - let tuple = setup_local_ciphernode(&bus, &rng, true, addr, None).await?; + let tuple = setup_local_ciphernode(&bus, &rng, true, addr, None, cipher).await?; result.push(tuple); } @@ -269,11 +274,11 @@ fn get_common_setup() -> Result<( #[actix::test] async fn test_public_key_aggregation_and_decryption() -> Result<()> { // Setup - env::set_var("CIPHERNODE_SECRET", "Don't tell anyone my secret"); let (bus, rng, seed, params, crpoly, e3_id) = get_common_setup()?; + let cipher = Arc::new(Cipher::from_password("Don't tell anyone my secret").await?); // Setup actual ciphernodes and dispatch add events - let ciphernode_addrs = create_local_ciphernodes(&bus, &rng, 3).await?; + let ciphernode_addrs = create_local_ciphernodes(&bus, &rng, 3, &cipher).await?; let eth_addrs = ciphernode_addrs .iter() .map(|tup| tup.0.to_owned()) @@ -375,13 +380,13 @@ async fn test_public_key_aggregation_and_decryption() -> Result<()> { #[actix::test] async fn test_stopped_keyshares_retain_state() -> Result<()> { - env::set_var("CIPHERNODE_SECRET", "Don't tell anyone my secret"); let (bus, rng, seed, params, crpoly, e3_id) = get_common_setup()?; + let cipher = Arc::new(Cipher::from_password("Don't tell anyone my secret").await?); let eth_addrs = create_random_eth_addrs(2); - let cn1 = setup_local_ciphernode(&bus, &rng, true, ð_addrs[0], None).await?; - let cn2 = setup_local_ciphernode(&bus, &rng, true, ð_addrs[1], None).await?; + let cn1 = setup_local_ciphernode(&bus, &rng, true, ð_addrs[0], None, &cipher).await?; + let cn2 = setup_local_ciphernode(&bus, &rng, true, ð_addrs[1], None, &cipher).await?; add_ciphernodes(&bus, ð_addrs).await?; // Send e3request @@ -409,7 +414,7 @@ async fn test_stopped_keyshares_retain_state() -> Result<()> { // Reset history bus.send(ResetHistory).await?; - + // Check event count is correct assert_eq!(history.len(), 7); @@ -420,8 +425,8 @@ async fn test_stopped_keyshares_retain_state() -> Result<()> { // Apply the address and data node to two new actors // Here we test that hydration occurred sucessfully - setup_local_ciphernode(&bus, &rng, true, &addr1, Some(data1)).await?; - setup_local_ciphernode(&bus, &rng, true, &addr2, Some(data2)).await?; + setup_local_ciphernode(&bus, &rng, true, &addr1, Some(data1), &cipher).await?; + setup_local_ciphernode(&bus, &rng, true, &addr2, Some(data2), &cipher).await?; // get the public key from history. let pubkey: PublicKey = history .iter() diff --git a/tests/basic_integration/test.sh b/tests/basic_integration/test.sh index fe7d9002..9fe6a617 100755 --- a/tests/basic_integration/test.sh +++ b/tests/basic_integration/test.sh @@ -26,6 +26,7 @@ ENCLAVE_CONTRACT="0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" REGISTRY_CONTRACT="0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" REGISTRY_FILTER_CONTRACT="0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" INPUT_VALIDATOR_CONTRACT="0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" + # These are random addresses for now CIPHERNODE_ADDRESS_1="0x2546BcD3c84621e976D8185a91A922aE77ECEc30" CIPHERNODE_ADDRESS_2="0xbDA5747bFD65F08deb54cb465eB87D40e51B197E" @@ -91,7 +92,7 @@ set_password() { launch_ciphernode() { local name="$1" heading "Launch ciphernode $name" - yarn ciphernode:launch \ + yarn enclave \ --config "$SCRIPT_DIR/lib/$name/config.yaml" } From b208e7b1e445c91f4ac79534342dc5d0322eee48 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 12:57:30 +1100 Subject: [PATCH 15/45] Ensure test works --- package.json | 1 - packages/ciphernode/Cargo.lock | 3 + packages/ciphernode/Cargo.toml | 1 + packages/ciphernode/config/Cargo.toml | 1 + packages/ciphernode/config/src/app_config.rs | 8 ++ packages/ciphernode/config/src/parsers.rs | 43 ++++++++++ packages/ciphernode/enclave/Cargo.toml | 5 +- .../ciphernode/enclave/src/commands/mod.rs | 5 +- .../ciphernode/enclave/src/commands/start.rs | 10 ++- .../enclave/src/commands/wallet/mod.rs | 15 +++- .../enclave/src/commands/wallet/set.rs | 22 +++-- packages/ciphernode/enclave/src/main.rs | 2 +- .../ciphernode/enclave_node/src/aggregator.rs | 9 +- .../ciphernode/enclave_node/src/ciphernode.rs | 2 +- packages/ciphernode/evm/Cargo.toml | 1 + packages/ciphernode/evm/src/helpers.rs | 20 ++++- packages/ciphernode/scripts/launch.sh | 2 +- tests/basic_integration/lib/ag/config.yaml | 2 +- tests/basic_integration/lib/ag/key | 1 - tests/basic_integration/lib/cn1/config.yaml | 2 +- tests/basic_integration/lib/cn1/key | 1 - tests/basic_integration/lib/cn2/config.yaml | 2 +- tests/basic_integration/lib/cn2/key | 1 - tests/basic_integration/lib/cn3/config.yaml | 2 +- tests/basic_integration/lib/cn3/key | 1 - tests/basic_integration/lib/cn4/config.yaml | 9 ++ tests/basic_integration/lib/prebuild.sh | 2 +- tests/basic_integration/test.sh | 86 ++++++++++++------- 28 files changed, 190 insertions(+), 69 deletions(-) create mode 100644 packages/ciphernode/config/src/parsers.rs delete mode 100644 tests/basic_integration/lib/ag/key delete mode 100644 tests/basic_integration/lib/cn1/key delete mode 100644 tests/basic_integration/lib/cn2/key delete mode 100644 tests/basic_integration/lib/cn3/key create mode 100644 tests/basic_integration/lib/cn4/config.yaml diff --git a/package.json b/package.json index 064f021d..d4fc3c7a 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,6 @@ "ciphernode:lint": "cd packages/ciphernode && cargo fmt -- --check", "ciphernode:add": "cd packages/evm && yarn ciphernode:add", "ciphernode:remove": "cd packages/evm && yarn ciphernode:remove", - "ciphernode:aggregator": "cd packages/ciphernode && ./scripts/aggregator.sh", "ciphernode:test": "cd packages/ciphernode && cargo test", "ciphernode:build": "cd packages/ciphernode && cargo build --release", "preciphernode:build": "yarn evm:compile", diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index 1b7f634f..a7e2d698 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -1680,6 +1680,7 @@ dependencies = [ name = "config" version = "0.1.0" dependencies = [ + "alloy", "anyhow", "dirs", "figment", @@ -2158,6 +2159,7 @@ dependencies = [ "dialoguer", "enclave-core", "enclave_node", + "hex", "router", "rpassword", "tokio", @@ -2300,6 +2302,7 @@ dependencies = [ "alloy", "alloy-primitives 0.6.4", "anyhow", + "cipher 0.1.0", "data", "enclave-core", "futures-util", diff --git a/packages/ciphernode/Cargo.toml b/packages/ciphernode/Cargo.toml index 82ae8e2c..e8a4fcd9 100644 --- a/packages/ciphernode/Cargo.toml +++ b/packages/ciphernode/Cargo.toml @@ -43,6 +43,7 @@ fhe-traits = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-b fhe-util = { git = "https://github.com/gnosisguild/fhe.rs", version = "0.1.0-beta.7" } futures = "0.3.30" futures-util = "0.3" +hex = "0.4.3" num = "0.4.3" rand_chacha = "0.3.1" rand = "0.8.5" diff --git a/packages/ciphernode/config/Cargo.toml b/packages/ciphernode/config/Cargo.toml index 548449bb..a694ceb5 100644 --- a/packages/ciphernode/config/Cargo.toml +++ b/packages/ciphernode/config/Cargo.toml @@ -8,3 +8,4 @@ dirs = { workspace = true } anyhow = { workspace = true } serde = { workspace = true } figment = { workspace = true } +alloy = { workspace = true } diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index 82eeda57..9f92fc1b 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -1,3 +1,4 @@ +use alloy::primitives::Address; use anyhow::Result; use figment::{ providers::{Format, Serialized, Yaml}, @@ -40,6 +41,8 @@ pub struct AppConfig { cwd: PathBuf, /// The default config dir for the operating system this should not be changed default_dir: PathBuf, + /// Ethereum Address for the node + address: Option

, } impl Default for AppConfig { @@ -53,6 +56,7 @@ impl Default for AppConfig { config_file: PathBuf::from("config.yaml"), // ~/.config/enclave/config.yaml cwd: env::current_dir().unwrap_or_default(), default_dir, // ~/.config/enclave + address: None, } } } @@ -95,6 +99,10 @@ impl AppConfig { false } + pub fn address(&self) -> Option
{ + self.address + } + pub fn config_dir(&self) -> PathBuf { normalize_path(self.config_dir_impl()) } diff --git a/packages/ciphernode/config/src/parsers.rs b/packages/ciphernode/config/src/parsers.rs new file mode 100644 index 00000000..e68831c1 --- /dev/null +++ b/packages/ciphernode/config/src/parsers.rs @@ -0,0 +1,43 @@ +use alloy::primitives::Address; +use clap::builder::TypedValueParser; + + +#[derive(Clone)] +pub struct EthAddressParser; + +impl TypedValueParser for EthAddressParser { + type Value = Address; + + fn parse_ref( + &self, + cmd: &clap::Command, + arg: Option<&clap::Arg>, + value: &std::ffi::OsStr, + ) -> Result { + let address_str = value.to_str().ok_or_else(|| { + clap::Error::raw( + clap::error::ErrorKind::InvalidUtf8, + "Ethereum address must be valid UTF-8", + ) + })?; + + // Remove '0x' prefix if present + let cleaned_address = address_str.strip_prefix("0x").unwrap_or(address_str); + + // Validate address format + if !cleaned_address.len() == 40 { + return Err(clap::Error::raw( + clap::error::ErrorKind::InvalidValue, + "Ethereum address must be 40 hexadecimal characters", + )); + } + + // Parse the address using alloy + Address::from_str(address_str).map_err(|_| { + clap::Error::raw( + clap::error::ErrorKind::InvalidValue, + "Invalid Ethereum address format", + ) + }) + } +} diff --git a/packages/ciphernode/enclave/Cargo.toml b/packages/ciphernode/enclave/Cargo.toml index 7703f586..e19c1036 100644 --- a/packages/ciphernode/enclave/Cargo.toml +++ b/packages/ciphernode/enclave/Cargo.toml @@ -15,11 +15,12 @@ anyhow = { workspace = true } cipher = { path = "../cipher" } clap = { workspace = true } config = { path = "../config" } -enclave-core = { path = "../core" } data = { path = "../data" } -router = { path = "../router" } dialoguer = "0.11.0" +enclave-core = { path = "../core" } enclave_node = { path = "../enclave_node" } +hex = { workspace = true } +router = { path = "../router" } rpassword = "7.3.1" tokio = { workspace = true } tracing = { workspace = true } diff --git a/packages/ciphernode/enclave/src/commands/mod.rs b/packages/ciphernode/enclave/src/commands/mod.rs index 6bd68378..9f6bde5c 100644 --- a/packages/ciphernode/enclave/src/commands/mod.rs +++ b/packages/ciphernode/enclave/src/commands/mod.rs @@ -11,10 +11,7 @@ use self::password::PasswordCommands; #[derive(Subcommand)] pub enum Commands { /// Start the application - Start { - #[arg(long)] - address: String, - }, + Start, /// Aggregator node management commands Aggregator { diff --git a/packages/ciphernode/enclave/src/commands/start.rs b/packages/ciphernode/enclave/src/commands/start.rs index a41252a1..14df1c64 100644 --- a/packages/ciphernode/enclave/src/commands/start.rs +++ b/packages/ciphernode/enclave/src/commands/start.rs @@ -1,15 +1,17 @@ -use alloy::primitives::Address; -use anyhow::{Context, Result}; +use anyhow::{anyhow, Result}; use config::AppConfig; use enclave_node::{listen_for_shutdown, setup_ciphernode}; use tracing::info; use crate::owo; -pub async fn execute(config: AppConfig, address: &str) -> Result<()> { +pub async fn execute(config: AppConfig) -> Result<()> { owo(); - let address = Address::parse_checksummed(&address, None).context("Invalid address")?; + // let address = Address::parse_checksummed(&config.address(), None).context("Invalid address")?; + let Some(address) = config.address() else { + return Err(anyhow!("You must provide an address")); + }; info!("LAUNCHING CIPHERNODE: ({})", address); diff --git a/packages/ciphernode/enclave/src/commands/wallet/mod.rs b/packages/ciphernode/enclave/src/commands/wallet/mod.rs index 519ad329..b07b1cc6 100644 --- a/packages/ciphernode/enclave/src/commands/wallet/mod.rs +++ b/packages/ciphernode/enclave/src/commands/wallet/mod.rs @@ -7,12 +7,19 @@ use config::AppConfig; pub enum WalletCommands { /// Set a new Wallet Private Key Set { - /// The new private key - #[arg(long = "private-key")] + /// The new private key - note we are leaving as hex string as it is easier to manage with + /// the allow Signer coercion + #[arg(long = "private-key", value_parser = ensure_hex)] private_key: String, }, - // /// Delete the current wallet - // Delete, +} + +fn ensure_hex(s: &str) -> Result { + if !s.starts_with("0x") { + bail!("hex value must start with '0x'") + } + hex::decode(&s[2..])?; + Ok(s.to_string()) } pub async fn execute(command: WalletCommands, config: AppConfig) -> Result<()> { diff --git a/packages/ciphernode/enclave/src/commands/wallet/set.rs b/packages/ciphernode/enclave/src/commands/wallet/set.rs index 08bf33a9..83a5206f 100644 --- a/packages/ciphernode/enclave/src/commands/wallet/set.rs +++ b/packages/ciphernode/enclave/src/commands/wallet/set.rs @@ -1,23 +1,29 @@ use actix::Actor; -use anyhow::Result; +use anyhow::{bail, Result}; use cipher::Cipher; use config::AppConfig; use enclave_core::{EventBus, GetErrors}; use enclave_node::get_repositories; pub async fn execute(config: &AppConfig, input: String) -> Result<()> { + println!("WALLET KEY: {}", input); let cipher = Cipher::from_config(config).await?; - let mut vec_input = input.as_bytes().to_vec(); - let encrypted = cipher.encrypt_data(&mut vec_input)?; + let encrypted = cipher.encrypt_data(&mut input.as_bytes().to_vec())?; let bus = EventBus::new(true).start(); let repositories = get_repositories(&config, &bus)?; + + // NOTE: We are writing an encrypted string here repositories.eth_private_key().write(&encrypted); + let errors = bus.send(GetErrors).await?; - for error in errors.iter() { - println!("{error}"); - } - if errors.len() == 0 { - println!("WalletKey key has been successfully encrypted."); + if errors.len() > 0 { + for error in errors.iter() { + println!("{error}"); + } + bail!("There were errors setting the wallet key") } + + println!("WalletKey key has been successfully encrypted."); + Ok(()) } diff --git a/packages/ciphernode/enclave/src/main.rs b/packages/ciphernode/enclave/src/main.rs index d6849203..fbb89333 100644 --- a/packages/ciphernode/enclave/src/main.rs +++ b/packages/ciphernode/enclave/src/main.rs @@ -43,7 +43,7 @@ impl Cli { let config = load_config(config_path)?; match self.command { - Commands::Start { address } => start::execute(config, &address).await?, + Commands::Start => start::execute(config).await?, Commands::Password { command } => password::execute(command, config).await?, Commands::Aggregator { command } => aggregator::execute(command, config).await?, Commands::Wallet { command } => wallet::execute(command, config).await?, diff --git a/packages/ciphernode/enclave_node/src/aggregator.rs b/packages/ciphernode/enclave_node/src/aggregator.rs index 273a671e..a341d0b3 100644 --- a/packages/ciphernode/enclave_node/src/aggregator.rs +++ b/packages/ciphernode/enclave_node/src/aggregator.rs @@ -1,10 +1,10 @@ use actix::{Actor, Addr}; -use anyhow::Result; +use anyhow::{bail, Result}; +use cipher::Cipher; use config::AppConfig; -use data::{DataStore, InMemStore, SledStore}; use enclave_core::EventBus; use evm::{ - helpers::pull_eth_signer_from_env, CiphernodeRegistrySol, EnclaveSol, RegistryFilterSol, + helpers::get_signer_from_repository, CiphernodeRegistrySol, EnclaveSol, RegistryFilterSol, }; use logger::SimpleLogger; use p2p::P2p; @@ -33,7 +33,8 @@ pub async fn setup_aggregator( let store = setup_datastore(&config, &bus)?; let repositories = store.repositories(); let sortition = Sortition::attach(&bus, repositories.sortition()); - let signer = pull_eth_signer_from_env("PRIVATE_KEY").await?; + let cipher = Arc::new(Cipher::from_config(&config).await?); + let signer = get_signer_from_repository(repositories.eth_private_key(),&cipher).await?; for chain in config .chains() .iter() diff --git a/packages/ciphernode/enclave_node/src/ciphernode.rs b/packages/ciphernode/enclave_node/src/ciphernode.rs index 73660e97..e947e896 100644 --- a/packages/ciphernode/enclave_node/src/ciphernode.rs +++ b/packages/ciphernode/enclave_node/src/ciphernode.rs @@ -27,7 +27,7 @@ pub async fn setup_ciphernode( rand_chacha::ChaCha20Rng::from_rng(OsRng).expect("Failed to create RNG"), )); let bus = EventBus::new(true).start(); - let cipher = Arc::new(Cipher::from_file(config.key_file()).await?); + let cipher = Arc::new(Cipher::from_config(&config).await?); let store = setup_datastore(&config, &bus)?; let repositories = store.repositories(); diff --git a/packages/ciphernode/evm/Cargo.toml b/packages/ciphernode/evm/Cargo.toml index 994f3e60..3224286c 100644 --- a/packages/ciphernode/evm/Cargo.toml +++ b/packages/ciphernode/evm/Cargo.toml @@ -12,5 +12,6 @@ enclave-core = { path = "../core" } data = { path = "../data" } futures-util = { workspace = true } sortition = { path = "../sortition" } +cipher = { path = "../cipher" } tokio = { workspace = true } tracing = { workspace = true } diff --git a/packages/ciphernode/evm/src/helpers.rs b/packages/ciphernode/evm/src/helpers.rs index 4aa15dca..77e81eec 100644 --- a/packages/ciphernode/evm/src/helpers.rs +++ b/packages/ciphernode/evm/src/helpers.rs @@ -12,7 +12,9 @@ use alloy::{ signers::local::PrivateKeySigner, transports::BoxTransport, }; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; +use cipher::Cipher; +use data::Repository; use enclave_core::{BusError, EnclaveErrorType, EnclaveEvent}; use futures_util::stream::StreamExt; use tokio::{select, sync::oneshot}; @@ -138,6 +140,22 @@ pub async fn pull_eth_signer_from_env(var: &str) -> Result Ok(Arc::new(signer)) } +pub async fn get_signer_from_repository( + repository: Repository>, + cipher: &Arc, +) -> Result> { + let Some(private_key_encrypted) = repository.read().await? else { + bail!("No private key was found!") + }; + + let encoded_private_key = cipher.decrypt_data(&private_key_encrypted)?; + + let private_key = String::from_utf8(encoded_private_key)?; + + let signer = private_key.parse()?; + Ok(Arc::new(signer)) +} + pub fn ensure_http_rpc(rpc_url: &str) -> String { if rpc_url.starts_with("ws://") { return rpc_url.replacen("ws://", "http://", 1); diff --git a/packages/ciphernode/scripts/launch.sh b/packages/ciphernode/scripts/launch.sh index d0afb599..df841f45 100755 --- a/packages/ciphernode/scripts/launch.sh +++ b/packages/ciphernode/scripts/launch.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -RUSTFLAGS="-A warnings" cargo run --bin enclave -- "$@" +RUSTFLAGS="-A warnings" cargo run --quiet --bin enclave -- "$@" diff --git a/tests/basic_integration/lib/ag/config.yaml b/tests/basic_integration/lib/ag/config.yaml index adc6e6f9..4b840a87 100644 --- a/tests/basic_integration/lib/ag/config.yaml +++ b/tests/basic_integration/lib/ag/config.yaml @@ -1,5 +1,5 @@ config_dir: . -address: 0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199 +address: "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199" chains: - name: "hardhat" rpc_url: "ws://localhost:8545" diff --git a/tests/basic_integration/lib/ag/key b/tests/basic_integration/lib/ag/key deleted file mode 100644 index 0970f941..00000000 --- a/tests/basic_integration/lib/ag/key +++ /dev/null @@ -1 +0,0 @@ -We are the music makers and we are the dreamers of the dreams. diff --git a/tests/basic_integration/lib/cn1/config.yaml b/tests/basic_integration/lib/cn1/config.yaml index 4c856c8a..f4d9c94d 100644 --- a/tests/basic_integration/lib/cn1/config.yaml +++ b/tests/basic_integration/lib/cn1/config.yaml @@ -1,5 +1,5 @@ config_dir: . -address: 0x2546BcD3c84621e976D8185a91A922aE77ECEc30 +address: "0x2546BcD3c84621e976D8185a91A922aE77ECEc30" chains: - name: "hardhat" rpc_url: "ws://localhost:8545" diff --git a/tests/basic_integration/lib/cn1/key b/tests/basic_integration/lib/cn1/key deleted file mode 100644 index 0970f941..00000000 --- a/tests/basic_integration/lib/cn1/key +++ /dev/null @@ -1 +0,0 @@ -We are the music makers and we are the dreamers of the dreams. diff --git a/tests/basic_integration/lib/cn2/config.yaml b/tests/basic_integration/lib/cn2/config.yaml index d57b9879..20d5183f 100644 --- a/tests/basic_integration/lib/cn2/config.yaml +++ b/tests/basic_integration/lib/cn2/config.yaml @@ -1,5 +1,5 @@ config_dir: . -address: 0xbDA5747bFD65F08deb54cb465eB87D40e51B197E +address: "0xbDA5747bFD65F08deb54cb465eB87D40e51B197E" chains: - name: "hardhat" rpc_url: "ws://localhost:8545" diff --git a/tests/basic_integration/lib/cn2/key b/tests/basic_integration/lib/cn2/key deleted file mode 100644 index 0970f941..00000000 --- a/tests/basic_integration/lib/cn2/key +++ /dev/null @@ -1 +0,0 @@ -We are the music makers and we are the dreamers of the dreams. diff --git a/tests/basic_integration/lib/cn3/config.yaml b/tests/basic_integration/lib/cn3/config.yaml index 6a3aa585..b98e0c63 100644 --- a/tests/basic_integration/lib/cn3/config.yaml +++ b/tests/basic_integration/lib/cn3/config.yaml @@ -1,5 +1,5 @@ config_dir: . -address: 0xdD2FD4581271e230360230F9337D5c0430Bf44C0 +address: "0xdD2FD4581271e230360230F9337D5c0430Bf44C0" chains: - name: "hardhat" rpc_url: "ws://localhost:8545" diff --git a/tests/basic_integration/lib/cn3/key b/tests/basic_integration/lib/cn3/key deleted file mode 100644 index 0970f941..00000000 --- a/tests/basic_integration/lib/cn3/key +++ /dev/null @@ -1 +0,0 @@ -We are the music makers and we are the dreamers of the dreams. diff --git a/tests/basic_integration/lib/cn4/config.yaml b/tests/basic_integration/lib/cn4/config.yaml new file mode 100644 index 00000000..4b840a87 --- /dev/null +++ b/tests/basic_integration/lib/cn4/config.yaml @@ -0,0 +1,9 @@ +config_dir: . +address: "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199" +chains: + - name: "hardhat" + rpc_url: "ws://localhost:8545" + contracts: + enclave: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" + ciphernode_registry: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" + filter_registry: "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" diff --git a/tests/basic_integration/lib/prebuild.sh b/tests/basic_integration/lib/prebuild.sh index a3cd9e88..4bf0baa8 100755 --- a/tests/basic_integration/lib/prebuild.sh +++ b/tests/basic_integration/lib/prebuild.sh @@ -1,3 +1,3 @@ #!/usr/bin/env sh -cd packages/ciphernode && RUSTFLAGS="-A warnings" cargo build --bin fake_encrypt --bin enclave --bin aggregator; +cd packages/ciphernode && RUSTFLAGS="-A warnings" cargo build --bin fake_encrypt --bin enclave; diff --git a/tests/basic_integration/test.sh b/tests/basic_integration/test.sh index 9fe6a617..df895af9 100755 --- a/tests/basic_integration/test.sh +++ b/tests/basic_integration/test.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -eu # Exit immediately if a command exits with a non-zero status -# + # Get the script's location SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" @@ -79,50 +79,59 @@ waiton-files() { done } -# This is temporary until we write the command api set_password() { local name="$1" local password="$2" - local config_dir="$SCRIPT_DIR/lib/$name" - mkdir -p $config_dir - echo "$password" > "$config_dir/key" - chmod 400 "$config_dir/key" + yarn enclave password create \ + --config "$SCRIPT_DIR/lib/$name/config.yaml" \ + --password "$password" } launch_ciphernode() { local name="$1" heading "Launch ciphernode $name" - yarn enclave \ - --config "$SCRIPT_DIR/lib/$name/config.yaml" + yarn enclave start \ + --config "$SCRIPT_DIR/lib/$name/config.yaml" & } +set_private_key() { + local name="$1" + local private_key="$2" + + yarn enclave wallet set \ + --config "$SCRIPT_DIR/lib/$name/config.yaml" \ + --private-key "$private_key" +} -# XXX: WE NEED TO NOW ADD THE PRIVATE KEY TO BE STORED ON THE DATASTORE -# - [x] Setup command files one for each command -# - [x] Setup cli parsing for the commands that defer to the commands -# - We must have access to the config -# -# enclave set-password -# -# -# enclave set-private-key launch_aggregator() { - local private_key="$1" - PRIVATE_KEY=$private_key yarn ciphernode:aggregator \ - --config "$SCRIPT_DIR/lib/ciphernode_config.yaml" \ + local name="$1" + heading "Launch aggregator $name" + + yarn enclave aggregator start \ + --config "$SCRIPT_DIR/lib/$name/config.yaml" \ --pubkey-write-path "$SCRIPT_DIR/output/pubkey.bin" \ --plaintext-write-path "$SCRIPT_DIR/output/plaintext.txt" & } +clean_folders() { + # Delete output artifacts + rm -rf "$SCRIPT_DIR/output/"* + + # Delete enclave artifacts + for name in cn1 cn2 cn3 cn4 ag; do + # List all files and directories except config.yaml, then delete them + find "$SCRIPT_DIR/lib/$name" -mindepth 1 ! -regex '.*/config\.yaml$' -exec rm -rf {} + + done +} + pkill -9 -f "target/debug/enclave" || true pkill -9 -f "hardhat node" || true -pkill -9 -f "target/debug/aggregator" || true # Set up trap to catch errors and interrupts trap 'cleanup $?' ERR INT TERM -# Delete output artifacts -rm -rf $ROOT_DIR/tests/basic_integration/output/* +# Tidy up files from previous runs +clean_folders $SCRIPT_DIR/lib/prebuild.sh @@ -138,18 +147,21 @@ done set_password cn1 "$CIPHERNODE_SECRET" set_password cn2 "$CIPHERNODE_SECRET" set_password cn3 "$CIPHERNODE_SECRET" +set_password cn4 "$CIPHERNODE_SECRET" set_password ag "$CIPHERNODE_SECRET" +set_private_key ag "$PRIVATE_KEY" + # Launch 4 ciphernodes -launch_ciphernode "$CIPHERNODE_ADDRESS_1" "$CIPHERNODE_SECRET" -launch_ciphernode "$CIPHERNODE_ADDRESS_2" "$CIPHERNODE_SECRET" -launch_ciphernode "$CIPHERNODE_ADDRESS_3" "$CIPHERNODE_SECRET" -launch_ciphernode "$CIPHERNODE_ADDRESS_4" "$CIPHERNODE_SECRET" -launch_aggregator "$PRIVATE_KEY" +launch_ciphernode cn1 +launch_ciphernode cn2 +launch_ciphernode cn3 +launch_ciphernode cn4 +launch_aggregator ag sleep 1 -waiton-files "$ROOT_DIR/packages/ciphernode/target/debug/enclave" "$ROOT_DIR/packages/ciphernode/target/debug/aggregator" "$ROOT_DIR/packages/ciphernode/target/debug/fake_encrypt" +waiton-files "$ROOT_DIR/packages/ciphernode/target/debug/enclave" "$ROOT_DIR/packages/ciphernode/target/debug/fake_encrypt" heading "Add ciphernode $CIPHERNODE_ADDRESS_1" yarn ciphernode:add --ciphernode-address $CIPHERNODE_ADDRESS_1 --network localhost @@ -202,6 +214,22 @@ if [[ "$ACTUAL" != "$PLAINTEXT"* ]]; then fi heading "Test PASSED !" +echo -e "\033[32m + β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ + β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ + β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ + β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ + β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ + β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ + β–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ + β–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ + β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ + β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ + β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ + β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ + β–ˆβ–ˆβ–ˆβ–ˆ + β–ˆβ–ˆ + \033[0m" pkill -15 -f "target/debug/enclave" || true pkill -15 -f "target/debug/aggregator" || true From f5d48179718f803ac52b1a8b4823e7c8dbbd6256 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 13:48:52 +1100 Subject: [PATCH 16/45] Fix folders being deleted --- packages/ciphernode/config/src/app_config.rs | 62 ++++++++++++-------- tests/basic_integration/lib/ag/.gitignore | 2 + tests/basic_integration/lib/ag/config.yaml | 1 + tests/basic_integration/lib/clean_folders.sh | 14 +++++ tests/basic_integration/lib/cn1/.gitignore | 2 + tests/basic_integration/lib/cn1/config.yaml | 1 + tests/basic_integration/lib/cn2/.gitignore | 2 + tests/basic_integration/lib/cn3/.gitignore | 2 + tests/basic_integration/lib/cn3/config.yaml | 1 + tests/basic_integration/lib/cn4/.gitignore | 2 + tests/basic_integration/lib/cn4/config.yaml | 1 + tests/basic_integration/test.sh | 14 +---- 12 files changed, 69 insertions(+), 35 deletions(-) create mode 100644 tests/basic_integration/lib/ag/.gitignore create mode 100755 tests/basic_integration/lib/clean_folders.sh create mode 100644 tests/basic_integration/lib/cn1/.gitignore create mode 100644 tests/basic_integration/lib/cn2/.gitignore create mode 100644 tests/basic_integration/lib/cn3/.gitignore create mode 100644 tests/basic_integration/lib/cn4/.gitignore diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index 9f92fc1b..7be01c9c 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -39,30 +39,29 @@ pub struct AppConfig { config_file: PathBuf, /// Used for testing if required cwd: PathBuf, - /// The default config dir for the operating system this should not be changed - default_dir: PathBuf, + /// The data dir for enclave defaults to `~/.local/share/enclave` + data_dir: PathBuf, /// Ethereum Address for the node address: Option
, } impl Default for AppConfig { fn default() -> Self { - let default_dir = dirs::config_dir().unwrap().join("enclave"); // ~/.config/enclave Self { chains: vec![], - key_file: PathBuf::from("key"), // ~/.config/enclave/key - db_file: PathBuf::from("db"), // ~/.config/enclave/db - config_dir: default_dir.clone(), // ~/.config/enclave + key_file: PathBuf::from("key"), // ~/.config/enclave/key + db_file: PathBuf::from("db"), // ~/.config/enclave/db + config_dir: OsDirs::config_dir(), // ~/.config/enclave + data_dir: OsDirs::data_dir(), // ~/.config/enclave config_file: PathBuf::from("config.yaml"), // ~/.config/enclave/config.yaml cwd: env::current_dir().unwrap_or_default(), - default_dir, // ~/.config/enclave address: None, } } } impl AppConfig { - fn ensure_full_path(&self, file: &PathBuf) -> PathBuf { + fn ensure_full_path(&self, dir:&PathBuf, file: &PathBuf) -> PathBuf { normalize_path({ // If this is absolute return it if file.is_absolute() || file.to_string_lossy().starts_with("~") { @@ -71,27 +70,26 @@ impl AppConfig { // We have to find where it should be relative from // Assume it should be the config_dir - self.config_dir().join(file) + dir.join(file) }) } - fn config_dir_impl(&self) -> PathBuf { - let config_dir = &self.config_dir; + fn resolve_base_dir(&self, base_dir:&PathBuf, default_base_dir:&PathBuf) -> PathBuf { - if config_dir.is_relative() { + if base_dir.is_relative() { // ConfigDir is relative and the config file is absolute then use the location of the // config file. That way all paths are relative to the config file if self.config_file.is_absolute() { self.config_file .parent() - .map_or_else(|| config_dir.clone(), |p| p.join(config_dir)) + .map_or_else(|| base_dir.clone(), |p| p.join(base_dir)) } else { // If the config_file is not set but there are relative paths use the default dir use the default dir - self.default_dir.join(config_dir) + default_base_dir.join(base_dir) } } else { - // Use the absolute config_dir - config_dir.to_owned() + // Use the absolute base_dir + base_dir.to_owned() } } @@ -102,9 +100,13 @@ impl AppConfig { pub fn address(&self) -> Option
{ self.address } - + + pub fn data_dir(&self) -> PathBuf { + normalize_path(self.resolve_base_dir(&self.data_dir, &OsDirs::data_dir())) + } + pub fn config_dir(&self) -> PathBuf { - normalize_path(self.config_dir_impl()) + normalize_path(self.resolve_base_dir(&self.config_dir, &OsDirs::config_dir())) } pub fn chains(&self) -> &Vec { @@ -112,15 +114,15 @@ impl AppConfig { } pub fn key_file(&self) -> PathBuf { - self.ensure_full_path(&self.key_file) + self.ensure_full_path(&self.config_dir(), &self.key_file) } pub fn db_file(&self) -> PathBuf { - self.ensure_full_path(&self.db_file) + self.ensure_full_path(&self.data_dir(), &self.db_file) } pub fn config_file(&self) -> PathBuf { - self.ensure_full_path(&self.config_file) + self.ensure_full_path(&self.config_dir(), &self.config_file) } pub fn cwd(&self) -> PathBuf { @@ -173,6 +175,17 @@ fn normalize_path(path: impl AsRef) -> PathBuf { result } +struct OsDirs; +impl OsDirs { + pub fn config_dir() -> PathBuf { + dirs::config_dir().unwrap().join("enclave") + } + + pub fn data_dir() -> PathBuf { + dirs::data_local_dir().unwrap().join("enclave") + } +} + #[cfg(test)] mod tests { use super::*; @@ -186,11 +199,12 @@ mod tests { let config = AppConfig { config_file: "/home/testuser/docs/myconfig.yaml".into(), config_dir: "../foo".into(), + data_dir: "../bar".into(), ..AppConfig::default() }; assert_eq!(config.key_file(), PathBuf::from("/home/testuser/foo/key")); - assert_eq!(config.db_file(), PathBuf::from("/home/testuser/foo/db")); + assert_eq!(config.db_file(), PathBuf::from("/home/testuser/bar/db")); Ok(()) }); @@ -210,7 +224,7 @@ mod tests { assert_eq!( config.db_file(), - PathBuf::from("/home/testuser/.config/enclave/db") + PathBuf::from("/home/testuser/.local/share/enclave/db") ); assert_eq!( @@ -262,3 +276,5 @@ chains: }); } } + + diff --git a/tests/basic_integration/lib/ag/.gitignore b/tests/basic_integration/lib/ag/.gitignore new file mode 100644 index 00000000..e8d99d12 --- /dev/null +++ b/tests/basic_integration/lib/ag/.gitignore @@ -0,0 +1,2 @@ +db +key diff --git a/tests/basic_integration/lib/ag/config.yaml b/tests/basic_integration/lib/ag/config.yaml index 4b840a87..55bdb526 100644 --- a/tests/basic_integration/lib/ag/config.yaml +++ b/tests/basic_integration/lib/ag/config.yaml @@ -1,4 +1,5 @@ config_dir: . +data_dir: . address: "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199" chains: - name: "hardhat" diff --git a/tests/basic_integration/lib/clean_folders.sh b/tests/basic_integration/lib/clean_folders.sh new file mode 100755 index 00000000..ff7e9d22 --- /dev/null +++ b/tests/basic_integration/lib/clean_folders.sh @@ -0,0 +1,14 @@ +clean_folders() { + local SCRIPT_DIR=$1 + + # Delete output artifacts + rm -rf "$SCRIPT_DIR/output/"* + + # Delete enclave artifacts + for name in cn1 cn2 cn3 cn4 ag; do + # List all files and directories except config.yaml, then delete them + find "$SCRIPT_DIR/lib/$name" -mindepth 1 ! -regex '.*/config\.yaml$' ! -regex '.*/.gitignore$' -exec rm -rf {} + + done +} + +clean_folders $1 diff --git a/tests/basic_integration/lib/cn1/.gitignore b/tests/basic_integration/lib/cn1/.gitignore new file mode 100644 index 00000000..e8d99d12 --- /dev/null +++ b/tests/basic_integration/lib/cn1/.gitignore @@ -0,0 +1,2 @@ +db +key diff --git a/tests/basic_integration/lib/cn1/config.yaml b/tests/basic_integration/lib/cn1/config.yaml index f4d9c94d..508112cd 100644 --- a/tests/basic_integration/lib/cn1/config.yaml +++ b/tests/basic_integration/lib/cn1/config.yaml @@ -1,4 +1,5 @@ config_dir: . +data_dir: . address: "0x2546BcD3c84621e976D8185a91A922aE77ECEc30" chains: - name: "hardhat" diff --git a/tests/basic_integration/lib/cn2/.gitignore b/tests/basic_integration/lib/cn2/.gitignore new file mode 100644 index 00000000..e8d99d12 --- /dev/null +++ b/tests/basic_integration/lib/cn2/.gitignore @@ -0,0 +1,2 @@ +db +key diff --git a/tests/basic_integration/lib/cn3/.gitignore b/tests/basic_integration/lib/cn3/.gitignore new file mode 100644 index 00000000..e8d99d12 --- /dev/null +++ b/tests/basic_integration/lib/cn3/.gitignore @@ -0,0 +1,2 @@ +db +key diff --git a/tests/basic_integration/lib/cn3/config.yaml b/tests/basic_integration/lib/cn3/config.yaml index b98e0c63..258b8488 100644 --- a/tests/basic_integration/lib/cn3/config.yaml +++ b/tests/basic_integration/lib/cn3/config.yaml @@ -1,4 +1,5 @@ config_dir: . +data_dir: . address: "0xdD2FD4581271e230360230F9337D5c0430Bf44C0" chains: - name: "hardhat" diff --git a/tests/basic_integration/lib/cn4/.gitignore b/tests/basic_integration/lib/cn4/.gitignore new file mode 100644 index 00000000..e8d99d12 --- /dev/null +++ b/tests/basic_integration/lib/cn4/.gitignore @@ -0,0 +1,2 @@ +db +key diff --git a/tests/basic_integration/lib/cn4/config.yaml b/tests/basic_integration/lib/cn4/config.yaml index 4b840a87..55bdb526 100644 --- a/tests/basic_integration/lib/cn4/config.yaml +++ b/tests/basic_integration/lib/cn4/config.yaml @@ -1,4 +1,5 @@ config_dir: . +data_dir: . address: "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199" chains: - name: "hardhat" diff --git a/tests/basic_integration/test.sh b/tests/basic_integration/test.sh index df895af9..1fb9e3f5 100755 --- a/tests/basic_integration/test.sh +++ b/tests/basic_integration/test.sh @@ -113,16 +113,7 @@ launch_aggregator() { --plaintext-write-path "$SCRIPT_DIR/output/plaintext.txt" & } -clean_folders() { - # Delete output artifacts - rm -rf "$SCRIPT_DIR/output/"* - - # Delete enclave artifacts - for name in cn1 cn2 cn3 cn4 ag; do - # List all files and directories except config.yaml, then delete them - find "$SCRIPT_DIR/lib/$name" -mindepth 1 ! -regex '.*/config\.yaml$' -exec rm -rf {} + - done -} + pkill -9 -f "target/debug/enclave" || true pkill -9 -f "hardhat node" || true @@ -130,9 +121,8 @@ pkill -9 -f "hardhat node" || true # Set up trap to catch errors and interrupts trap 'cleanup $?' ERR INT TERM -# Tidy up files from previous runs -clean_folders +$SCRIPT_DIR/lib/clean_folders.sh "$SCRIPT_DIR" $SCRIPT_DIR/lib/prebuild.sh heading "Start the EVM node" From 67822b09e318803a67f027e10f48354a63717f0c Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 13:52:19 +1100 Subject: [PATCH 17/45] Formatting --- packages/ciphernode/config/src/app_config.rs | 13 +++++-------- packages/ciphernode/core/src/eventbus.rs | 15 ++++++++------- packages/ciphernode/data/src/sled_store.rs | 8 ++++++-- packages/ciphernode/enclave/src/commands/mod.rs | 13 ++++++------- .../enclave/src/commands/password/create.rs | 4 ++-- .../enclave/src/commands/password/mod.rs | 2 +- .../enclave/src/commands/password/overwrite.rs | 2 +- .../ciphernode/enclave_node/src/aggregator.rs | 2 +- packages/ciphernode/enclave_node/src/datastore.rs | 3 +-- packages/ciphernode/enclave_node/src/lib.rs | 4 ++-- packages/ciphernode/evm/src/enclave_sol_writer.rs | 1 - packages/ciphernode/router/src/hooks.rs | 10 +++++----- 12 files changed, 38 insertions(+), 39 deletions(-) diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index 7be01c9c..8f0e1dd1 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -49,10 +49,10 @@ impl Default for AppConfig { fn default() -> Self { Self { chains: vec![], - key_file: PathBuf::from("key"), // ~/.config/enclave/key - db_file: PathBuf::from("db"), // ~/.config/enclave/db + key_file: PathBuf::from("key"), // ~/.config/enclave/key + db_file: PathBuf::from("db"), // ~/.config/enclave/db config_dir: OsDirs::config_dir(), // ~/.config/enclave - data_dir: OsDirs::data_dir(), // ~/.config/enclave + data_dir: OsDirs::data_dir(), // ~/.config/enclave config_file: PathBuf::from("config.yaml"), // ~/.config/enclave/config.yaml cwd: env::current_dir().unwrap_or_default(), address: None, @@ -61,7 +61,7 @@ impl Default for AppConfig { } impl AppConfig { - fn ensure_full_path(&self, dir:&PathBuf, file: &PathBuf) -> PathBuf { + fn ensure_full_path(&self, dir: &PathBuf, file: &PathBuf) -> PathBuf { normalize_path({ // If this is absolute return it if file.is_absolute() || file.to_string_lossy().starts_with("~") { @@ -74,8 +74,7 @@ impl AppConfig { }) } - fn resolve_base_dir(&self, base_dir:&PathBuf, default_base_dir:&PathBuf) -> PathBuf { - + fn resolve_base_dir(&self, base_dir: &PathBuf, default_base_dir: &PathBuf) -> PathBuf { if base_dir.is_relative() { // ConfigDir is relative and the config file is absolute then use the location of the // config file. That way all paths are relative to the config file @@ -276,5 +275,3 @@ chains: }); } } - - diff --git a/packages/ciphernode/core/src/eventbus.rs b/packages/ciphernode/core/src/eventbus.rs index 5ece9e57..f1a07a37 100644 --- a/packages/ciphernode/core/src/eventbus.rs +++ b/packages/ciphernode/core/src/eventbus.rs @@ -88,16 +88,17 @@ impl Handler for EventBus { type Result = Vec; fn handle(&mut self, _: GetErrors, _: &mut Context) -> Vec { - self.history.iter().filter_map(|evt| { - match evt { - EnclaveEvent::EnclaveError {data, .. } => Some(data), - _ => None - } - }).cloned().collect() + self.history + .iter() + .filter_map(|evt| match evt { + EnclaveEvent::EnclaveError { data, .. } => Some(data), + _ => None, + }) + .cloned() + .collect() } } - impl Handler for EventBus { type Result = (); diff --git a/packages/ciphernode/data/src/sled_store.rs b/packages/ciphernode/data/src/sled_store.rs index 8869049e..41de208f 100644 --- a/packages/ciphernode/data/src/sled_store.rs +++ b/packages/ciphernode/data/src/sled_store.rs @@ -63,8 +63,12 @@ pub struct SledDb { impl SledDb { pub fn new(path: &PathBuf) -> Result { - let db = sled::open(path) - .with_context(|| format!("Could not open database at path '{}'", path.to_string_lossy()))?; + let db = sled::open(path).with_context(|| { + format!( + "Could not open database at path '{}'", + path.to_string_lossy() + ) + })?; Ok(Self { db }) } diff --git a/packages/ciphernode/enclave/src/commands/mod.rs b/packages/ciphernode/enclave/src/commands/mod.rs index 9f6bde5c..aebde76a 100644 --- a/packages/ciphernode/enclave/src/commands/mod.rs +++ b/packages/ciphernode/enclave/src/commands/mod.rs @@ -1,12 +1,12 @@ -pub mod start; -pub mod password; pub mod aggregator; +pub mod password; +pub mod start; pub mod wallet; +use self::password::PasswordCommands; use aggregator::AggregatorCommands; use clap::Subcommand; use wallet::WalletCommands; -use self::password::PasswordCommands; #[derive(Subcommand)] pub enum Commands { @@ -18,7 +18,7 @@ pub enum Commands { #[command(subcommand)] command: AggregatorCommands, }, - + /// Password management commands Password { #[command(subcommand)] @@ -28,7 +28,6 @@ pub enum Commands { /// Wallet management commands Wallet { #[command(subcommand)] - command: WalletCommands - } + command: WalletCommands, + }, } - diff --git a/packages/ciphernode/enclave/src/commands/password/create.rs b/packages/ciphernode/enclave/src/commands/password/create.rs index 9630f933..227e837b 100644 --- a/packages/ciphernode/enclave/src/commands/password/create.rs +++ b/packages/ciphernode/enclave/src/commands/password/create.rs @@ -25,11 +25,11 @@ fn get_zeroizing_pw_vec(input: Option) -> Result>> { } let pw = Zeroizing::new(pw_str.trim().as_bytes().to_owned()); - + // Clean up sensitive data pw_str.zeroize(); confirm_pw_str.zeroize(); - + Ok(pw) } diff --git a/packages/ciphernode/enclave/src/commands/password/mod.rs b/packages/ciphernode/enclave/src/commands/password/mod.rs index aa85ec9a..e9264881 100644 --- a/packages/ciphernode/enclave/src/commands/password/mod.rs +++ b/packages/ciphernode/enclave/src/commands/password/mod.rs @@ -1,6 +1,6 @@ +mod create; mod delete; mod overwrite; -mod create; use anyhow::*; use clap::Subcommand; use config::AppConfig; diff --git a/packages/ciphernode/enclave/src/commands/password/overwrite.rs b/packages/ciphernode/enclave/src/commands/password/overwrite.rs index 5c868a2b..301539d5 100644 --- a/packages/ciphernode/enclave/src/commands/password/overwrite.rs +++ b/packages/ciphernode/enclave/src/commands/password/overwrite.rs @@ -1,5 +1,5 @@ -use super::delete::{prompt_delete, DeleteMode}; use super::create::execute as set_password; +use super::delete::{prompt_delete, DeleteMode}; use anyhow::Result; use config::AppConfig; diff --git a/packages/ciphernode/enclave_node/src/aggregator.rs b/packages/ciphernode/enclave_node/src/aggregator.rs index a341d0b3..533e1e32 100644 --- a/packages/ciphernode/enclave_node/src/aggregator.rs +++ b/packages/ciphernode/enclave_node/src/aggregator.rs @@ -34,7 +34,7 @@ pub async fn setup_aggregator( let repositories = store.repositories(); let sortition = Sortition::attach(&bus, repositories.sortition()); let cipher = Arc::new(Cipher::from_config(&config).await?); - let signer = get_signer_from_repository(repositories.eth_private_key(),&cipher).await?; + let signer = get_signer_from_repository(repositories.eth_private_key(), &cipher).await?; for chain in config .chains() .iter() diff --git a/packages/ciphernode/enclave_node/src/datastore.rs b/packages/ciphernode/enclave_node/src/datastore.rs index 3617a338..f88ff834 100644 --- a/packages/ciphernode/enclave_node/src/datastore.rs +++ b/packages/ciphernode/enclave_node/src/datastore.rs @@ -14,8 +14,7 @@ pub fn setup_datastore(config: &AppConfig, bus: &Addr) -> Result) -> Result { +pub fn get_repositories(config: &AppConfig, bus: &Addr) -> Result { let store = setup_datastore(config, &bus)?; Ok(store.repositories()) } - diff --git a/packages/ciphernode/enclave_node/src/lib.rs b/packages/ciphernode/enclave_node/src/lib.rs index 40c439e0..d66e66b1 100644 --- a/packages/ciphernode/enclave_node/src/lib.rs +++ b/packages/ciphernode/enclave_node/src/lib.rs @@ -1,9 +1,9 @@ mod aggregator; mod ciphernode; -mod shutdown; mod datastore; +mod shutdown; pub use aggregator::*; pub use ciphernode::*; -pub use shutdown::*; pub use datastore::*; +pub use shutdown::*; diff --git a/packages/ciphernode/evm/src/enclave_sol_writer.rs b/packages/ciphernode/evm/src/enclave_sol_writer.rs index 689c1e6d..ce7dfa03 100644 --- a/packages/ciphernode/evm/src/enclave_sol_writer.rs +++ b/packages/ciphernode/evm/src/enclave_sol_writer.rs @@ -38,7 +38,6 @@ impl EnclaveSolWriter { contract_address: Address, signer: &Arc, ) -> Result { - Ok(Self { provider: create_provider_with_signer(&ensure_http_rpc(rpc_url), signer).await?, contract_address, diff --git a/packages/ciphernode/router/src/hooks.rs b/packages/ciphernode/router/src/hooks.rs index 3ba881c9..c2969214 100644 --- a/packages/ciphernode/router/src/hooks.rs +++ b/packages/ciphernode/router/src/hooks.rs @@ -87,15 +87,15 @@ impl E3Feature for FheFeature { pub struct KeyshareFeature { bus: Addr, address: String, - cipher: Arc + cipher: Arc, } impl KeyshareFeature { - pub fn create(bus: &Addr, address: &str, cipher:&Arc) -> Box { + pub fn create(bus: &Addr, address: &str, cipher: &Arc) -> Box { Box::new(Self { bus: bus.clone(), address: address.to_owned(), - cipher: cipher.to_owned() + cipher: cipher.to_owned(), }) } } @@ -127,7 +127,7 @@ impl E3Feature for KeyshareFeature { store: ctx.repositories().keyshare(&e3_id), fhe: fhe.clone(), address: self.address.clone(), - cipher: self.cipher.clone() + cipher: self.cipher.clone(), }) .start(), ); @@ -166,7 +166,7 @@ impl E3Feature for KeyshareFeature { bus: self.bus.clone(), store, address: self.address.clone(), - cipher: self.cipher.clone() + cipher: self.cipher.clone(), }, snap, ) From eba452f4c31c4abfdb4ecb1722f95dfe32b130d8 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 14:14:53 +1100 Subject: [PATCH 18/45] Try using the supplied home var --- packages/ciphernode/config/src/app_config.rs | 24 +++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index 8f0e1dd1..d7a692ae 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -193,17 +193,18 @@ mod tests { #[test] fn test_ensure_relative_path() { Jail::expect_with(|jail| { - jail.set_env("HOME", "/home/testuser"); + let home = std::env::var("HOME").unwrap_or_else(|_| "/home/testuser".to_string()); + jail.set_env("HOME", &home); let config = AppConfig { - config_file: "/home/testuser/docs/myconfig.yaml".into(), + config_file: format!("{}/docs/myconfig.yaml", &home).into(), config_dir: "../foo".into(), data_dir: "../bar".into(), ..AppConfig::default() }; - assert_eq!(config.key_file(), PathBuf::from("/home/testuser/foo/key")); - assert_eq!(config.db_file(), PathBuf::from("/home/testuser/bar/db")); + assert_eq!(config.key_file(), PathBuf::from(format!("{}/foo/key",home))); + assert_eq!(config.db_file(), PathBuf::from(format!("{}/bar/db", home))); Ok(()) }); @@ -212,28 +213,29 @@ mod tests { #[test] fn test_defaults() { Jail::expect_with(|jail| { - jail.set_env("HOME", "/home/testuser"); + let home = std::env::var("HOME").unwrap_or_else(|_| "/home/testuser".to_string()); + jail.set_env("HOME", &home); let config = AppConfig::default(); assert_eq!( config.key_file(), - PathBuf::from("/home/testuser/.config/enclave/key") + PathBuf::from(format!("{}/.config/enclave/key",home)) ); assert_eq!( config.db_file(), - PathBuf::from("/home/testuser/.local/share/enclave/db") + PathBuf::from(format!("{}/.local/share/enclave/db", home)) ); assert_eq!( config.config_file(), - PathBuf::from("/home/testuser/.config/enclave/config.yaml") + PathBuf::from(format!("{}/.config/enclave/config.yaml",home)) ); assert_eq!( config.config_dir(), - PathBuf::from("/home/testuser/.config/enclave/") + PathBuf::from(format!("{}/.config/enclave/", home)) ); Ok(()) @@ -244,11 +246,11 @@ mod tests { fn test_config() { Jail::expect_with(|jail| { let home = format!("{}", jail.directory().to_string_lossy()); + jail.set_env("HOME", &home); + let filename = format!("{}/.config/enclave/config.yaml", home); let filedir = format!("{}/.config/enclave", home); - jail.create_dir(filedir)?; - jail.set_env("HOME", &home); jail.create_file( filename, r#" From 4bd5815e2c8b690be8547008e69fbe8077ee5680 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 14:17:38 +1100 Subject: [PATCH 19/45] Formatting --- packages/ciphernode/config/src/app_config.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index d7a692ae..a27aca53 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -203,7 +203,10 @@ mod tests { ..AppConfig::default() }; - assert_eq!(config.key_file(), PathBuf::from(format!("{}/foo/key",home))); + assert_eq!( + config.key_file(), + PathBuf::from(format!("{}/foo/key", home)) + ); assert_eq!(config.db_file(), PathBuf::from(format!("{}/bar/db", home))); Ok(()) @@ -220,7 +223,7 @@ mod tests { assert_eq!( config.key_file(), - PathBuf::from(format!("{}/.config/enclave/key",home)) + PathBuf::from(format!("{}/.config/enclave/key", home)) ); assert_eq!( @@ -230,7 +233,7 @@ mod tests { assert_eq!( config.config_file(), - PathBuf::from(format!("{}/.config/enclave/config.yaml",home)) + PathBuf::from(format!("{}/.config/enclave/config.yaml", home)) ); assert_eq!( From 00c7636922e300470ff5f2aefc84f2ba96c570fe Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 15:35:18 +1100 Subject: [PATCH 20/45] Debug env --- .github/workflows/rust-ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index f1bdd692..20fb022f 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -41,6 +41,9 @@ jobs: - name: Checking code format run: cd ./packages/ciphernode && cargo fmt -- --check + - name: debug + run: env + - name: Run tests run: | cd ./packages/ciphernode/ From 0b6da01ae207a0431eb5c460e6a08b87c27f91b3 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 15:39:18 +1100 Subject: [PATCH 21/45] Try setting TMPDIR to RUNNER_TEMP --- .github/workflows/rust-ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index 20fb022f..f538c733 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -41,10 +41,7 @@ jobs: - name: Checking code format run: cd ./packages/ciphernode && cargo fmt -- --check - - name: debug - run: env - - name: Run tests run: | cd ./packages/ciphernode/ - cargo test + TMPDIR=${RUNNER_TEMP} cargo test From e28c5b408a57564459623d9270079b13bae4c7a6 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 15:48:25 +1100 Subject: [PATCH 22/45] Try setting env in ga --- .github/workflows/rust-ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index f538c733..966cfd78 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -10,7 +10,8 @@ on: jobs: ci: runs-on: ubuntu-latest - + env: + TMPDIR: ${{ runner.temp }} steps: - uses: actions/checkout@v2 @@ -44,4 +45,4 @@ jobs: - name: Run tests run: | cd ./packages/ciphernode/ - TMPDIR=${RUNNER_TEMP} cargo test + cargo test From 443c0fba04837b455dcbd10b8951c5958f8fd863 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 15:50:14 +1100 Subject: [PATCH 23/45] Check env --- .github/workflows/rust-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index 966cfd78..ac49a9ac 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -44,5 +44,6 @@ jobs: - name: Run tests run: | + env && cd ./packages/ciphernode/ cargo test From 3a87a6f110a3f1b1bbcb46bd83b45f67b7f91c62 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 15:51:39 +1100 Subject: [PATCH 24/45] Try setting in step --- .github/workflows/rust-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index ac49a9ac..dd985b61 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -10,8 +10,6 @@ on: jobs: ci: runs-on: ubuntu-latest - env: - TMPDIR: ${{ runner.temp }} steps: - uses: actions/checkout@v2 @@ -43,6 +41,8 @@ jobs: run: cd ./packages/ciphernode && cargo fmt -- --check - name: Run tests + env: + TMPDIR: ${{ runner.temp }} run: | env && cd ./packages/ciphernode/ From 04ee093a677ead7a338df31ee4cb9d61dafe6817 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 15:57:02 +1100 Subject: [PATCH 25/45] Try debug where the file is written to --- packages/ciphernode/config/src/app_config.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index a27aca53..3c1efbb1 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -249,6 +249,7 @@ mod tests { fn test_config() { Jail::expect_with(|jail| { let home = format!("{}", jail.directory().to_string_lossy()); + println!(">>> HOME: {}", &home); jail.set_env("HOME", &home); let filename = format!("{}/.config/enclave/config.yaml", home); From 004637b6f92be57cbacabc2d59e7a8bada57e15e Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 16:25:30 +1100 Subject: [PATCH 26/45] debug config_file() --- .github/workflows/rust-ci.yml | 2 +- packages/ciphernode/config/src/app_config.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index dd985b61..85c68bff 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -46,4 +46,4 @@ jobs: run: | env && cd ./packages/ciphernode/ - cargo test + cargo test -- --nocapture diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index 3c1efbb1..4592906f 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -268,8 +268,10 @@ chains: "#, )?; + println!("AppConfig::default().config_file: {:?}", AppConfig::default().config_file()); let config: AppConfig = load_config(None).map_err(|err| err.to_string())?; let chain = config.chains().first().unwrap(); + assert_eq!(chain.name, "hardhat"); assert_eq!(chain.rpc_url, "ws://localhost:8545"); assert_eq!( From 90d30aa3ea62fa7974e361f52b5fd7e49294a70f Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 16:29:32 +1100 Subject: [PATCH 27/45] Format --- packages/ciphernode/config/src/app_config.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index 4592906f..341028cb 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -268,7 +268,10 @@ chains: "#, )?; - println!("AppConfig::default().config_file: {:?}", AppConfig::default().config_file()); + println!( + "AppConfig::default().config_file: {:?}", + AppConfig::default().config_file() + ); let config: AppConfig = load_config(None).map_err(|err| err.to_string())?; let chain = config.chains().first().unwrap(); From 632b3a3d74a878ee5567199c0ec183042e8adca9 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 16:41:48 +1100 Subject: [PATCH 28/45] Add tracing --- packages/ciphernode/config/src/app_config.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index 341028cb..4fd52908 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -75,18 +75,30 @@ impl AppConfig { } fn resolve_base_dir(&self, base_dir: &PathBuf, default_base_dir: &PathBuf) -> PathBuf { + println!( + "### resolve_base_dir {:?}<::>{:?}", + base_dir, default_base_dir + ); if base_dir.is_relative() { // ConfigDir is relative and the config file is absolute then use the location of the // config file. That way all paths are relative to the config file + println!("### base_dir.is_relative() !"); + if self.config_file.is_absolute() { + println!("### config_file.is_absolute() !"); + self.config_file .parent() .map_or_else(|| base_dir.clone(), |p| p.join(base_dir)) } else { + println!("### config_file.is_NOT_absolute() !"); + // If the config_file is not set but there are relative paths use the default dir use the default dir default_base_dir.join(base_dir) } } else { + println!("### DECIDED TO JUST use base_dir!"); + // Use the absolute base_dir base_dir.to_owned() } From e2b01d15e3b9f7ec5e0abd14b9ed7c7fbd91850a Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 16:52:37 +1100 Subject: [PATCH 29/45] Try setting XDG_CONFIG_HOME --- packages/ciphernode/config/src/app_config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index 4fd52908..f090c4aa 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -263,7 +263,7 @@ mod tests { let home = format!("{}", jail.directory().to_string_lossy()); println!(">>> HOME: {}", &home); jail.set_env("HOME", &home); - + jail.set_env("XDG_CONFIG_HOME", &format!("{}/.config", home)); let filename = format!("{}/.config/enclave/config.yaml", home); let filedir = format!("{}/.config/enclave", home); jail.create_dir(filedir)?; From 1e2153c7412123a29605977276bf911db4ce1302 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 16:57:28 +1100 Subject: [PATCH 30/45] Remove debugging --- packages/ciphernode/config/src/app_config.rs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index f090c4aa..6b02d47a 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -75,30 +75,18 @@ impl AppConfig { } fn resolve_base_dir(&self, base_dir: &PathBuf, default_base_dir: &PathBuf) -> PathBuf { - println!( - "### resolve_base_dir {:?}<::>{:?}", - base_dir, default_base_dir - ); if base_dir.is_relative() { // ConfigDir is relative and the config file is absolute then use the location of the // config file. That way all paths are relative to the config file - println!("### base_dir.is_relative() !"); - if self.config_file.is_absolute() { - println!("### config_file.is_absolute() !"); - self.config_file .parent() .map_or_else(|| base_dir.clone(), |p| p.join(base_dir)) } else { - println!("### config_file.is_NOT_absolute() !"); - // If the config_file is not set but there are relative paths use the default dir use the default dir default_base_dir.join(base_dir) } } else { - println!("### DECIDED TO JUST use base_dir!"); - // Use the absolute base_dir base_dir.to_owned() } @@ -261,7 +249,6 @@ mod tests { fn test_config() { Jail::expect_with(|jail| { let home = format!("{}", jail.directory().to_string_lossy()); - println!(">>> HOME: {}", &home); jail.set_env("HOME", &home); jail.set_env("XDG_CONFIG_HOME", &format!("{}/.config", home)); let filename = format!("{}/.config/enclave/config.yaml", home); @@ -280,10 +267,7 @@ chains: "#, )?; - println!( - "AppConfig::default().config_file: {:?}", - AppConfig::default().config_file() - ); + let config: AppConfig = load_config(None).map_err(|err| err.to_string())?; let chain = config.chains().first().unwrap(); From b083df2f2e036338b3d43c8fca4590621a56f810 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 17:01:18 +1100 Subject: [PATCH 31/45] Formatting --- .github/workflows/rust-ci.yml | 4 +--- packages/ciphernode/Cargo.toml | 3 ++- packages/ciphernode/config/src/app_config.rs | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index 85c68bff..5a9daa70 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -41,9 +41,7 @@ jobs: run: cd ./packages/ciphernode && cargo fmt -- --check - name: Run tests - env: - TMPDIR: ${{ runner.temp }} run: | env && cd ./packages/ciphernode/ - cargo test -- --nocapture + cargo test diff --git a/packages/ciphernode/Cargo.toml b/packages/ciphernode/Cargo.toml index e8a4fcd9..4caceadf 100644 --- a/packages/ciphernode/Cargo.toml +++ b/packages/ciphernode/Cargo.toml @@ -14,7 +14,8 @@ members = [ "test_helpers", "logger", "tests", - "cipher", "config", + "cipher", + "config", ] [workspace.dependencies] diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index 6b02d47a..80075646 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -267,7 +267,6 @@ chains: "#, )?; - let config: AppConfig = load_config(None).map_err(|err| err.to_string())?; let chain = config.chains().first().unwrap(); From bb820bdeb168ef655d6b24c5b92645f5903de99d Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 17:35:49 +1100 Subject: [PATCH 32/45] Add comment --- packages/ciphernode/cipher/src/cipher.rs | 5 +++-- packages/ciphernode/cipher/src/password_manager.rs | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/ciphernode/cipher/src/cipher.rs b/packages/ciphernode/cipher/src/cipher.rs index ffe00bb5..3321271f 100644 --- a/packages/ciphernode/cipher/src/cipher.rs +++ b/packages/ciphernode/cipher/src/cipher.rs @@ -16,9 +16,10 @@ use crate::{ }; // ARGON2 PARAMS -const ARGON2_M_COST: u32 = 32 * 1024; // 32 MiB +// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html +const ARGON2_M_COST: u32 = 19 * 1024; // 19 MiB const ARGON2_T_COST: u32 = 2; -const ARGON2_P_COST: u32 = 2; +const ARGON2_P_COST: u32 = 1; const ARGON2_OUTPUT_LEN: usize = 32; const ARGON2_ALGORITHM: Algorithm = Algorithm::Argon2id; const ARGON2_VERSION: Version = Version::V0x13; diff --git a/packages/ciphernode/cipher/src/password_manager.rs b/packages/ciphernode/cipher/src/password_manager.rs index 80170ea2..2298658e 100644 --- a/packages/ciphernode/cipher/src/password_manager.rs +++ b/packages/ciphernode/cipher/src/password_manager.rs @@ -89,6 +89,13 @@ impl FilePasswordManager { #[async_trait] impl PasswordManager for FilePasswordManager { + // We are assuming a secrets manager will mount the secret on the volume. Hence we would expect + // the password to be a string of random characters. + // + // We may setup a system where we create a tool for creating an Argon2id hash as a secret + // clientside and allow the user to upload that although this is something we should talk about + // as it adds a layer of indirection for users trying to configure the node and the benefit + // didn't seem clear. async fn get_key(&self) -> Result>> { let path = &self.path; From 1bbb0636fe8f5939b931954c91985afa1e2680b6 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 17:39:21 +1100 Subject: [PATCH 33/45] Format --- packages/ciphernode/cipher/src/password_manager.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ciphernode/cipher/src/password_manager.rs b/packages/ciphernode/cipher/src/password_manager.rs index 2298658e..fa0cd0d2 100644 --- a/packages/ciphernode/cipher/src/password_manager.rs +++ b/packages/ciphernode/cipher/src/password_manager.rs @@ -90,7 +90,7 @@ impl FilePasswordManager { #[async_trait] impl PasswordManager for FilePasswordManager { // We are assuming a secrets manager will mount the secret on the volume. Hence we would expect - // the password to be a string of random characters. + // the password to be a string of random characters. // // We may setup a system where we create a tool for creating an Argon2id hash as a secret // clientside and allow the user to upload that although this is something we should talk about From 44407766903058a629be94b5c4654755173b47bc Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 17:40:32 +1100 Subject: [PATCH 34/45] Update comment --- packages/ciphernode/cipher/src/password_manager.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/ciphernode/cipher/src/password_manager.rs b/packages/ciphernode/cipher/src/password_manager.rs index fa0cd0d2..95d11e0a 100644 --- a/packages/ciphernode/cipher/src/password_manager.rs +++ b/packages/ciphernode/cipher/src/password_manager.rs @@ -90,12 +90,7 @@ impl FilePasswordManager { #[async_trait] impl PasswordManager for FilePasswordManager { // We are assuming a secrets manager will mount the secret on the volume. Hence we would expect - // the password to be a string of random characters. - // - // We may setup a system where we create a tool for creating an Argon2id hash as a secret - // clientside and allow the user to upload that although this is something we should talk about - // as it adds a layer of indirection for users trying to configure the node and the benefit - // didn't seem clear. + // the password to be a string provided by the user. async fn get_key(&self) -> Result>> { let path = &self.path; From f19caf77ea9362656771d588570d4f8186afaaaa Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 17:43:20 +1100 Subject: [PATCH 35/45] Update comment --- packages/ciphernode/cipher/src/password_manager.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/ciphernode/cipher/src/password_manager.rs b/packages/ciphernode/cipher/src/password_manager.rs index 95d11e0a..7f816195 100644 --- a/packages/ciphernode/cipher/src/password_manager.rs +++ b/packages/ciphernode/cipher/src/password_manager.rs @@ -91,6 +91,10 @@ impl FilePasswordManager { impl PasswordManager for FilePasswordManager { // We are assuming a secrets manager will mount the secret on the volume. Hence we would expect // the password to be a string provided by the user. + // See the following for more info: + // https://docs.docker.com/engine/swarm/secrets/ + // https://kubernetes.io/docs/concepts/configuration/secret/ + // https://developer.hashicorp.com/vault/docs/platform/k8s/injector async fn get_key(&self) -> Result>> { let path = &self.path; From 97638e32d80510dd355854d5913b16386855f103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Mon, 28 Oct 2024 22:06:50 +1100 Subject: [PATCH 36/45] Delete packages/ciphernode/config/src/parsers.rs --- packages/ciphernode/config/src/parsers.rs | 43 ----------------------- 1 file changed, 43 deletions(-) delete mode 100644 packages/ciphernode/config/src/parsers.rs diff --git a/packages/ciphernode/config/src/parsers.rs b/packages/ciphernode/config/src/parsers.rs deleted file mode 100644 index e68831c1..00000000 --- a/packages/ciphernode/config/src/parsers.rs +++ /dev/null @@ -1,43 +0,0 @@ -use alloy::primitives::Address; -use clap::builder::TypedValueParser; - - -#[derive(Clone)] -pub struct EthAddressParser; - -impl TypedValueParser for EthAddressParser { - type Value = Address; - - fn parse_ref( - &self, - cmd: &clap::Command, - arg: Option<&clap::Arg>, - value: &std::ffi::OsStr, - ) -> Result { - let address_str = value.to_str().ok_or_else(|| { - clap::Error::raw( - clap::error::ErrorKind::InvalidUtf8, - "Ethereum address must be valid UTF-8", - ) - })?; - - // Remove '0x' prefix if present - let cleaned_address = address_str.strip_prefix("0x").unwrap_or(address_str); - - // Validate address format - if !cleaned_address.len() == 40 { - return Err(clap::Error::raw( - clap::error::ErrorKind::InvalidValue, - "Ethereum address must be 40 hexadecimal characters", - )); - } - - // Parse the address using alloy - Address::from_str(address_str).map_err(|_| { - clap::Error::raw( - clap::error::ErrorKind::InvalidValue, - "Invalid Ethereum address format", - ) - }) - } -} From e8d06990faefc71a0d1f607de37d6dbbb1dc5930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Mon, 28 Oct 2024 22:32:42 +1100 Subject: [PATCH 37/45] Update rust-ci.yml --- .github/workflows/rust-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index 5a9daa70..1fdf5d12 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -42,6 +42,5 @@ jobs: - name: Run tests run: | - env && cd ./packages/ciphernode/ cargo test From c5d3ad62567491215efadbd95bd6ef77dff24483 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 22:37:56 +1100 Subject: [PATCH 38/45] Woops --- packages/ciphernode/enclave/src/commands/wallet/set.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ciphernode/enclave/src/commands/wallet/set.rs b/packages/ciphernode/enclave/src/commands/wallet/set.rs index 83a5206f..ae6d5773 100644 --- a/packages/ciphernode/enclave/src/commands/wallet/set.rs +++ b/packages/ciphernode/enclave/src/commands/wallet/set.rs @@ -6,7 +6,6 @@ use enclave_core::{EventBus, GetErrors}; use enclave_node::get_repositories; pub async fn execute(config: &AppConfig, input: String) -> Result<()> { - println!("WALLET KEY: {}", input); let cipher = Cipher::from_config(config).await?; let encrypted = cipher.encrypt_data(&mut input.as_bytes().to_vec())?; let bus = EventBus::new(true).start(); From 1b482f2ea3907b99770c40cd1bf4c0a4989f1f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Mon, 28 Oct 2024 23:02:13 +1100 Subject: [PATCH 39/45] Update packages/ciphernode/enclave/src/main.rs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- packages/ciphernode/enclave/src/main.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/ciphernode/enclave/src/main.rs b/packages/ciphernode/enclave/src/main.rs index fbb89333..74ee4500 100644 --- a/packages/ciphernode/enclave/src/main.rs +++ b/packages/ciphernode/enclave/src/main.rs @@ -62,8 +62,9 @@ pub async fn main() { match cli.execute().await { Ok(_) => (), Err(err) => { - println!("{}", err); - println!("There was a problem running. Goodbye") + eprintln!("{}", err); + eprintln!("There was a problem running. Goodbye"); + std::process::exit(1); } } } From d741abdb0081c3f694a97d0bfefba66e6ce44399 Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 23:10:08 +1100 Subject: [PATCH 40/45] Add zeroize --- packages/ciphernode/Cargo.lock | 1 + packages/ciphernode/evm/Cargo.toml | 1 + packages/ciphernode/evm/src/helpers.rs | 8 ++++---- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index a7e2d698..c9e6ecaf 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -2309,6 +2309,7 @@ dependencies = [ "sortition", "tokio", "tracing", + "zeroize", ] [[package]] diff --git a/packages/ciphernode/evm/Cargo.toml b/packages/ciphernode/evm/Cargo.toml index 3224286c..7c3a1cbf 100644 --- a/packages/ciphernode/evm/Cargo.toml +++ b/packages/ciphernode/evm/Cargo.toml @@ -15,3 +15,4 @@ sortition = { path = "../sortition" } cipher = { path = "../cipher" } tokio = { workspace = true } tracing = { workspace = true } +zeroize = { workspace = true } diff --git a/packages/ciphernode/evm/src/helpers.rs b/packages/ciphernode/evm/src/helpers.rs index 77e81eec..2bd89457 100644 --- a/packages/ciphernode/evm/src/helpers.rs +++ b/packages/ciphernode/evm/src/helpers.rs @@ -1,5 +1,3 @@ -use std::{env, sync::Arc}; - use actix::Recipient; use alloy::{ network::{Ethereum, EthereumWallet}, @@ -17,8 +15,10 @@ use cipher::Cipher; use data::Repository; use enclave_core::{BusError, EnclaveErrorType, EnclaveEvent}; use futures_util::stream::StreamExt; +use std::{env, sync::Arc}; use tokio::{select, sync::oneshot}; use tracing::{info, trace}; +use zeroize::Zeroizing; pub async fn stream_from_evm( provider: WithChainId

, @@ -148,9 +148,9 @@ pub async fn get_signer_from_repository( bail!("No private key was found!") }; - let encoded_private_key = cipher.decrypt_data(&private_key_encrypted)?; + let encoded_private_key = Zeroizing::new(cipher.decrypt_data(&private_key_encrypted)?); - let private_key = String::from_utf8(encoded_private_key)?; + let private_key = Zeroizing::new(String::from_utf8(encoded_private_key.to_vec())?); let signer = private_key.parse()?; Ok(Arc::new(signer)) From e079b2d312e8f57df34a4177a8fde8831120a29c Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 23:11:05 +1100 Subject: [PATCH 41/45] remove allocation --- packages/ciphernode/keyshare/src/keyshare.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ciphernode/keyshare/src/keyshare.rs b/packages/ciphernode/keyshare/src/keyshare.rs index c170763f..8b24cece 100644 --- a/packages/ciphernode/keyshare/src/keyshare.rs +++ b/packages/ciphernode/keyshare/src/keyshare.rs @@ -61,7 +61,7 @@ impl Keyshare { fn get_secret(&self) -> Result> { let encrypted = self .secret - .clone() + .as_ref() .ok_or(anyhow!("No secret share available on Keyshare"))?; let decrypted = self.cipher.decrypt_data(&encrypted)?; From 475c20acd308688438c7f77a861747b98287591b Mon Sep 17 00:00:00 2001 From: ktdlr Date: Mon, 28 Oct 2024 23:12:34 +1100 Subject: [PATCH 42/45] add shebang --- tests/basic_integration/lib/clean_folders.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/basic_integration/lib/clean_folders.sh b/tests/basic_integration/lib/clean_folders.sh index ff7e9d22..aecc8380 100755 --- a/tests/basic_integration/lib/clean_folders.sh +++ b/tests/basic_integration/lib/clean_folders.sh @@ -1,3 +1,4 @@ +#!/usr/bin/env sh clean_folders() { local SCRIPT_DIR=$1 From 15d24c6e238d022ec8bc01809af84dc30c931297 Mon Sep 17 00:00:00 2001 From: ryardley Date: Tue, 29 Oct 2024 10:06:24 +1100 Subject: [PATCH 43/45] Add home expansion to normalization --- packages/ciphernode/config/src/app_config.rs | 39 +++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index 80075646..fb1c0576 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -143,9 +143,12 @@ pub fn load_config(config_file: Option<&str>) -> Result { Ok(config) } -/// Utility to normalize paths +// Utility to normalize paths +// We use this so we can avoid using canonicalize() and having to have real files in order to +// manipulate and validate paths: https://doc.rust-lang.org/std/fs/fn.canonicalize.html fn normalize_path(path: impl AsRef) -> PathBuf { - let path = path.as_ref(); + let path = expand_tilde(path.as_ref()); + let mut components = Vec::new(); for component in path.components() { @@ -174,6 +177,28 @@ fn normalize_path(path: impl AsRef) -> PathBuf { result } +fn expand_tilde(path: &Path) -> PathBuf { + let path_str = match path.to_str() { + None => return path.to_path_buf(), + Some(s) => s, + }; + + if !path_str.starts_with('~') { + return path.to_path_buf(); + } + + let home_dir = match env::var("HOME") { + Err(_) => return path.to_path_buf(), + Ok(dir) => dir, + }; + + if path_str.len() == 1 { + PathBuf::from(home_dir) + } else { + PathBuf::from(format!("{}{}", home_dir, &path_str[1..])) + } +} + struct OsDirs; impl OsDirs { pub fn config_dir() -> PathBuf { @@ -190,6 +215,16 @@ mod tests { use super::*; use figment::Jail; + #[test] + fn test_normalization() { + Jail::expect_with(|jail| { + jail.set_env("HOME", "/home/user"); + let path = normalize_path(&PathBuf::from("~/foo/bar/../baz.txt")); + assert_eq!(path, PathBuf::from(format!("/home/user/foo/baz.txt"))); + Ok(()) + }) + } + #[test] fn test_ensure_relative_path() { Jail::expect_with(|jail| { From 7c024375b599161f8fb4d021c27530889c50ebd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=B3=CE=BB?= Date: Tue, 29 Oct 2024 10:19:05 +1100 Subject: [PATCH 44/45] Update tests/basic_integration/lib/clean_folders.sh Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- tests/basic_integration/lib/clean_folders.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/basic_integration/lib/clean_folders.sh b/tests/basic_integration/lib/clean_folders.sh index aecc8380..58a1195e 100755 --- a/tests/basic_integration/lib/clean_folders.sh +++ b/tests/basic_integration/lib/clean_folders.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash clean_folders() { local SCRIPT_DIR=$1 From bede3edf34d43bb8ac06f9b7c8327ecec821c532 Mon Sep 17 00:00:00 2001 From: ryardley Date: Tue, 29 Oct 2024 12:16:44 +1100 Subject: [PATCH 45/45] Fix up blank password validation and remove exit message --- packages/ciphernode/cipher/src/password_manager.rs | 6 +++++- .../enclave/src/commands/password/create.rs | 11 +++++++++-- packages/ciphernode/enclave/src/main.rs | 1 - 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/ciphernode/cipher/src/password_manager.rs b/packages/ciphernode/cipher/src/password_manager.rs index 7f816195..550eadf2 100644 --- a/packages/ciphernode/cipher/src/password_manager.rs +++ b/packages/ciphernode/cipher/src/password_manager.rs @@ -117,9 +117,13 @@ impl PasswordManager for FilePasswordManager { async fn set_key(&mut self, contents: Zeroizing>) -> Result<()> { let path = &self.path; + if contents.len() == 0 { + bail!("Password must contain data!") + } + // Check if file exists if path.exists() { - bail!("Keyfile already exists. Refusing to overwrite."); + bail!("Keyfile already exists. Refusing to overwrite.") } // Create new file with restrictive permissions from the start diff --git a/packages/ciphernode/enclave/src/commands/password/create.rs b/packages/ciphernode/enclave/src/commands/password/create.rs index 227e837b..5af3fa46 100644 --- a/packages/ciphernode/enclave/src/commands/password/create.rs +++ b/packages/ciphernode/enclave/src/commands/password/create.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{bail, Result}; use cipher::{FilePasswordManager, PasswordManager}; use config::AppConfig; use rpassword::prompt_password; @@ -6,6 +6,9 @@ use zeroize::{Zeroize, Zeroizing}; fn get_zeroizing_pw_vec(input: Option) -> Result>> { if let Some(mut pw_str) = input { + if pw_str.trim().is_empty() { + bail!("Password must not be blank") + } let pw = Zeroizing::new(pw_str.trim().as_bytes().to_owned()); pw_str.zeroize(); return Ok(pw); @@ -13,6 +16,10 @@ fn get_zeroizing_pw_vec(input: Option) -> Result>> { // First password entry let mut pw_str = prompt_password("\n\nPlease enter a new password: ")?; + if pw_str.trim().is_empty() { + bail!("Password must not be blank") + } + // Second password entry for confirmation let mut confirm_pw_str = prompt_password("Please confirm your password: ")?; @@ -21,7 +28,7 @@ fn get_zeroizing_pw_vec(input: Option) -> Result>> { // Clean up sensitive data pw_str.zeroize(); confirm_pw_str.zeroize(); - return Err(anyhow::anyhow!("Passwords do not match")); + bail!("Passwords do not match") } let pw = Zeroizing::new(pw_str.trim().as_bytes().to_owned()); diff --git a/packages/ciphernode/enclave/src/main.rs b/packages/ciphernode/enclave/src/main.rs index 74ee4500..923ad9db 100644 --- a/packages/ciphernode/enclave/src/main.rs +++ b/packages/ciphernode/enclave/src/main.rs @@ -63,7 +63,6 @@ pub async fn main() { Ok(_) => (), Err(err) => { eprintln!("{}", err); - eprintln!("There was a problem running. Goodbye"); std::process::exit(1); } }