1use std::{fs, path::Path};
4
5use casper_types::{AsymmetricType, PublicKey, SecretKey};
6
7use crate::Error;
8
9pub const SECRET_KEY_PEM: &str = "secret_key.pem";
11pub const PUBLIC_KEY_HEX: &str = "public_key_hex";
13pub const PUBLIC_KEY_PEM: &str = "public_key.pem";
15
16pub const FILES: [&str; 3] = [SECRET_KEY_PEM, PUBLIC_KEY_PEM, PUBLIC_KEY_HEX];
18
19pub const ED25519: &str = "Ed25519";
21pub const SECP256K1: &str = "secp256k1";
23
24pub fn generate_files(output_dir: &str, algorithm: &str, force: bool) -> Result<(), Error> {
34 if output_dir.is_empty() {
35 return Err(Error::EmptyKeygenPath);
36 }
37 fs::create_dir_all(output_dir).map_err(move |error| Error::IoError {
38 context: format!("unable to create directory at '{}'", output_dir),
39 error,
40 })?;
41 let output_dir = Path::new(output_dir)
42 .canonicalize()
43 .map_err(|error| Error::IoError {
44 context: format!("unable get canonical path at '{}'", output_dir),
45 error,
46 })?;
47
48 if !force {
49 for file in FILES.iter().map(|filename| output_dir.join(filename)) {
50 if file.exists() {
51 return Err(Error::FileAlreadyExists(file));
52 }
53 }
54 }
55
56 let secret_key = if algorithm.eq_ignore_ascii_case(ED25519) {
57 SecretKey::generate_ed25519().unwrap()
58 } else if algorithm.eq_ignore_ascii_case(SECP256K1) {
59 SecretKey::generate_secp256k1().unwrap()
60 } else {
61 return Err(Error::UnsupportedAlgorithm(algorithm.to_string()));
62 };
63
64 let public_key = PublicKey::from(&secret_key);
65
66 let public_key_hex_path = output_dir.join(PUBLIC_KEY_HEX);
67 fs::write(public_key_hex_path, public_key.to_hex()).map_err(|error| Error::IoError {
68 context: format!(
69 "unable to write public key hex file at {:?}",
70 output_dir.join(PUBLIC_KEY_HEX)
71 ),
72 error,
73 })?;
74
75 let secret_key_path = output_dir.join(SECRET_KEY_PEM);
76 secret_key
77 .to_file(secret_key_path)
78 .map_err(|error| Error::CryptoError {
79 context: "secret_key",
80 error,
81 })?;
82
83 let public_key_path = output_dir.join(PUBLIC_KEY_PEM);
84 public_key
85 .to_file(public_key_path)
86 .map_err(|error| Error::CryptoError {
87 context: "public_key",
88 error,
89 })?;
90
91 Ok(())
92}