diff options
author | Ellie Huxtable <ellie@elliehuxtable.com> | 2024-02-02 14:05:13 +0000 |
---|---|---|
committer | Ellie Huxtable <ellie@elliehuxtable.com> | 2024-02-02 18:01:09 +0000 |
commit | 212dc928c9c4cc8b03b9cad83f9b8b81044c2b02 (patch) | |
tree | bafda4b459395ec0c6954d5a452eb5a022528dde | |
parent | 744f0059c2b503e8dc43a004d95b0a086af8c4e5 (diff) |
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.
-rw-r--r-- | atuin-client/src/record/sqlite_store.rs | 12 | ||||
-rw-r--r-- | atuin-client/src/record/store.rs | 1 | ||||
-rw-r--r-- | atuin/src/command/client/store.rs | 3 | ||||
-rw-r--r-- | atuin/src/command/client/store/verify.rs | 26 |
4 files changed, 42 insertions, 0 deletions
diff --git a/atuin-client/src/record/sqlite_store.rs b/atuin-client/src/record/sqlite_store.rs index 8bf200c3..5df446b4 100644 --- a/atuin-client/src/record/sqlite_store.rs +++ b/atuin-client/src/record/sqlite_store.rs @@ -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)] diff --git a/atuin-client/src/record/store.rs b/atuin-client/src/record/store.rs index 9c052213..04fba630 100644 --- a/atuin-client/src/record/store.rs +++ b/atuin-client/src/record/store.rs @@ -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( diff --git a/atuin/src/command/client/store.rs b/atuin/src/command/client/store.rs index 016f01b7..4729a0f3 100644 --- a/atuin/src/command/client/store.rs +++ b/atuin/src/command/client/store.rs @@ -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, diff --git a/atuin/src/command/client/store/verify.rs b/atuin/src/command/client/store/verify.rs new file mode 100644 index 00000000..84bec96a --- /dev/null +++ b/atuin/src/command/client/store/verify.rs @@ -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(()) + } +} |