1use eyre::WrapErr;
2
3pub const SECRET_KEY_ENV_VAR: &str = "FASTN_SECRET_KEY";
5
6pub const SECRET_KEY_FILE: &str = ".fastn.secret-key";
8
9pub const ID52_FILE: &str = ".fastn.id52";
11
12pub fn generate_secret_key() -> eyre::Result<(String, fastn_id52::SecretKey)> {
18 let secret_key = fastn_id52::SecretKey::generate();
19 let id52 = secret_key.id52();
20 Ok((id52, secret_key))
21}
22
23pub async fn generate_and_save_key() -> eyre::Result<(String, fastn_id52::SecretKey)> {
32 let (id52, secret_key) = generate_secret_key()?;
33 let e = keyring_entry(&id52)?;
34 e.set_secret(&secret_key.to_bytes())
35 .wrap_err_with(|| format!("failed to save secret key for {id52}"))?;
36 tokio::fs::write(ID52_FILE, &id52).await?;
37 Ok((id52, secret_key))
38}
39
40fn keyring_entry(id52: &str) -> eyre::Result<keyring::Entry> {
41 keyring::Entry::new("fastn", id52)
42 .wrap_err_with(|| format!("failed to create keyring Entry for {id52}"))
43}
44
45fn handle_secret(secret: &str) -> eyre::Result<(String, fastn_id52::SecretKey)> {
46 use std::str::FromStr;
47 let secret_key = fastn_id52::SecretKey::from_str(secret).map_err(|e| eyre::anyhow!("{}", e))?;
48 let id52 = secret_key.id52();
49 Ok((id52, secret_key))
50}
51
52pub fn get_secret_key(_id52: &str, _path: &str) -> eyre::Result<fastn_id52::SecretKey> {
60 todo!("implement for fastn")
63}
64
65#[tracing::instrument]
77pub async fn read_or_create_key() -> eyre::Result<(String, fastn_id52::SecretKey)> {
78 if let Ok(secret) = std::env::var(SECRET_KEY_ENV_VAR) {
79 tracing::info!("Using secret key from environment variable {SECRET_KEY_ENV_VAR}");
80 return handle_secret(&secret);
81 } else {
82 match tokio::fs::read_to_string(SECRET_KEY_FILE).await {
83 Ok(secret) => {
84 tracing::info!("Using secret key from file {SECRET_KEY_FILE}");
85 let secret = secret.trim_end();
86 return handle_secret(secret);
87 }
88 Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
89 Err(e) => {
90 tracing::error!("failed to read {SECRET_KEY_FILE}: {e}");
91 return Err(e.into());
92 }
93 }
94 }
95
96 tracing::info!("No secret key found in environment or file, trying {ID52_FILE}");
97 match tokio::fs::read_to_string(ID52_FILE).await {
98 Ok(id52) => {
99 let e = keyring_entry(&id52)?;
100 match e.get_secret() {
101 Ok(secret) => {
102 if secret.len() != 32 {
103 return Err(eyre::anyhow!(
104 "keyring: secret for {id52} has invalid length: {}",
105 secret.len()
106 ));
107 }
108
109 let bytes: [u8; 32] = secret.try_into().expect("already checked for length");
110 let secret_key = fastn_id52::SecretKey::from_bytes(&bytes);
111 let id52 = secret_key.id52();
112 Ok((id52, secret_key))
113 }
114 Err(e) => {
115 tracing::error!("failed to read secret for {id52} from keyring: {e}");
116 Err(e.into())
117 }
118 }
119 }
120 Err(e) if e.kind() == std::io::ErrorKind::NotFound => generate_and_save_key().await,
121 Err(e) => {
122 tracing::error!("failed to read {ID52_FILE}: {e}");
123 Err(e.into())
124 }
125 }
126}