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}