use keyring_core::{set_default_store, Entry};
use secrecy::{ExposeSecret, SecretBox};
use thiserror::Error;
use zbus_secret_service_keyring_store::Store;
const SERVICE: &str = "imsg";
const ACCOUNT: &str = "database-encryption-key";
pub struct KeyringReady(());
#[derive(Debug, Error)]
pub enum Error {
#[error("Secret Service store init failed: {0}")]
StoreInit(keyring_core::Error),
#[error("keyring entry error: {0}")]
Entry(keyring_core::Error),
#[error("stored key is {0} bytes, expected 32")]
BadLength(usize),
#[error("key generation failed: {0}")]
Generate(getrandom::Error),
}
pub fn init_store() -> Result<KeyringReady, Error> {
let store = Store::new().map_err(Error::StoreInit)?;
set_default_store(store);
Ok(KeyringReady(()))
}
pub fn get_or_create_db_key(_ready: &KeyringReady) -> Result<SecretBox<[u8; 32]>, Error> {
let entry = Entry::new(SERVICE, ACCOUNT).map_err(Error::Entry)?;
match entry.get_secret() {
Ok(bytes) => bytes_to_secret(bytes),
Err(keyring_core::Error::NoEntry) => create_and_store_key(&entry),
Err(e) => Err(Error::Entry(e)),
}
}
fn bytes_to_secret(bytes: Vec<u8>) -> Result<SecretBox<[u8; 32]>, Error> {
let key: [u8; 32] = bytes.try_into().map_err(|v: Vec<u8>| Error::BadLength(v.len()))?;
Ok(SecretBox::new(Box::new(key)))
}
fn create_and_store_key(entry: &Entry) -> Result<SecretBox<[u8; 32]>, Error> {
let secret = SecretBox::<[u8; 32]>::try_init_with(|| -> Result<_, Error> {
let mut key = [0u8; 32];
getrandom::fill(&mut key).map_err(Error::Generate)?;
Ok(key)
})?;
entry.set_secret(secret.expose_secret()).map_err(Error::Entry)?;
Ok(secret)
}