Simplified the CLI to directly accept a Rhai script path as its primary argument, remove all commands
This commit is contained in:
parent
0890db4810
commit
a86a247180
@ -10,7 +10,7 @@ license = "MIT"
|
|||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "crypto-cli"
|
name = "hero-vault"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -1,772 +0,0 @@
|
|||||||
use colored::Colorize;
|
|
||||||
use std::fs;
|
|
||||||
use std::io::{self, Read, Write};
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::cli::error::{CliError, Result};
|
|
||||||
use crate::cli::{CryptoCommands, EthereumCommands, KeyCommands};
|
|
||||||
use webassembly::core::keypair;
|
|
||||||
use webassembly::core::symmetric;
|
|
||||||
use webassembly::core::ethereum;
|
|
||||||
use webassembly::core::error::CryptoError;
|
|
||||||
|
|
||||||
// Load a key space from disk
|
|
||||||
fn load_key_space(name: &str, password: &str) -> Result<()> {
|
|
||||||
// Load config to get key spaces directory
|
|
||||||
let config = crate::cli::config::Config::default();
|
|
||||||
|
|
||||||
// Check if directory exists
|
|
||||||
if !config.key_spaces_dir.exists() {
|
|
||||||
return Err(CliError::ConfigError("Key spaces directory does not exist".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the key space file path
|
|
||||||
let space_path = config.key_spaces_dir.join(format!("{}.json", name));
|
|
||||||
|
|
||||||
// Check if file exists
|
|
||||||
if !space_path.exists() {
|
|
||||||
return Err(CliError::ConfigError(format!("Key space file not found: {}", space_path.display())));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the file
|
|
||||||
let serialized = fs::read_to_string(&space_path)?;
|
|
||||||
|
|
||||||
// Deserialize the encrypted space
|
|
||||||
let encrypted_space = match symmetric::deserialize_encrypted_space(&serialized) {
|
|
||||||
Ok(space) => space,
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error deserializing key space: {}", e);
|
|
||||||
return Err(CliError::CryptoError(format!("Failed to deserialize key space: {}", e)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Decrypt the space
|
|
||||||
let space = match symmetric::decrypt_key_space(&encrypted_space, password) {
|
|
||||||
Ok(space) => space,
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error decrypting key space: {}", e);
|
|
||||||
return Err(CliError::CryptoError(format!("Failed to decrypt key space: {}", e)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set as current space
|
|
||||||
keypair::set_current_space(space)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute key management commands
|
|
||||||
pub fn execute_key_command(command: &KeyCommands) -> Result<()> {
|
|
||||||
match command {
|
|
||||||
KeyCommands::Load { name, password } => {
|
|
||||||
println!("Loading key space: {}", name);
|
|
||||||
|
|
||||||
// Get password
|
|
||||||
let pwd = match password {
|
|
||||||
Some(p) => p.clone(),
|
|
||||||
None => {
|
|
||||||
println!("Enter password for key space {}:", name);
|
|
||||||
rpassword::read_password()?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load the key space
|
|
||||||
load_key_space(name, &pwd)?;
|
|
||||||
println!("{}", "Key space loaded successfully".green());
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
KeyCommands::CreateSpace { name, password } => {
|
|
||||||
println!("Creating key space: {}", name);
|
|
||||||
|
|
||||||
// Create the key space
|
|
||||||
keypair::create_space(name)?;
|
|
||||||
|
|
||||||
// Get the current space
|
|
||||||
let space = keypair::get_current_space()?;
|
|
||||||
|
|
||||||
// Encrypt the key space with the provided password
|
|
||||||
let encrypted_space = symmetric::encrypt_key_space(&space, &password)?;
|
|
||||||
|
|
||||||
// Load config to get key spaces directory
|
|
||||||
let config = crate::cli::config::Config::default();
|
|
||||||
config.ensure_key_spaces_dir()?;
|
|
||||||
|
|
||||||
// Store the encrypted space to disk
|
|
||||||
let space_path = config.key_spaces_dir.join(format!("{}.json", name));
|
|
||||||
let serialized = symmetric::serialize_encrypted_space(&encrypted_space)?;
|
|
||||||
fs::write(&space_path, serialized)?;
|
|
||||||
|
|
||||||
println!("Key space encrypted with password and saved to {}", space_path.display());
|
|
||||||
println!("{}", "Key space created successfully".green());
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
KeyCommands::ListSpaces => {
|
|
||||||
println!("Listing key spaces:");
|
|
||||||
|
|
||||||
// Load config to get key spaces directory
|
|
||||||
let config = crate::cli::config::Config::default();
|
|
||||||
|
|
||||||
// Check if directory exists
|
|
||||||
if !config.key_spaces_dir.exists() {
|
|
||||||
println!("No key spaces found (directory does not exist)");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// List all space files
|
|
||||||
let mut spaces_found = false;
|
|
||||||
for entry in fs::read_dir(&config.key_spaces_dir)? {
|
|
||||||
let entry = entry?;
|
|
||||||
let path = entry.path();
|
|
||||||
|
|
||||||
if path.is_file() && path.extension().map_or(false, |ext| ext == "json") {
|
|
||||||
if let Some(name) = path.file_stem() {
|
|
||||||
println!("- {}", name.to_string_lossy());
|
|
||||||
spaces_found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !spaces_found {
|
|
||||||
println!("No key spaces found in {}", config.key_spaces_dir.display());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show the current space if it exists
|
|
||||||
match keypair::get_current_space() {
|
|
||||||
Ok(space) => {
|
|
||||||
println!("\nCurrent active space: {}", space.name.green());
|
|
||||||
},
|
|
||||||
Err(_) => {
|
|
||||||
println!("\nNo active key space");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
KeyCommands::CreateKeypair { name, space, password } => {
|
|
||||||
println!("Creating keypair: {}", name);
|
|
||||||
|
|
||||||
// Check if we have an active space
|
|
||||||
if let Err(_) = keypair::get_current_space() {
|
|
||||||
// If space is provided, try to load it
|
|
||||||
if let Some(space_name) = space {
|
|
||||||
// Get password
|
|
||||||
let pwd = match password {
|
|
||||||
Some(p) => p.clone(),
|
|
||||||
None => {
|
|
||||||
println!("Enter password for key space {}:", space_name);
|
|
||||||
rpassword::read_password()?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load the key space
|
|
||||||
load_key_space(&space_name, &pwd)?;
|
|
||||||
println!("Loaded key space: {}", space_name);
|
|
||||||
} else {
|
|
||||||
// Try to load the default space
|
|
||||||
println!("No active key space. Enter the name of the key space to use:");
|
|
||||||
let mut space_name = String::new();
|
|
||||||
io::stdin().read_line(&mut space_name)?;
|
|
||||||
space_name = space_name.trim().to_string();
|
|
||||||
|
|
||||||
println!("Enter password for key space {}:", space_name);
|
|
||||||
let pwd = rpassword::read_password()?;
|
|
||||||
|
|
||||||
// Load the key space
|
|
||||||
load_key_space(&space_name, &pwd)?;
|
|
||||||
println!("Loaded key space: {}", space_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the keypair in the current space
|
|
||||||
keypair::create_keypair(name)?;
|
|
||||||
|
|
||||||
// Save the key space to disk
|
|
||||||
let space = keypair::get_current_space()?;
|
|
||||||
|
|
||||||
// Get the space name
|
|
||||||
let space_name = space.name.clone();
|
|
||||||
|
|
||||||
// Get password
|
|
||||||
let pwd = match password {
|
|
||||||
Some(p) => p.clone(),
|
|
||||||
None => {
|
|
||||||
println!("Enter password for key space {}:", space_name);
|
|
||||||
rpassword::read_password()?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Encrypt the key space
|
|
||||||
let encrypted_space = symmetric::encrypt_key_space(&space, &pwd)?;
|
|
||||||
|
|
||||||
// Load config to get key spaces directory
|
|
||||||
let config = crate::cli::config::Config::default();
|
|
||||||
config.ensure_key_spaces_dir()?;
|
|
||||||
|
|
||||||
// Store the encrypted space to disk
|
|
||||||
let space_path = config.key_spaces_dir.join(format!("{}.json", space_name));
|
|
||||||
let serialized = symmetric::serialize_encrypted_space(&encrypted_space)?;
|
|
||||||
fs::write(&space_path, serialized)?;
|
|
||||||
|
|
||||||
println!("Key space saved to {}", space_path.display());
|
|
||||||
|
|
||||||
println!("{}", "Keypair created successfully".green());
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
KeyCommands::ListKeypairs { space, password } => {
|
|
||||||
println!("Listing keypairs:");
|
|
||||||
|
|
||||||
// Check if we have an active space
|
|
||||||
if let Err(_) = keypair::get_current_space() {
|
|
||||||
// If space is provided, try to load it
|
|
||||||
if let Some(space_name) = space {
|
|
||||||
// Get password
|
|
||||||
let pwd = match password {
|
|
||||||
Some(p) => p.clone(),
|
|
||||||
None => {
|
|
||||||
println!("Enter password for key space {}:", space_name);
|
|
||||||
rpassword::read_password()?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load the key space
|
|
||||||
match load_key_space(&space_name, &pwd) {
|
|
||||||
Ok(_) => println!("Loaded key space: {}", space_name),
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error loading key space: {}", e);
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Try to load the default space
|
|
||||||
println!("No active key space. Enter the name of the key space to use:");
|
|
||||||
let mut space_name = String::new();
|
|
||||||
io::stdin().read_line(&mut space_name)?;
|
|
||||||
space_name = space_name.trim().to_string();
|
|
||||||
|
|
||||||
println!("Enter password for key space {}:", space_name);
|
|
||||||
let pwd = rpassword::read_password()?;
|
|
||||||
|
|
||||||
// Load the key space
|
|
||||||
match load_key_space(&space_name, &pwd) {
|
|
||||||
Ok(_) => println!("Loaded key space: {}", space_name),
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error loading key space: {}", e);
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current space
|
|
||||||
let space = match keypair::get_current_space() {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error getting current key space: {}", e);
|
|
||||||
return Err(CliError::CryptoError(format!("Failed to get current key space: {}", e)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// List keypairs directly from the space
|
|
||||||
let keypairs = space.list_keypairs();
|
|
||||||
if keypairs.is_empty() {
|
|
||||||
println!("No keypairs found");
|
|
||||||
} else {
|
|
||||||
for (i, name) in keypairs.iter().enumerate() {
|
|
||||||
println!("{}. {}", i + 1, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
KeyCommands::Export { name, output, space, password } => {
|
|
||||||
println!("Exporting keypair: {}", name);
|
|
||||||
|
|
||||||
// If space and password are provided, load the key space
|
|
||||||
if let (Some(s), Some(p)) = (space, password) {
|
|
||||||
load_key_space(s, p)?;
|
|
||||||
println!("Loaded key space: {}", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have an active space
|
|
||||||
if let Err(_) = keypair::get_current_space() {
|
|
||||||
return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select the keypair
|
|
||||||
keypair::select_keypair(name)?;
|
|
||||||
|
|
||||||
// Get the keypair
|
|
||||||
let kp = keypair::get_selected_keypair()?;
|
|
||||||
|
|
||||||
// Serialize the keypair
|
|
||||||
let serialized = serde_json::to_string_pretty(&kp)
|
|
||||||
.map_err(|e| CliError::CryptoError(format!("Failed to serialize keypair: {}", e)))?;
|
|
||||||
|
|
||||||
// Output the serialized keypair
|
|
||||||
match output {
|
|
||||||
Some(path) => {
|
|
||||||
fs::write(path, &serialized)?;
|
|
||||||
println!("{}", format!("Keypair exported to {}", path).green());
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
println!("{}", serialized);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
KeyCommands::Import { name, input, space: space_name, password } => {
|
|
||||||
println!("Importing keypair: {}", name);
|
|
||||||
|
|
||||||
// Check if we have an active space
|
|
||||||
if let Err(_) = keypair::get_current_space() {
|
|
||||||
// If space is provided, try to load it
|
|
||||||
if let Some(space_name) = space_name {
|
|
||||||
// Get password
|
|
||||||
let pwd = match password {
|
|
||||||
Some(p) => p.clone(),
|
|
||||||
None => {
|
|
||||||
println!("Enter password for key space {}:", space_name);
|
|
||||||
rpassword::read_password()?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load the key space
|
|
||||||
load_key_space(&space_name, &pwd)?;
|
|
||||||
println!("Loaded key space: {}", space_name);
|
|
||||||
} else {
|
|
||||||
// Try to load the default space
|
|
||||||
println!("No active key space. Enter the name of the key space to use:");
|
|
||||||
let mut space_name = String::new();
|
|
||||||
io::stdin().read_line(&mut space_name)?;
|
|
||||||
space_name = space_name.trim().to_string();
|
|
||||||
|
|
||||||
println!("Enter password for key space {}:", space_name);
|
|
||||||
let pwd = rpassword::read_password()?;
|
|
||||||
|
|
||||||
// Load the key space
|
|
||||||
load_key_space(&space_name, &pwd)?;
|
|
||||||
println!("Loaded key space: {}", space_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get input data
|
|
||||||
let import_data = match input {
|
|
||||||
Some(path) => fs::read_to_string(path)?,
|
|
||||||
None => {
|
|
||||||
println!("Enter keypair data (end with Ctrl+D on Unix or Ctrl+Z on Windows):");
|
|
||||||
let mut buffer = String::new();
|
|
||||||
io::stdin().read_to_string(&mut buffer)?;
|
|
||||||
buffer
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Deserialize the keypair
|
|
||||||
let kp: keypair::KeyPair = serde_json::from_str(&import_data)
|
|
||||||
.map_err(|e| CliError::CryptoError(format!("Failed to deserialize keypair: {}", e)))?;
|
|
||||||
|
|
||||||
// Get the current space
|
|
||||||
let mut space = keypair::get_current_space()?;
|
|
||||||
|
|
||||||
// Add the keypair to the space
|
|
||||||
space.keypairs.insert(name.to_string(), kp);
|
|
||||||
|
|
||||||
// Update the space
|
|
||||||
keypair::set_current_space(space)?;
|
|
||||||
|
|
||||||
// Auto-save the key space
|
|
||||||
let space = keypair::get_current_space()?;
|
|
||||||
let space_name = space.name.clone();
|
|
||||||
|
|
||||||
// Get password
|
|
||||||
let pwd = match password {
|
|
||||||
Some(p) => p.clone(),
|
|
||||||
None => {
|
|
||||||
println!("Enter password for key space {}:", space_name);
|
|
||||||
rpassword::read_password()?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Encrypt the key space
|
|
||||||
let encrypted_space = symmetric::encrypt_key_space(&space, &pwd)?;
|
|
||||||
|
|
||||||
// Load config to get key spaces directory
|
|
||||||
let config = crate::cli::config::Config::default();
|
|
||||||
config.ensure_key_spaces_dir()?;
|
|
||||||
|
|
||||||
// Store the encrypted space to disk
|
|
||||||
let space_path = config.key_spaces_dir.join(format!("{}.json", space_name));
|
|
||||||
let serialized = symmetric::serialize_encrypted_space(&encrypted_space)?;
|
|
||||||
fs::write(&space_path, serialized)?;
|
|
||||||
|
|
||||||
println!("Key space saved to {}", space_path.display());
|
|
||||||
println!("{}", "Keypair imported successfully".green());
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute cryptographic operation commands
|
|
||||||
pub fn execute_crypto_command(command: &CryptoCommands) -> Result<()> {
|
|
||||||
match command {
|
|
||||||
CryptoCommands::Sign { message, input, keypair, output, space, password } => {
|
|
||||||
println!("Signing with keypair: {}", keypair);
|
|
||||||
|
|
||||||
// If space and password are provided, load the key space
|
|
||||||
if let (Some(s), Some(p)) = (space, password) {
|
|
||||||
load_key_space(s, p)?;
|
|
||||||
println!("Loaded key space: {}", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have an active space
|
|
||||||
if let Err(_) = keypair::get_current_space() {
|
|
||||||
return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select the keypair
|
|
||||||
keypair::select_keypair(keypair)?;
|
|
||||||
|
|
||||||
// Get message to sign
|
|
||||||
let msg = match (message, input) {
|
|
||||||
(Some(m), _) => m.clone(),
|
|
||||||
(_, Some(path)) => fs::read_to_string(path)?,
|
|
||||||
_ => {
|
|
||||||
println!("Enter message to sign (end with Ctrl+D on Unix or Ctrl+Z on Windows):");
|
|
||||||
let mut buffer = String::new();
|
|
||||||
io::stdin().read_to_string(&mut buffer)?;
|
|
||||||
buffer
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sign the message
|
|
||||||
let signature_bytes = keypair::keypair_sign(msg.as_bytes())?;
|
|
||||||
|
|
||||||
// Encode the signature as base64
|
|
||||||
let signature = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &signature_bytes);
|
|
||||||
|
|
||||||
// Output signature
|
|
||||||
match output {
|
|
||||||
Some(path) => {
|
|
||||||
fs::write(path, &signature)?;
|
|
||||||
println!("{}", format!("Signature written to {}", path).green());
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
println!("Signature: {}", signature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
CryptoCommands::Verify { message, input, signature, keypair, pubkey, space, password } => {
|
|
||||||
println!("Verifying signature");
|
|
||||||
|
|
||||||
// If space and password are provided, load the key space
|
|
||||||
if let (Some(s), Some(p)) = (space, password) {
|
|
||||||
load_key_space(s, p)?;
|
|
||||||
println!("Loaded key space: {}", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have an active space and a keypair is specified
|
|
||||||
if let Some(kp) = keypair {
|
|
||||||
if let Err(_) = keypair::get_current_space() {
|
|
||||||
return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get message to verify
|
|
||||||
let msg = match (message, input) {
|
|
||||||
(Some(m), _) => m.clone(),
|
|
||||||
(_, Some(path)) => fs::read_to_string(path)?,
|
|
||||||
_ => {
|
|
||||||
println!("Enter message to verify (end with Ctrl+D on Unix or Ctrl+Z on Windows):");
|
|
||||||
let mut buffer = String::new();
|
|
||||||
io::stdin().read_to_string(&mut buffer)?;
|
|
||||||
buffer
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Decode the signature from base64
|
|
||||||
let signature_bytes = match base64::Engine::decode(&base64::engine::general_purpose::STANDARD, signature) {
|
|
||||||
Ok(bytes) => bytes,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(CliError::CryptoError(format!("Invalid signature format: {}", e)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Verify the signature
|
|
||||||
let is_valid = if let Some(kp) = keypair {
|
|
||||||
// Select the keypair and verify
|
|
||||||
keypair::select_keypair(kp)?;
|
|
||||||
keypair::keypair_verify(msg.as_bytes(), &signature_bytes)?
|
|
||||||
} else if let Some(pk) = pubkey {
|
|
||||||
// Decode the public key from base64
|
|
||||||
let pubkey_bytes = match base64::Engine::decode(&base64::engine::general_purpose::STANDARD, pk) {
|
|
||||||
Ok(bytes) => bytes,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(CliError::CryptoError(format!("Invalid public key format: {}", e)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Verify with the public key
|
|
||||||
keypair::verify_with_public_key(&pubkey_bytes, msg.as_bytes(), &signature_bytes)?
|
|
||||||
} else {
|
|
||||||
// Use the currently selected keypair
|
|
||||||
keypair::keypair_verify(msg.as_bytes(), &signature_bytes)?
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_valid {
|
|
||||||
println!("{}", "Signature is valid".green());
|
|
||||||
} else {
|
|
||||||
println!("{}", "Signature is invalid".red());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
CryptoCommands::Encrypt { data, input, recipient, output, space, password } => {
|
|
||||||
println!("Encrypting for recipient: {}", recipient);
|
|
||||||
|
|
||||||
// If space and password are provided, load the key space
|
|
||||||
if let (Some(s), Some(p)) = (space, password) {
|
|
||||||
load_key_space(s, p)?;
|
|
||||||
println!("Loaded key space: {}", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have an active space
|
|
||||||
if let Err(_) = keypair::get_current_space() {
|
|
||||||
return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get data to encrypt
|
|
||||||
let plaintext = match (data, input) {
|
|
||||||
(Some(d), _) => d.clone(),
|
|
||||||
(_, Some(path)) => fs::read_to_string(path)?,
|
|
||||||
_ => {
|
|
||||||
println!("Enter data to encrypt (end with Ctrl+D on Unix or Ctrl+Z on Windows):");
|
|
||||||
let mut buffer = String::new();
|
|
||||||
io::stdin().read_to_string(&mut buffer)?;
|
|
||||||
buffer
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get the recipient's public key
|
|
||||||
// For now, we'll assume the recipient is a keypair name
|
|
||||||
keypair::select_keypair(&recipient)?;
|
|
||||||
let recipient_pubkey = keypair::keypair_pub_key()?;
|
|
||||||
|
|
||||||
// Encrypt the data
|
|
||||||
let ciphertext_bytes = keypair::encrypt_asymmetric(&recipient_pubkey, plaintext.as_bytes())?;
|
|
||||||
|
|
||||||
// Encode the ciphertext as base64
|
|
||||||
let ciphertext = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &ciphertext_bytes);
|
|
||||||
|
|
||||||
// Output ciphertext
|
|
||||||
match output {
|
|
||||||
Some(path) => {
|
|
||||||
fs::write(path, &ciphertext)?;
|
|
||||||
println!("{}", format!("Encrypted data written to {}", path).green());
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
println!("Encrypted data: {}", ciphertext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
CryptoCommands::Decrypt { data, input, keypair, output, space, password } => {
|
|
||||||
println!("Decrypting with keypair: {}", keypair);
|
|
||||||
|
|
||||||
// If space and password are provided, load the key space
|
|
||||||
if let (Some(s), Some(p)) = (space, password) {
|
|
||||||
load_key_space(s, p)?;
|
|
||||||
println!("Loaded key space: {}", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have an active space
|
|
||||||
if let Err(_) = keypair::get_current_space() {
|
|
||||||
return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select the keypair
|
|
||||||
keypair::select_keypair(keypair)?;
|
|
||||||
|
|
||||||
// Get data to decrypt
|
|
||||||
let ciphertext = match (data, input) {
|
|
||||||
(Some(d), _) => d.clone(),
|
|
||||||
(_, Some(path)) => fs::read_to_string(path)?,
|
|
||||||
_ => {
|
|
||||||
println!("Enter data to decrypt (end with Ctrl+D on Unix or Ctrl+Z on Windows):");
|
|
||||||
let mut buffer = String::new();
|
|
||||||
io::stdin().read_to_string(&mut buffer)?;
|
|
||||||
buffer
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Decode the ciphertext from base64
|
|
||||||
let ciphertext_bytes = match base64::Engine::decode(&base64::engine::general_purpose::STANDARD, &ciphertext) {
|
|
||||||
Ok(bytes) => bytes,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(CliError::CryptoError(format!("Invalid ciphertext format: {}", e)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Decrypt the data
|
|
||||||
let plaintext_bytes = keypair::decrypt_asymmetric(&ciphertext_bytes)?;
|
|
||||||
|
|
||||||
// Convert the plaintext to a string
|
|
||||||
let plaintext = match String::from_utf8(plaintext_bytes) {
|
|
||||||
Ok(text) => text,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(CliError::CryptoError(format!("Invalid UTF-8 in decrypted data: {}", e)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Output plaintext
|
|
||||||
match output {
|
|
||||||
Some(path) => {
|
|
||||||
fs::write(path, &plaintext)?;
|
|
||||||
println!("{}", format!("Decrypted data written to {}", path).green());
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
println!("Decrypted data: {}", plaintext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute Ethereum wallet commands
|
|
||||||
pub fn execute_ethereum_command(command: &EthereumCommands) -> Result<()> {
|
|
||||||
match command {
|
|
||||||
EthereumCommands::Create { keypair, space, password } => {
|
|
||||||
println!("Creating Ethereum wallet from keypair: {}", keypair);
|
|
||||||
|
|
||||||
// If space and password are provided, load the key space
|
|
||||||
if let (Some(s), Some(p)) = (space, password) {
|
|
||||||
load_key_space(s, p)?;
|
|
||||||
println!("Loaded key space: {}", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have an active space
|
|
||||||
if let Err(_) = keypair::get_current_space() {
|
|
||||||
return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select the keypair
|
|
||||||
keypair::select_keypair(keypair)?;
|
|
||||||
|
|
||||||
// Create the Ethereum wallet
|
|
||||||
let wallet = ethereum::create_ethereum_wallet()?;
|
|
||||||
|
|
||||||
println!("{}", "Ethereum wallet created successfully".green());
|
|
||||||
println!("Address: {}", wallet.address_string());
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
EthereumCommands::Address { keypair, space, password } => {
|
|
||||||
println!("Getting Ethereum address for keypair: {}", keypair);
|
|
||||||
|
|
||||||
// If space and password are provided, load the key space
|
|
||||||
if let (Some(s), Some(p)) = (space, password) {
|
|
||||||
load_key_space(s, p)?;
|
|
||||||
println!("Loaded key space: {}", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have an active space
|
|
||||||
if let Err(_) = keypair::get_current_space() {
|
|
||||||
return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select the keypair
|
|
||||||
keypair::select_keypair(keypair)?;
|
|
||||||
|
|
||||||
// Get the Ethereum address
|
|
||||||
match ethereum::get_current_ethereum_wallet() {
|
|
||||||
Ok(wallet) => {
|
|
||||||
println!("Ethereum address: {}", wallet.address_string());
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
// If no wallet exists, create one
|
|
||||||
if let CryptoError::NoKeypairSelected = e {
|
|
||||||
println!("No Ethereum wallet found for this keypair. Creating one...");
|
|
||||||
let wallet = ethereum::create_ethereum_wallet()?;
|
|
||||||
println!("Ethereum address: {}", wallet.address_string());
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(e.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
EthereumCommands::Balance { address, network, space, password } => {
|
|
||||||
// If space and password are provided, load the key space
|
|
||||||
if let (Some(s), Some(p)) = (space, password) {
|
|
||||||
load_key_space(s, p)?;
|
|
||||||
println!("Loaded key space: {}", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have an active space
|
|
||||||
if let Err(_) = keypair::get_current_space() {
|
|
||||||
return Err(CliError::CryptoError("No active key space. Please use 'key load' command first or provide --space and --password".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let addr = match address {
|
|
||||||
Some(a) => a.clone(),
|
|
||||||
None => {
|
|
||||||
// Try to get the address from the current wallet
|
|
||||||
match ethereum::get_current_ethereum_wallet() {
|
|
||||||
Ok(wallet) => wallet.address_string(),
|
|
||||||
Err(_) => {
|
|
||||||
println!("Enter Ethereum address:");
|
|
||||||
let mut buffer = String::new();
|
|
||||||
io::stdin().read_line(&mut buffer)?;
|
|
||||||
buffer.trim().to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("Getting balance for address {} on network {}", addr, network);
|
|
||||||
|
|
||||||
// Create provider based on network
|
|
||||||
let provider = match network.to_lowercase().as_str() {
|
|
||||||
"gnosis" => ethereum::create_gnosis_provider()?,
|
|
||||||
_ => {
|
|
||||||
return Err(CliError::CryptoError(format!("Unsupported network: {}", network)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the address
|
|
||||||
let eth_address = addr.parse::<ethers::types::Address>()
|
|
||||||
.map_err(|_| CliError::CryptoError("Invalid Ethereum address".to_string()))?;
|
|
||||||
|
|
||||||
// Get the balance
|
|
||||||
let rt = tokio::runtime::Runtime::new()
|
|
||||||
.map_err(|e| CliError::IoError(format!("Failed to create runtime: {}", e)))?;
|
|
||||||
|
|
||||||
let balance = rt.block_on(ethereum::get_balance(&provider, eth_address))?;
|
|
||||||
|
|
||||||
// Format the balance
|
|
||||||
let formatted_balance = ethereum::format_eth_balance(balance);
|
|
||||||
|
|
||||||
println!("Balance: {}", formatted_balance);
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to read file contents
|
|
||||||
fn read_file<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
|
|
||||||
let mut file = fs::File::open(path)?;
|
|
||||||
let mut buffer = Vec::new();
|
|
||||||
file.read_to_end(&mut buffer)?;
|
|
||||||
Ok(buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to write file contents
|
|
||||||
fn write_file<P: AsRef<Path>>(path: P, data: &[u8]) -> io::Result<()> {
|
|
||||||
let mut file = fs::File::create(path)?;
|
|
||||||
file.write_all(data)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -14,7 +14,7 @@ pub struct Config {
|
|||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||||
let key_spaces_dir = home_dir.join(".crypto-cli").join("key-spaces");
|
let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces");
|
||||||
|
|
||||||
Config {
|
Config {
|
||||||
default_key_space: None,
|
default_key_space: None,
|
||||||
@ -30,7 +30,7 @@ impl Config {
|
|||||||
Some(p) => PathBuf::from(p.as_ref()),
|
Some(p) => PathBuf::from(p.as_ref()),
|
||||||
None => {
|
None => {
|
||||||
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||||
home_dir.join(".crypto-cli").join("config.json")
|
home_dir.join(".hero-vault").join("config.json")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ impl Config {
|
|||||||
Some(p) => PathBuf::from(p.as_ref()),
|
Some(p) => PathBuf::from(p.as_ref()),
|
||||||
None => {
|
None => {
|
||||||
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||||
let config_dir = home_dir.join(".crypto-cli");
|
let config_dir = home_dir.join(".hero-vault");
|
||||||
|
|
||||||
if !config_dir.exists() {
|
if !config_dir.exists() {
|
||||||
fs::create_dir_all(&config_dir)
|
fs::create_dir_all(&config_dir)
|
||||||
|
244
src/cli/mod.rs
244
src/cli/mod.rs
@ -1,16 +1,14 @@
|
|||||||
pub mod commands;
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod shell;
|
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::Parser;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "crypto-cli")]
|
#[command(name = "hero-vault")]
|
||||||
#[command(about = "Cryptographic operations CLI with Rhai scripting support", long_about = None)]
|
#[command(about = "Cryptographic operations CLI with Rhai scripting support", long_about = None)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
#[command(subcommand)]
|
/// Path to Rhai script file to execute
|
||||||
pub command: Commands,
|
pub script_path: String,
|
||||||
|
|
||||||
#[arg(short, long, help = "Enable verbose output")]
|
#[arg(short, long, help = "Enable verbose output")]
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
@ -18,237 +16,3 @@ pub struct Cli {
|
|||||||
#[arg(short, long, help = "Config file path")]
|
#[arg(short, long, help = "Config file path")]
|
||||||
pub config: Option<String>,
|
pub config: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub enum Commands {
|
|
||||||
/// Key management commands
|
|
||||||
Key {
|
|
||||||
#[command(subcommand)]
|
|
||||||
command: KeyCommands,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Encryption/decryption commands
|
|
||||||
Crypto {
|
|
||||||
#[command(subcommand)]
|
|
||||||
command: CryptoCommands,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Ethereum wallet commands
|
|
||||||
Ethereum {
|
|
||||||
#[command(subcommand)]
|
|
||||||
command: EthereumCommands,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Execute Rhai script
|
|
||||||
Script {
|
|
||||||
#[arg(help = "Path to Rhai script file")]
|
|
||||||
path: Option<String>,
|
|
||||||
|
|
||||||
#[arg(short, long, help = "Execute script from string")]
|
|
||||||
inline: Option<String>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Interactive shell
|
|
||||||
Shell,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define subcommands for each category
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub enum KeyCommands {
|
|
||||||
/// Create a new key space
|
|
||||||
CreateSpace {
|
|
||||||
/// Name of the key space
|
|
||||||
name: String,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Password to encrypt the key space (required)
|
|
||||||
password: String
|
|
||||||
},
|
|
||||||
/// List available key spaces
|
|
||||||
ListSpaces,
|
|
||||||
/// Load a key space
|
|
||||||
Load {
|
|
||||||
/// Name of the key space to load
|
|
||||||
name: String,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Password to decrypt the key space
|
|
||||||
password: Option<String>
|
|
||||||
},
|
|
||||||
/// Create a new keypair in the current key space
|
|
||||||
CreateKeypair {
|
|
||||||
/// Name of the keypair
|
|
||||||
name: String,
|
|
||||||
#[arg(long)]
|
|
||||||
space: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
password: Option<String>
|
|
||||||
},
|
|
||||||
/// List keypairs in the current key space
|
|
||||||
ListKeypairs {
|
|
||||||
#[arg(long)]
|
|
||||||
/// Key space to list keypairs from
|
|
||||||
space: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Password to decrypt the key space
|
|
||||||
password: Option<String>
|
|
||||||
},
|
|
||||||
/// Export a keypair
|
|
||||||
Export {
|
|
||||||
/// Name of the keypair to export
|
|
||||||
name: String,
|
|
||||||
/// Output file path (prints to stdout if not specified)
|
|
||||||
output: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Key space containing the keypair
|
|
||||||
space: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Password to decrypt the key space
|
|
||||||
password: Option<String>
|
|
||||||
},
|
|
||||||
/// Import a keypair
|
|
||||||
Import {
|
|
||||||
/// Name to give the imported keypair
|
|
||||||
name: String,
|
|
||||||
/// Input file path (reads from stdin if not specified)
|
|
||||||
input: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Key space to import the keypair into
|
|
||||||
space: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Password to decrypt the key space
|
|
||||||
password: Option<String>
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub enum CryptoCommands {
|
|
||||||
/// Sign a message with a keypair
|
|
||||||
Sign {
|
|
||||||
#[arg(long)]
|
|
||||||
/// Message to sign (as a string)
|
|
||||||
message: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Input file containing the message to sign
|
|
||||||
input: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Name of the keypair to use for signing
|
|
||||||
keypair: String,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Output file for the signature (prints to stdout if not specified)
|
|
||||||
output: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Key space containing the keypair
|
|
||||||
space: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Password to decrypt the key space
|
|
||||||
password: Option<String>
|
|
||||||
},
|
|
||||||
/// Verify a signature
|
|
||||||
Verify {
|
|
||||||
#[arg(long)]
|
|
||||||
/// Message to verify (as a string)
|
|
||||||
message: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Input file containing the message to verify
|
|
||||||
input: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Signature to verify (base64 encoded)
|
|
||||||
signature: String,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Name of the keypair to use for verification
|
|
||||||
keypair: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Public key to use for verification (base64 encoded)
|
|
||||||
pubkey: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Key space containing the keypair
|
|
||||||
space: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Password to decrypt the key space
|
|
||||||
password: Option<String>
|
|
||||||
},
|
|
||||||
/// Encrypt data for a recipient
|
|
||||||
Encrypt {
|
|
||||||
#[arg(long)]
|
|
||||||
/// Data to encrypt (as a string)
|
|
||||||
data: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Input file containing the data to encrypt
|
|
||||||
input: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Name of the recipient keypair
|
|
||||||
recipient: String,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Output file for the encrypted data (prints to stdout if not specified)
|
|
||||||
output: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Key space containing the keypair
|
|
||||||
space: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Password to decrypt the key space
|
|
||||||
password: Option<String>
|
|
||||||
},
|
|
||||||
/// Decrypt data with a keypair
|
|
||||||
Decrypt {
|
|
||||||
#[arg(long)]
|
|
||||||
/// Data to decrypt (as a string, base64 encoded)
|
|
||||||
data: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Input file containing the data to decrypt
|
|
||||||
input: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Name of the keypair to use for decryption
|
|
||||||
keypair: String,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Output file for the decrypted data (prints to stdout if not specified)
|
|
||||||
output: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Key space containing the keypair
|
|
||||||
space: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Password to decrypt the key space
|
|
||||||
password: Option<String>
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub enum EthereumCommands {
|
|
||||||
/// Create an Ethereum wallet from a keypair
|
|
||||||
Create {
|
|
||||||
#[arg(long)]
|
|
||||||
/// Name of the keypair to use
|
|
||||||
keypair: String,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Key space containing the keypair
|
|
||||||
space: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Password to decrypt the key space
|
|
||||||
password: Option<String>
|
|
||||||
},
|
|
||||||
/// Get the Ethereum address for a keypair
|
|
||||||
Address {
|
|
||||||
#[arg(long)]
|
|
||||||
/// Name of the keypair
|
|
||||||
keypair: String,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Key space containing the keypair
|
|
||||||
space: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Password to decrypt the key space
|
|
||||||
password: Option<String>
|
|
||||||
},
|
|
||||||
/// Get the balance of an Ethereum address
|
|
||||||
Balance {
|
|
||||||
#[arg(long)]
|
|
||||||
/// Ethereum address (uses the current wallet if not specified)
|
|
||||||
address: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Network to use (e.g., "gnosis")
|
|
||||||
network: String,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Key space containing the keypair
|
|
||||||
space: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
/// Password to decrypt the key space
|
|
||||||
password: Option<String>
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
284
src/cli/shell.rs
284
src/cli/shell.rs
@ -1,284 +0,0 @@
|
|||||||
use colored::Colorize;
|
|
||||||
use rustyline::error::ReadlineError;
|
|
||||||
use rustyline::DefaultEditor;
|
|
||||||
use std::process;
|
|
||||||
|
|
||||||
use crate::cli::commands::{execute_crypto_command, execute_ethereum_command, execute_key_command};
|
|
||||||
use crate::cli::error::{CliError, Result};
|
|
||||||
use crate::cli::{CryptoCommands, EthereumCommands, KeyCommands};
|
|
||||||
|
|
||||||
pub fn run_interactive_shell() -> Result<()> {
|
|
||||||
println!("{}", "Crypto CLI Interactive Shell".green().bold());
|
|
||||||
println!("Type 'help' for a list of commands, 'exit' to quit");
|
|
||||||
|
|
||||||
let mut rl = DefaultEditor::new().map_err(|e| CliError::IoError(e.to_string()))?;
|
|
||||||
|
|
||||||
if rl.load_history("history.txt").is_err() {
|
|
||||||
println!("No previous history.");
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let readline = rl.readline("crypto> ");
|
|
||||||
match readline {
|
|
||||||
Ok(line) => {
|
|
||||||
rl.add_history_entry(line.as_str());
|
|
||||||
|
|
||||||
let line = line.trim();
|
|
||||||
if line.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
match line {
|
|
||||||
"exit" | "quit" => {
|
|
||||||
println!("Goodbye!");
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
"help" => {
|
|
||||||
print_help();
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
if let Err(e) = process_command(line) {
|
|
||||||
println!("{}: {}", "Error".red().bold(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(ReadlineError::Interrupted) => {
|
|
||||||
println!("CTRL-C");
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
Err(ReadlineError::Eof) => {
|
|
||||||
println!("CTRL-D");
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
println!("Error: {:?}", err);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rl.save_history("history.txt").map_err(|e| CliError::IoError(e.to_string()))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_help() {
|
|
||||||
println!("{}", "Available Commands:".green().bold());
|
|
||||||
println!(" {}", "Key Management:".yellow());
|
|
||||||
println!(" key create-space <name> [password]");
|
|
||||||
println!(" key list-spaces");
|
|
||||||
println!(" key load <name> [password]");
|
|
||||||
println!(" key create-keypair <name>");
|
|
||||||
println!(" key list-keypairs");
|
|
||||||
println!(" key export <name> [output-file]");
|
|
||||||
println!(" key import <name> [input-file]");
|
|
||||||
|
|
||||||
println!(" {}", "Cryptographic Operations:".yellow());
|
|
||||||
println!(" crypto sign <keypair> <message> [output-file]");
|
|
||||||
println!(" crypto verify <signature> <message> [keypair]");
|
|
||||||
println!(" crypto encrypt <recipient> <data> [output-file]");
|
|
||||||
println!(" crypto decrypt <keypair> <data> [output-file]");
|
|
||||||
|
|
||||||
println!(" {}", "Ethereum Operations:".yellow());
|
|
||||||
println!(" eth create <keypair>");
|
|
||||||
println!(" eth address <keypair>");
|
|
||||||
println!(" eth balance <address> <network>");
|
|
||||||
|
|
||||||
println!(" {}", "General:".yellow());
|
|
||||||
println!(" help - Show this help message");
|
|
||||||
println!(" exit - Exit the shell");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_command(cmd: &str) -> Result<()> {
|
|
||||||
let parts: Vec<&str> = cmd.split_whitespace().collect();
|
|
||||||
|
|
||||||
if parts.is_empty() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
match parts[0] {
|
|
||||||
"key" => {
|
|
||||||
if parts.len() < 2 {
|
|
||||||
println!("Missing key subcommand. Try 'help' for a list of commands.");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
match parts[1] {
|
|
||||||
"create-space" => {
|
|
||||||
if parts.len() < 3 {
|
|
||||||
println!("Missing space name. Usage: key create-space <name> [password]");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = parts[2].to_string();
|
|
||||||
let password = if parts.len() > 3 { parts[3].to_string() } else { String::new() };
|
|
||||||
|
|
||||||
execute_key_command(&KeyCommands::CreateSpace { name, password })
|
|
||||||
},
|
|
||||||
"list-spaces" => {
|
|
||||||
execute_key_command(&KeyCommands::ListSpaces)
|
|
||||||
},
|
|
||||||
"load" => {
|
|
||||||
if parts.len() < 3 {
|
|
||||||
println!("Missing space name. Usage: key load <name> [password]");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = parts[2].to_string();
|
|
||||||
let password = if parts.len() > 3 { Some(parts[3].to_string()) } else { None };
|
|
||||||
|
|
||||||
execute_key_command(&KeyCommands::Load { name, password })
|
|
||||||
},
|
|
||||||
"create-keypair" => {
|
|
||||||
if parts.len() < 3 {
|
|
||||||
println!("Missing keypair name. Usage: key create-keypair <name>");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = parts[2].to_string();
|
|
||||||
|
|
||||||
execute_key_command(&KeyCommands::CreateKeypair { name, space: None, password: None })
|
|
||||||
},
|
|
||||||
"list-keypairs" => {
|
|
||||||
execute_key_command(&KeyCommands::ListKeypairs { space: None, password: None })
|
|
||||||
},
|
|
||||||
"export" => {
|
|
||||||
if parts.len() < 3 {
|
|
||||||
println!("Missing keypair name. Usage: key export <name> [output-file]");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = parts[2].to_string();
|
|
||||||
let output = if parts.len() > 3 { Some(parts[3].to_string()) } else { None };
|
|
||||||
|
|
||||||
execute_key_command(&KeyCommands::Export { name, output, space: None, password: None })
|
|
||||||
},
|
|
||||||
"import" => {
|
|
||||||
if parts.len() < 3 {
|
|
||||||
println!("Missing keypair name. Usage: key import <name> [input-file]");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = parts[2].to_string();
|
|
||||||
let input = if parts.len() > 3 { Some(parts[3].to_string()) } else { None };
|
|
||||||
|
|
||||||
execute_key_command(&KeyCommands::Import { name, input, space: None, password: None })
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
println!("Unknown key subcommand: {}. Try 'help' for a list of commands.", parts[1]);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"crypto" => {
|
|
||||||
if parts.len() < 2 {
|
|
||||||
println!("Missing crypto subcommand. Try 'help' for a list of commands.");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
match parts[1] {
|
|
||||||
"sign" => {
|
|
||||||
if parts.len() < 4 {
|
|
||||||
println!("Missing arguments. Usage: crypto sign <keypair> <message> [output-file]");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let keypair = parts[2].to_string();
|
|
||||||
let message = Some(parts[3].to_string());
|
|
||||||
let output = if parts.len() > 4 { Some(parts[4].to_string()) } else { None };
|
|
||||||
|
|
||||||
execute_crypto_command(&CryptoCommands::Sign { message, input: None, keypair, output, space: None, password: None })
|
|
||||||
},
|
|
||||||
"verify" => {
|
|
||||||
if parts.len() < 4 {
|
|
||||||
println!("Missing arguments. Usage: crypto verify <signature> <message> [keypair]");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let signature = parts[2].to_string();
|
|
||||||
let message = Some(parts[3].to_string());
|
|
||||||
let keypair = if parts.len() > 4 { Some(parts[4].to_string()) } else { None };
|
|
||||||
|
|
||||||
execute_crypto_command(&CryptoCommands::Verify { message, input: None, signature, keypair, pubkey: None, space: None, password: None })
|
|
||||||
},
|
|
||||||
"encrypt" => {
|
|
||||||
if parts.len() < 4 {
|
|
||||||
println!("Missing arguments. Usage: crypto encrypt <recipient> <data> [output-file]");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let recipient = parts[2].to_string();
|
|
||||||
let data = Some(parts[3].to_string());
|
|
||||||
let output = if parts.len() > 4 { Some(parts[4].to_string()) } else { None };
|
|
||||||
|
|
||||||
execute_crypto_command(&CryptoCommands::Encrypt { data, input: None, recipient, output, space: None, password: None })
|
|
||||||
},
|
|
||||||
"decrypt" => {
|
|
||||||
if parts.len() < 4 {
|
|
||||||
println!("Missing arguments. Usage: crypto decrypt <keypair> <data> [output-file]");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let keypair = parts[2].to_string();
|
|
||||||
let data = Some(parts[3].to_string());
|
|
||||||
let output = if parts.len() > 4 { Some(parts[4].to_string()) } else { None };
|
|
||||||
|
|
||||||
execute_crypto_command(&CryptoCommands::Decrypt { data, input: None, keypair, output, space: None, password: None })
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
println!("Unknown crypto subcommand: {}. Try 'help' for a list of commands.", parts[1]);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"eth" => {
|
|
||||||
if parts.len() < 2 {
|
|
||||||
println!("Missing eth subcommand. Try 'help' for a list of commands.");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
match parts[1] {
|
|
||||||
"create" => {
|
|
||||||
if parts.len() < 3 {
|
|
||||||
println!("Missing keypair name. Usage: eth create <keypair>");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let keypair = parts[2].to_string();
|
|
||||||
|
|
||||||
execute_ethereum_command(&EthereumCommands::Create { keypair, space: None, password: None })
|
|
||||||
},
|
|
||||||
"address" => {
|
|
||||||
if parts.len() < 3 {
|
|
||||||
println!("Missing keypair name. Usage: eth address <keypair>");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let keypair = parts[2].to_string();
|
|
||||||
|
|
||||||
execute_ethereum_command(&EthereumCommands::Address { keypair, space: None, password: None })
|
|
||||||
},
|
|
||||||
"balance" => {
|
|
||||||
if parts.len() < 4 {
|
|
||||||
println!("Missing arguments. Usage: eth balance <address> <network>");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let address = Some(parts[2].to_string());
|
|
||||||
let network = parts[3].to_string();
|
|
||||||
|
|
||||||
execute_ethereum_command(&EthereumCommands::Balance { address, network, space: None, password: None })
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
println!("Unknown eth subcommand: {}. Try 'help' for a list of commands.", parts[1]);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
println!("Unknown command: {}. Try 'help' for a list of commands.", parts[0]);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
36
src/main.rs
36
src/main.rs
@ -1,5 +1,4 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use colored::Colorize;
|
|
||||||
use env_logger::Builder;
|
use env_logger::Builder;
|
||||||
use log::{info, LevelFilter};
|
use log::{info, LevelFilter};
|
||||||
|
|
||||||
@ -9,7 +8,7 @@ extern crate webassembly;
|
|||||||
mod cli;
|
mod cli;
|
||||||
mod scripting;
|
mod scripting;
|
||||||
|
|
||||||
use cli::{Cli, Commands};
|
use cli::Cli;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
// Parse command line arguments
|
// Parse command line arguments
|
||||||
@ -24,35 +23,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
builder.init();
|
builder.init();
|
||||||
|
|
||||||
// Execute the appropriate command
|
// Initialize script engine
|
||||||
match &cli.command {
|
let mut engine = scripting::ScriptEngine::new();
|
||||||
Commands::Key { command } => {
|
|
||||||
cli::commands::execute_key_command(command)?;
|
|
||||||
},
|
|
||||||
Commands::Crypto { command } => {
|
|
||||||
cli::commands::execute_crypto_command(command)?;
|
|
||||||
},
|
|
||||||
Commands::Ethereum { command } => {
|
|
||||||
cli::commands::execute_ethereum_command(command)?;
|
|
||||||
},
|
|
||||||
Commands::Script { path, inline } => {
|
|
||||||
let mut engine = scripting::ScriptEngine::new();
|
|
||||||
|
|
||||||
if let Some(script_path) = path {
|
// Execute the script
|
||||||
info!("Executing script from file: {}", script_path);
|
info!("Executing script from file: {}", cli.script_path);
|
||||||
engine.eval_file(script_path)?;
|
engine.eval_file(&cli.script_path)?;
|
||||||
} else if let Some(script) = inline {
|
|
||||||
info!("Executing inline script");
|
|
||||||
engine.eval(script)?;
|
|
||||||
} else {
|
|
||||||
println!("Error: No script provided");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Commands::Shell => {
|
|
||||||
cli::shell::run_interactive_shell()?;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ use std::path::PathBuf;
|
|||||||
fn load_key_space(name: &str, password: &str) -> bool {
|
fn load_key_space(name: &str, password: &str) -> bool {
|
||||||
// Get the key spaces directory from config
|
// Get the key spaces directory from config
|
||||||
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||||
let key_spaces_dir = home_dir.join(".crypto-cli").join("key-spaces");
|
let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces");
|
||||||
|
|
||||||
// Check if directory exists
|
// Check if directory exists
|
||||||
if !key_spaces_dir.exists() {
|
if !key_spaces_dir.exists() {
|
||||||
@ -93,7 +93,7 @@ fn create_key_space(name: &str, password: &str) -> bool {
|
|||||||
|
|
||||||
// Get the key spaces directory
|
// Get the key spaces directory
|
||||||
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||||
let key_spaces_dir = home_dir.join(".crypto-cli").join("key-spaces");
|
let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces");
|
||||||
|
|
||||||
// Create directory if it doesn't exist
|
// Create directory if it doesn't exist
|
||||||
if !key_spaces_dir.exists() {
|
if !key_spaces_dir.exists() {
|
||||||
@ -156,7 +156,7 @@ fn auto_save_key_space(password: &str) -> bool {
|
|||||||
|
|
||||||
// Get the key spaces directory
|
// Get the key spaces directory
|
||||||
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||||
let key_spaces_dir = home_dir.join(".crypto-cli").join("key-spaces");
|
let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces");
|
||||||
|
|
||||||
// Create directory if it doesn't exist
|
// Create directory if it doesn't exist
|
||||||
if !key_spaces_dir.exists() {
|
if !key_spaces_dir.exists() {
|
||||||
|
Loading…
Reference in New Issue
Block a user