aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml4
-rw-r--r--src/login/mod.rs81
-rw-r--r--src/main.rs5
4 files changed, 62 insertions, 29 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1d4104d..230ac57 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -648,6 +648,7 @@ dependencies = [
[[package]]
name = "k2v-client"
version = "0.1.0"
+source = "git+https://git.deuxfleurs.fr/Deuxfleurs/garage.git?branch=improve-k2v-client#413c64d7a93d21adbe75d4a13b08cf63e74a95c7"
dependencies = [
"base64",
"http",
diff --git a/Cargo.toml b/Cargo.toml
index addf945..51726aa 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,5 +29,5 @@ tokio = "1.17.0"
toml = "0.5"
zstd = { version = "0.9", default-features = false }
-#k2v-client = { git = "https://git.deuxfleurs.fr/Deuxfleurs/garage.git" }
-k2v-client = { path = "../garage/src/k2v-client" }
+k2v-client = { git = "https://git.deuxfleurs.fr/Deuxfleurs/garage.git", branch = "improve-k2v-client" }
+#k2v-client = { path = "../garage/src/k2v-client" }
diff --git a/src/login/mod.rs b/src/login/mod.rs
index 7464cbb..75a39f8 100644
--- a/src/login/mod.rs
+++ b/src/login/mod.rs
@@ -95,7 +95,7 @@ impl CryptoKeys {
pub async fn init(storage: &StorageCredentials, password: &str) -> Result<Self> {
// Check that salt and public don't exist already
let k2v = storage.k2v_client()?;
- Self::check_uninitialized(&k2v).await?;
+ let (salt_ct, public_ct) = Self::check_uninitialized(&k2v).await?;
// Generate salt for password identifiers
let mut ident_salt = [0u8; 32];
@@ -129,8 +129,8 @@ impl CryptoKeys {
// Write values to storage
k2v.insert_batch(&[
- k2v_insert_single_key("keys", "salt", None, &ident_salt),
- k2v_insert_single_key("keys", "public", None, &keys.public),
+ k2v_insert_single_key("keys", "salt", salt_ct, &ident_salt),
+ k2v_insert_single_key("keys", "public", public_ct, &keys.public),
k2v_insert_single_key("keys", &password_sortkey, None, &password_blob),
])
.await
@@ -146,7 +146,7 @@ impl CryptoKeys {
) -> Result<Self> {
// Check that salt and public don't exist already
let k2v = storage.k2v_client()?;
- Self::check_uninitialized(&k2v).await?;
+ let (salt_ct, public_ct) = Self::check_uninitialized(&k2v).await?;
// Generate salt for password identifiers
let mut ident_salt = [0u8; 32];
@@ -162,8 +162,8 @@ impl CryptoKeys {
// Write values to storage
k2v.insert_batch(&[
- k2v_insert_single_key("keys", "salt", None, &ident_salt),
- k2v_insert_single_key("keys", "public", None, &keys.public),
+ k2v_insert_single_key("keys", "salt", salt_ct, &ident_salt),
+ k2v_insert_single_key("keys", "public", public_ct, &keys.public),
])
.await
.context("InsertBatch for salt and public")?;
@@ -184,7 +184,7 @@ impl CryptoKeys {
let password_blob = {
let mut val = match k2v.read_item("keys", &password_sortkey).await {
Err(k2v_client::Error::NotFound) => {
- bail!("given password does not exist in storage")
+ bail!("invalid password")
}
x => x?,
};
@@ -193,7 +193,7 @@ impl CryptoKeys {
}
match val.value.pop().unwrap() {
K2vValue::Value(v) => v,
- K2vValue::Tombstone => bail!("password is a tombstone"),
+ K2vValue::Tombstone => bail!("invalid password"),
}
};
@@ -257,15 +257,15 @@ impl CryptoKeys {
let password_blob = [&kdf_salt[..], &password_sealed].concat();
// List existing passwords to overwrite existing entry if necessary
- let existing_passwords = Self::list_existing_passwords(&k2v).await?;
- let ct = match existing_passwords.get(&password_sortkey) {
- Some(p) => {
- if p.value.iter().any(|x| matches!(x, K2vValue::Value(_))) {
- bail!("Password already exists");
+ let ct = match k2v.read_item("keys", &password_sortkey).await {
+ Err(k2v_client::Error::NotFound) => None,
+ v => {
+ let entry = v?;
+ if entry.value.iter().any(|x| matches!(x, K2vValue::Value(_))) {
+ bail!("password already exists");
}
- Some(p.causality.clone())
+ Some(entry.causality.clone())
}
- None => None,
};
// Write values to storage
@@ -315,11 +315,13 @@ impl CryptoKeys {
// ---- STORAGE UTIL ----
- async fn check_uninitialized(k2v: &K2vClient) -> Result<()> {
+ async fn check_uninitialized(
+ k2v: &K2vClient,
+ ) -> Result<(Option<CausalityToken>, Option<CausalityToken>)> {
let params = k2v
.read_batch(&[
- k2v_read_single_key("keys", "salt"),
- k2v_read_single_key("keys", "public"),
+ k2v_read_single_key("keys", "salt", true),
+ k2v_read_single_key("keys", "public", true),
])
.await
.context("ReadBatch for salt and public in check_uninitialized")?;
@@ -329,18 +331,41 @@ impl CryptoKeys {
params
);
}
- if !params[0].items.is_empty() || !params[1].items.is_empty() {
- bail!("`salt` or `public` already exists in keys storage.");
+ if params[0].items.len() > 1 || params[1].items.len() > 1 {
+ bail!(
+ "invalid response from k2v storage: {:?} (several items in single_item read)",
+ params
+ );
}
- Ok(())
+ let salt_ct = match params[0].items.iter().next() {
+ None => None,
+ Some((_, CausalValue { causality, value })) => {
+ if value.iter().any(|x| matches!(x, K2vValue::Value(_))) {
+ bail!("key storage already initialized");
+ }
+ Some(causality.clone())
+ }
+ };
+
+ let public_ct = match params[1].items.iter().next() {
+ None => None,
+ Some((_, CausalValue { causality, value })) => {
+ if value.iter().any(|x| matches!(x, K2vValue::Value(_))) {
+ bail!("key storage already initialized");
+ }
+ Some(causality.clone())
+ }
+ };
+
+ Ok((salt_ct, public_ct))
}
async fn load_salt_and_public(k2v: &K2vClient) -> Result<([u8; 32], PublicKey)> {
let mut params = k2v
.read_batch(&[
- k2v_read_single_key("keys", "salt"),
- k2v_read_single_key("keys", "public"),
+ k2v_read_single_key("keys", "salt", false),
+ k2v_read_single_key("keys", "public", false),
])
.await
.context("ReadBatch for salt and public in load_salt_and_public")?;
@@ -351,7 +376,7 @@ impl CryptoKeys {
);
}
if params[0].items.len() != 1 || params[1].items.len() != 1 {
- bail!("`salt` or `public` do not exist in storage.");
+ bail!("cryptographic keys not initialized for user");
}
// Retrieve salt from given response
@@ -453,7 +478,11 @@ pub fn argon2_kdf(salt: &[u8], password: &[u8], output_len: usize) -> Result<Vec
Ok(hash.as_bytes().to_vec())
}
-pub fn k2v_read_single_key<'a>(partition_key: &'a str, sort_key: &'a str) -> BatchReadOp<'a> {
+pub fn k2v_read_single_key<'a>(
+ partition_key: &'a str,
+ sort_key: &'a str,
+ tombstones: bool,
+) -> BatchReadOp<'a> {
BatchReadOp {
partition_key: partition_key,
filter: Filter {
@@ -464,7 +493,7 @@ pub fn k2v_read_single_key<'a>(partition_key: &'a str, sort_key: &'a str) -> Bat
reverse: false,
},
conflicts_only: false,
- tombstones: false,
+ tombstones,
single_item: true,
}
}
diff --git a/src/main.rs b/src/main.rs
index a2630d6..fe426b4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -158,7 +158,10 @@ async fn main() -> Result<()> {
let existing_password =
rpassword::prompt_password("Enter existing password to decrypt keys: ")?;
let new_password = if gen {
- let password = base64::encode(&u128::to_be_bytes(thread_rng().gen())[..10]);
+ let password = base64::encode_config(
+ &u128::to_be_bytes(thread_rng().gen())[..10],
+ base64::URL_SAFE_NO_PAD,
+ );
println!("Your new password: {}", password);
println!("Keep it safe!");
password