Implement add_password and delete_password
This commit is contained in:
parent
46145350eb
commit
3ac6896da9
1 changed files with 92 additions and 3 deletions
|
@ -1,9 +1,13 @@
|
||||||
pub mod ldap_provider;
|
pub mod ldap_provider;
|
||||||
pub mod static_provider;
|
pub mod static_provider;
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use async_trait::async_trait;
|
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 rand::prelude::*;
|
||||||
use rusoto_core::HttpClient;
|
use rusoto_core::HttpClient;
|
||||||
use rusoto_credential::{AwsCredentials, StaticProvider};
|
use rusoto_credential::{AwsCredentials, StaticProvider};
|
||||||
|
@ -237,7 +241,48 @@ impl CryptoKeys {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_password(&self, storage: &StorageCredentials, password: &str) -> Result<()> {
|
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(
|
pub async fn delete_password(
|
||||||
|
@ -246,7 +291,29 @@ impl CryptoKeys {
|
||||||
password: &str,
|
password: &str,
|
||||||
allow_delete_all: bool,
|
allow_delete_all: bool,
|
||||||
) -> Result<()> {
|
) -> 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 ----
|
// ---- STORAGE UTIL ----
|
||||||
|
@ -317,6 +384,28 @@ impl CryptoKeys {
|
||||||
Ok((salt_constlen, public))
|
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] {
|
fn serialize(&self) -> [u8; 64] {
|
||||||
let mut res = [0u8; 64];
|
let mut res = [0u8; 64];
|
||||||
res[..32].copy_from_slice(self.master.as_ref());
|
res[..32].copy_from_slice(self.master.as_ref());
|
||||||
|
|
Loading…
Reference in a new issue