Skip to content

Commit

Permalink
record encryption (#1058)
Browse files Browse the repository at this point in the history
* record encryption

* move paserk impl

* implicit assertions

* move wrapped cek

* add another test

* use host

* undo stray change

* more tests and docs

* fmt

* Update atuin-client/src/record/encryption.rs

Co-authored-by: Matteo Martellini <matteo@mercxry.me>

* Update atuin-client/src/record/encryption.rs

Co-authored-by: Matteo Martellini <matteo@mercxry.me>

* typo

---------

Co-authored-by: Matteo Martellini <matteo@mercxry.me>
  • Loading branch information
conradludgate and mercxry committed Jun 26, 2023
1 parent 1a63649 commit 6c53242
Show file tree
Hide file tree
Showing 11 changed files with 976 additions and 89 deletions.
452 changes: 418 additions & 34 deletions Cargo.lock

Large diffs are not rendered by default.

11 changes: 7 additions & 4 deletions atuin-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ sync = [
"reqwest",
"sha2",
"hex",
"base64",
"generic-array",
"xsalsa20poly1305",
]
Expand All @@ -27,6 +26,7 @@ sync = [
atuin-common = { path = "../atuin-common", version = "15.0.0" }

log = { workspace = true }
base64 = { workspace = true }
chrono = { workspace = true }
clap = { workspace = true }
eyre = { workspace = true }
Expand All @@ -52,15 +52,18 @@ lazy_static = "1"
memchr = "2.5"
rmp = { version = "0.8.11" }
typed-builder = "0.14.0"
tokio = { workspace = true }
semver = { workspace = true }

# encryption
rusty_paseto = { version = "0.5.0", default-features = false }
rusty_paserk = { version = "0.2.0", default-features = false, features = ["v4", "serde"] }

# sync
urlencoding = { version = "2.1.0", optional = true }
reqwest = { workspace = true, optional = true }
hex = { version = "0.4", optional = true }
sha2 = { version = "0.10", optional = true }
base64 = { workspace = true, optional = true }
tokio = { workspace = true }
semver = { workspace = true }
xsalsa20poly1305 = { version = "0.9.0", optional = true }
generic-array = { version = "0.14", optional = true, features = ["serde"] }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- store content encryption keys in the record
alter table records
add column cek text;
27 changes: 19 additions & 8 deletions atuin-client/src/kv.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use atuin_common::record::DecryptedData;
use eyre::{bail, ensure, eyre, Result};

use crate::record::encryption::PASETO_V4;
use crate::record::store::Store;
use crate::settings::Settings;

Expand All @@ -14,7 +16,7 @@ pub struct KvRecord {
}

impl KvRecord {
pub fn serialize(&self) -> Result<Vec<u8>> {
pub fn serialize(&self) -> Result<DecryptedData> {
use rmp::encode;

let mut output = vec![];
Expand All @@ -26,10 +28,10 @@ impl KvRecord {
encode::write_str(&mut output, &self.key)?;
encode::write_str(&mut output, &self.value)?;

Ok(output)
Ok(DecryptedData(output))
}

pub fn deserialize(data: &[u8], version: &str) -> Result<Self> {
pub fn deserialize(data: &DecryptedData, version: &str) -> Result<Self> {
use rmp::decode;

fn error_report<E: std::fmt::Debug>(err: E) -> eyre::Report {
Expand All @@ -38,7 +40,7 @@ impl KvRecord {

match version {
KV_VERSION => {
let mut bytes = decode::Bytes::new(data);
let mut bytes = decode::Bytes::new(&data.0);

let nfields = decode::read_array_len(&mut bytes).map_err(error_report)?;
ensure!(nfields == 3, "too many entries in v0 kv record");
Expand Down Expand Up @@ -84,6 +86,7 @@ impl KvStore {
pub async fn set(
&self,
store: &mut (impl Store + Send + Sync),
encryption_key: &[u8; 32],
namespace: &str,
key: &str,
value: &str,
Expand Down Expand Up @@ -111,7 +114,9 @@ impl KvStore {
.data(bytes)
.build();

store.push(&record).await?;
store
.push(&record.encrypt::<PASETO_V4>(encryption_key))
.await?;

Ok(())
}
Expand All @@ -121,6 +126,7 @@ impl KvStore {
pub async fn get(
&self,
store: &impl Store,
encryption_key: &[u8; 32],
namespace: &str,
key: &str,
) -> Result<Option<KvRecord>> {
Expand All @@ -137,12 +143,17 @@ impl KvStore {
};

loop {
let kv = KvRecord::deserialize(&record.data, &record.version)?;
let decrypted = match record.version.as_str() {
KV_VERSION => record.decrypt::<PASETO_V4>(encryption_key)?,
version => bail!("unknown version {version:?}"),
};

let kv = KvRecord::deserialize(&decrypted.data, &decrypted.version)?;
if kv.key == key && kv.namespace == namespace {
return Ok(Some(kv));
}

if let Some(parent) = record.parent {
if let Some(parent) = decrypted.parent {
record = store.get(parent.as_str()).await?;
} else {
break;
Expand Down Expand Up @@ -172,7 +183,7 @@ mod tests {
let encoded = kv.serialize().unwrap();
let decoded = KvRecord::deserialize(&encoded, KV_VERSION).unwrap();

assert_eq!(encoded, &snapshot);
assert_eq!(encoded.0, &snapshot);
assert_eq!(decoded, kv);
}
}

1 comment on commit 6c53242

@vercel
Copy link

@vercel vercel bot commented on 6c53242 Jun 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

atuin – ./

atuin.sh
atuin-ellieh.vercel.app
atuin-git-main-ellieh.vercel.app

Please sign in to comment.