/ crates / acdc-wizard / src / keys.rs
keys.rs
  1  //! Key generation and management.
  2  
  3  use acdc_core::{config::NodeInstance, Error, Result};
  4  use acdc_tui::output;
  5  use std::fs;
  6  use std::path::Path;
  7  use std::process::Command;
  8  use tracing::debug;
  9  
 10  /// Key file names.
 11  pub const PRIVATE_KEY_FILE: &str = "private.key";
 12  pub const PUBLIC_KEY_FILE: &str = "public.key";
 13  pub const ADDRESS_FILE: &str = "address";
 14  pub const VALIDATOR_KEY_FILE: &str = "validator.key";
 15  
 16  /// Generate keys for a node instance.
 17  pub fn generate_keys(instance: &NodeInstance) -> Result<bool> {
 18      let keys_path = Path::new(&instance.keys.path);
 19  
 20      // Create keys directory
 21      fs::create_dir_all(keys_path).map_err(|e| Error::Io(e))?;
 22  
 23      output::status("Generating cryptographic keys...");
 24  
 25      // Check if keys already exist
 26      let private_key_path = keys_path.join(PRIVATE_KEY_FILE);
 27      if private_key_path.exists() {
 28          output::warning("Keys already exist, skipping generation");
 29          return Ok(false);
 30      }
 31  
 32      // Generate account key pair
 33      // In production, this would call the ADNet binary or use the crypto library
 34      // For now, generate placeholder keys for testing
 35      generate_account_keys(keys_path)?;
 36  
 37      // For validators, also generate validator-specific keys
 38      if matches!(instance.role, acdc_core::NodeRole::Validator) {
 39          generate_validator_keys(keys_path)?;
 40      }
 41  
 42      output::success("Keys generated successfully");
 43      output::warning("IMPORTANT: Back up your keys! They cannot be recovered if lost.");
 44  
 45      Ok(true)
 46  }
 47  
 48  /// Generate account keys (private key, public key, address).
 49  fn generate_account_keys(keys_path: &Path) -> Result<()> {
 50      debug!("Generating account keys in {:?}", keys_path);
 51  
 52      // Check if adnet binary is available for proper key generation
 53      if check_adnet_binary() {
 54          // Use ADNet for key generation
 55          let output = Command::new("adnet")
 56              .args(["account", "new", "--output", keys_path.to_str().unwrap()])
 57              .output()
 58              .map_err(|e| Error::Io(e))?;
 59  
 60          if !output.status.success() {
 61              let stderr = String::from_utf8_lossy(&output.stderr);
 62              return Err(Error::KeyManagement(format!(
 63                  "Key generation failed: {}",
 64                  stderr
 65              )));
 66          }
 67      } else {
 68          // Generate placeholder keys for development
 69          // In production, this would be replaced with proper crypto
 70          generate_placeholder_keys(keys_path)?;
 71      }
 72  
 73      Ok(())
 74  }
 75  
 76  /// Generate validator-specific keys.
 77  fn generate_validator_keys(keys_path: &Path) -> Result<()> {
 78      debug!("Generating validator keys in {:?}", keys_path);
 79  
 80      let validator_key_path = keys_path.join(VALIDATOR_KEY_FILE);
 81  
 82      if check_adnet_binary() {
 83          let output = Command::new("adnet")
 84              .args([
 85                  "validator",
 86                  "keygen",
 87                  "--output",
 88                  validator_key_path.to_str().unwrap(),
 89              ])
 90              .output()
 91              .map_err(|e| Error::Io(e))?;
 92  
 93          if !output.status.success() {
 94              let stderr = String::from_utf8_lossy(&output.stderr);
 95              return Err(Error::KeyManagement(format!(
 96                  "Validator key generation failed: {}",
 97                  stderr
 98              )));
 99          }
100      } else {
101          // Placeholder for development
102          fs::write(&validator_key_path, "PLACEHOLDER_VALIDATOR_KEY\n").map_err(|e| Error::Io(e))?;
103          output::info("Note", "Placeholder validator key generated (dev mode)");
104      }
105  
106      Ok(())
107  }
108  
109  /// Check if the ADNet binary is available.
110  fn check_adnet_binary() -> bool {
111      Command::new("adnet")
112          .arg("--version")
113          .output()
114          .map(|o| o.status.success())
115          .unwrap_or(false)
116  }
117  
118  /// Generate placeholder keys for development.
119  fn generate_placeholder_keys(keys_path: &Path) -> Result<()> {
120      output::info("Note", "Generating placeholder keys (ADNet not installed)");
121  
122      // Generate random-ish placeholder data
123      let timestamp = std::time::SystemTime::now()
124          .duration_since(std::time::UNIX_EPOCH)
125          .unwrap()
126          .as_nanos();
127  
128      let private_key = format!("APrivateKey1placeholder{:032x}", timestamp);
129      let public_key = format!("aleo1placeholder{:048x}", timestamp);
130      let address = format!("ax1placeholder{:048x}", timestamp);
131  
132      fs::write(
133          keys_path.join(PRIVATE_KEY_FILE),
134          format!("{}\n", private_key),
135      )
136      .map_err(|e| Error::Io(e))?;
137      fs::write(keys_path.join(PUBLIC_KEY_FILE), format!("{}\n", public_key))
138          .map_err(|e| Error::Io(e))?;
139      fs::write(keys_path.join(ADDRESS_FILE), format!("{}\n", address)).map_err(|e| Error::Io(e))?;
140  
141      // Set secure permissions
142      #[cfg(unix)]
143      {
144          use std::os::unix::fs::PermissionsExt;
145          let private_key_path = keys_path.join(PRIVATE_KEY_FILE);
146          let mut perms = fs::metadata(&private_key_path)?.permissions();
147          perms.set_mode(0o600);
148          fs::set_permissions(&private_key_path, perms)?;
149      }
150  
151      Ok(())
152  }
153  
154  /// Import keys from a file.
155  pub fn import_keys(source_path: &Path, dest_path: &Path) -> Result<()> {
156      output::status("Importing keys...");
157  
158      if !source_path.exists() {
159          return Err(Error::KeyManagement(format!(
160              "Source path does not exist: {:?}",
161              source_path
162          )));
163      }
164  
165      fs::create_dir_all(dest_path).map_err(|e| Error::Io(e))?;
166  
167      // Copy key files
168      let files = [
169          PRIVATE_KEY_FILE,
170          PUBLIC_KEY_FILE,
171          ADDRESS_FILE,
172          VALIDATOR_KEY_FILE,
173      ];
174      let mut copied = 0;
175  
176      for file in &files {
177          let src = source_path.join(file);
178          if src.exists() {
179              let dst = dest_path.join(file);
180              fs::copy(&src, &dst).map_err(|e| Error::Io(e))?;
181              copied += 1;
182              debug!("Copied {:?} to {:?}", src, dst);
183          }
184      }
185  
186      if copied == 0 {
187          return Err(Error::KeyManagement(
188              "No valid key files found in source directory".to_string(),
189          ));
190      }
191  
192      output::success(&format!("Imported {} key file(s)", copied));
193      Ok(())
194  }
195  
196  /// Export keys to a file (for backup).
197  pub fn export_keys(keys_path: &Path, export_path: &Path) -> Result<()> {
198      output::status("Exporting keys...");
199  
200      if !keys_path.exists() {
201          return Err(Error::KeyManagement(format!(
202              "Keys path does not exist: {:?}",
203              keys_path
204          )));
205      }
206  
207      fs::create_dir_all(export_path).map_err(|e| Error::Io(e))?;
208  
209      // Copy key files
210      let files = [
211          PRIVATE_KEY_FILE,
212          PUBLIC_KEY_FILE,
213          ADDRESS_FILE,
214          VALIDATOR_KEY_FILE,
215      ];
216      let mut exported = 0;
217  
218      for file in &files {
219          let src = keys_path.join(file);
220          if src.exists() {
221              let dst = export_path.join(file);
222              fs::copy(&src, &dst).map_err(|e| Error::Io(e))?;
223              exported += 1;
224          }
225      }
226  
227      output::success(&format!(
228          "Exported {} key file(s) to {:?}",
229          exported, export_path
230      ));
231      Ok(())
232  }
233  
234  /// Verify key files exist and are valid.
235  pub fn verify_keys(keys_path: &Path) -> Result<KeyVerification> {
236      let mut verification = KeyVerification::default();
237  
238      let private_key_path = keys_path.join(PRIVATE_KEY_FILE);
239      verification.private_key = private_key_path.exists();
240  
241      let public_key_path = keys_path.join(PUBLIC_KEY_FILE);
242      verification.public_key = public_key_path.exists();
243  
244      let address_path = keys_path.join(ADDRESS_FILE);
245      verification.address = address_path.exists();
246  
247      let validator_key_path = keys_path.join(VALIDATOR_KEY_FILE);
248      verification.validator_key = validator_key_path.exists();
249  
250      // Read address for display
251      if verification.address {
252          verification.address_value = fs::read_to_string(&address_path).ok();
253      }
254  
255      Ok(verification)
256  }
257  
258  /// Key verification result.
259  #[derive(Debug, Default)]
260  pub struct KeyVerification {
261      /// Private key exists
262      pub private_key: bool,
263      /// Public key exists
264      pub public_key: bool,
265      /// Address file exists
266      pub address: bool,
267      /// Validator key exists (for validators)
268      pub validator_key: bool,
269      /// Address value (if available)
270      pub address_value: Option<String>,
271  }
272  
273  impl KeyVerification {
274      /// Check if minimum required keys are present.
275      pub fn is_valid(&self) -> bool {
276          self.private_key && self.address
277      }
278  
279      /// Check if validator keys are present.
280      pub fn has_validator_keys(&self) -> bool {
281          self.validator_key
282      }
283  }
284  
285  /// Print key verification status.
286  pub fn print_verification(verification: &KeyVerification) {
287      if verification.private_key {
288          output::success("Private key: Present");
289      } else {
290          output::error("Private key: Missing");
291      }
292  
293      if verification.public_key {
294          output::success("Public key: Present");
295      } else {
296          output::warning("Public key: Missing");
297      }
298  
299      if verification.address {
300          if let Some(ref addr) = verification.address_value {
301              output::success(&format!("Address: {}", addr.trim()));
302          } else {
303              output::success("Address: Present");
304          }
305      } else {
306          output::error("Address: Missing");
307      }
308  
309      if verification.validator_key {
310          output::success("Validator key: Present");
311      }
312  }