Implement add_password and delete_password

This commit is contained in:
Alex 2022-05-20 13:05:44 +02:00
parent 46145350eb
commit 3ac6896da9
Signed by: lx
GPG Key ID: 0E496D15096376BE
1 changed files with 92 additions and 3 deletions

View File

@ -1,9 +1,13 @@
pub mod ldap_provider;
pub mod static_provider;
use std::collections::BTreeMap;
use anyhow::{anyhow, bail, Result};
use async_trait::async_trait;
use k2v_client::{BatchInsertOp, BatchReadOp, CausalityToken, Filter, K2vClient, K2vValue};
use k2v_client::{
BatchInsertOp, BatchReadOp, CausalValue, CausalityToken, Filter, K2vClient, K2vValue,
};
use rand::prelude::*;
use rusoto_core::HttpClient;
use rusoto_credential::{AwsCredentials, StaticProvider};
@ -237,7 +241,48 @@ impl CryptoKeys {
}
pub async fn add_password(&self, storage: &StorageCredentials, password: &str) -> Result<()> {
unimplemented!()
let k2v = storage.k2v_client()?;
let (ident_salt, _public) = Self::load_salt_and_public(&k2v).await?;
// Generate short password digest (= password identity)
let ident = argon2_kdf(&ident_salt, password.as_bytes(), 16)?;
// Generate salt for KDF
let mut kdf_salt = [0u8; 32];
thread_rng().fill(&mut kdf_salt);
// Calculate key for password secret box
let password_key =
Key::from_slice(&argon2_kdf(&kdf_salt, password.as_bytes(), 32)?).unwrap();
// Seal a secret box that contains our crypto keys
let password_sealed = seal(&self.serialize(), &password_key)?;
let password_sortkey = format!("password:{}", hex::encode(&ident));
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");
}
Some(p.causality.clone())
}
None => None,
};
// Write values to storage
k2v.insert_batch(&[k2v_insert_single_key(
"keys",
&password_sortkey,
ct,
&password_blob,
)])
.await?;
Ok(())
}
pub async fn delete_password(
@ -246,7 +291,29 @@ impl CryptoKeys {
password: &str,
allow_delete_all: bool,
) -> Result<()> {
unimplemented!()
let k2v = storage.k2v_client()?;
let (ident_salt, _public) = Self::load_salt_and_public(&k2v).await?;
// Generate short password digest (= password identity)
let ident = argon2_kdf(&ident_salt, password.as_bytes(), 16)?;
let password_sortkey = format!("password:{}", hex::encode(&ident));
// List existing passwords
let existing_passwords = Self::list_existing_passwords(&k2v).await?;
// Check password is there
let pw = existing_passwords
.get(&password_sortkey)
.ok_or(anyhow!("password does not exist"))?;
if !allow_delete_all && existing_passwords.len() < 2 {
bail!("No other password exists, not deleting last password.");
}
k2v.delete_item("keys", &password_sortkey, pw.causality.clone())
.await?;
Ok(())
}
// ---- STORAGE UTIL ----
@ -317,6 +384,28 @@ impl CryptoKeys {
Ok((salt_constlen, public))
}
async fn list_existing_passwords(k2v: &K2vClient) -> Result<BTreeMap<String, CausalValue>> {
let mut res = k2v
.read_batch(&[BatchReadOp {
partition_key: "keys",
filter: Filter {
start: None,
end: None,
prefix: Some("password:"),
limit: None,
reverse: false,
},
conflicts_only: false,
tombstones: false,
single_item: false,
}])
.await?;
if res.len() != 1 {
bail!("unexpected k2v result: {:?}, expected one item", res);
}
Ok(res.pop().unwrap().items)
}
fn serialize(&self) -> [u8; 64] {
let mut res = [0u8; 64];
res[..32].copy_from_slice(self.master.as_ref());