feat: add verify command to local store

This ensures that the local store can be decrypted with the current
in-use key.

If it cannot, we can go on to perform maintenance operations and get
back into a happy state.
This commit is contained in:
Ellie Huxtable 2024-02-02 14:05:13 +00:00
parent 744f0059c2
commit 212dc928c9
4 changed files with 42 additions and 0 deletions

View File

@ -300,6 +300,18 @@ impl Store for SqliteStore {
Ok(())
}
/// Verify that every record in this store can be decrypted with the current key
/// Someday maybe also check each tag/record can be deserialized, but not for now.
async fn verify(&self, key: &[u8; 32]) -> Result<()> {
let all = self.load_all().await?;
all.into_iter()
.map(|record| record.decrypt::<PASETO_V4>(key))
.collect::<Result<Vec<_>>>()?;
Ok(())
}
}
#[cfg(test)]

View File

@ -29,6 +29,7 @@ pub trait Store {
async fn first(&self, host: HostId, tag: &str) -> Result<Option<Record<EncryptedData>>>;
async fn re_encrypt(&self, old_key: &[u8; 32], new_key: &[u8; 32]) -> Result<()>;
async fn verify(&self, key: &[u8; 32]) -> Result<()>;
/// Get the next `limit` records, after and including the given index
async fn next(

View File

@ -13,6 +13,7 @@ mod push;
mod rebuild;
mod rekey;
mod verify;
#[derive(Subcommand, Debug)]
#[command(infer_subcommands = true)]
@ -20,6 +21,7 @@ pub enum Cmd {
Status,
Rebuild(rebuild::Rebuild),
Rekey(rekey::Rekey),
Verify(verify::Verify),
#[cfg(feature = "sync")]
Push(push::Push),
@ -36,6 +38,7 @@ impl Cmd {
Self::Status => self.status(store).await,
Self::Rebuild(rebuild) => rebuild.run(settings, store, database).await,
Self::Rekey(rekey) => rekey.run(settings, store).await,
Self::Verify(verify) => verify.run(settings, store).await,
#[cfg(feature = "sync")]
Self::Push(push) => push.run(settings, store).await,

View File

@ -0,0 +1,26 @@
use clap::Args;
use eyre::Result;
use atuin_client::{
encryption::load_key,
record::{sqlite_store::SqliteStore, store::Store},
settings::Settings,
};
#[derive(Args, Debug)]
pub struct Verify {}
impl Verify {
pub async fn run(&self, settings: &Settings, store: SqliteStore) -> Result<()> {
println!("Verifying local store can be decrypted with the current key");
let key = load_key(settings)?;
match store.verify(&key.into()).await {
Ok(()) => println!("Local store encryption verified OK"),
Err(e) => println!("Failed to verify local store encryption: {e:?}"),
}
Ok(())
}
}