1use base64::{engine::general_purpose::STANDARD, Engine};
2use ed25519_dalek::SigningKey;
3use rand_core::OsRng;
4use std::io::Write;
5use std::path::Path;
6
7pub fn generate_keypair() -> Result<(String, String), Box<dyn std::error::Error>> {
8 let signing_key = SigningKey::generate(&mut OsRng);
9 let verifying_key = signing_key.verifying_key();
10
11 let private_b64 = STANDARD.encode(signing_key.to_bytes());
12 let public_b64 = STANDARD.encode(verifying_key.to_bytes());
13
14 Ok((private_b64, public_b64))
15}
16
17pub fn update_gitignore(private_key_path: &Path) -> Result<bool, std::io::Error> {
21 let dir = private_key_path.parent().unwrap_or_else(|| Path::new("."));
22 let gitignore_path = dir.join(".gitignore");
23
24 let key_name = private_key_path
25 .file_name()
26 .and_then(|n| n.to_str())
27 .ok_or_else(|| {
28 std::io::Error::new(
29 std::io::ErrorKind::InvalidInput,
30 format!(
31 "could not determine filename from path: {}",
32 private_key_path.display()
33 ),
34 )
35 })?;
36
37 let existing = std::fs::read_to_string(&gitignore_path).unwrap_or_default();
38
39 if existing.lines().any(|line| line.trim() == key_name) {
40 return Ok(false);
41 }
42
43 let mut file = std::fs::OpenOptions::new()
44 .create(true)
45 .append(true)
46 .open(&gitignore_path)?;
47
48 if !existing.is_empty() && !existing.ends_with('\n') {
49 writeln!(file)?;
50 }
51
52 writeln!(file, "# private key for codesign update signing:")?;
53 writeln!(file, "{key_name}")?;
54
55 Ok(true)
56}