Skip to main content

cargo_codesign/
keygen.rs

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
17/// Adds the private key filename to the `.gitignore` in the same directory.
18///
19/// Returns `Ok(true)` if the entry was added, `Ok(false)` if it was already present.
20pub 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}