use crate::{
crypto::{
self, rand_vec, Ciphertext, Creds, CredsPub, DecrypterExt, EncrypterExt, HashKind, SymKey,
},
Result,
};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
pub use private::ReadcapDict;
mod private {
use super::*;
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct ReadcapDict {
kind: HashKind,
salt: Vec<u8>,
readcaps: BTreeMap<Vec<u8>, Ciphertext<SymKey>>,
}
impl ReadcapDict {
pub fn new() -> Result<Self> {
let kind = HashKind::default();
Ok(ReadcapDict {
kind,
salt: rand_vec(kind.len())?,
readcaps: BTreeMap::new(),
})
}
fn key<C: CredsPub>(&self, creds: C) -> Result<Vec<u8>> {
let principal = creds.principal_of_kind(self.kind);
let mut key = vec![0u8; self.kind.len()];
self.kind
.digest(&mut key, [principal.as_slice(), &self.salt].into_iter())?;
Ok(key)
}
pub fn get<C: Creds>(&self, creds: C) -> Result<SymKey> {
let key = self.key(&creds)?;
let value = self.readcaps.get(&key).ok_or(crypto::Error::NoReadCap)?;
creds.ser_decrypt(value)
}
pub fn set<C: CredsPub>(&mut self, creds: C, block_key: &SymKey) -> Result<()> {
let key = self.key(&creds)?;
let value = creds.ser_encrypt(block_key)?;
self.readcaps.insert(key, value);
Ok(())
}
#[allow(dead_code)]
pub fn remove<C: CredsPub>(&mut self, creds: C) -> Result<bool> {
let key = self.key(creds)?;
Ok(self.readcaps.remove(&key).is_some())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{crypto::SymKeyKind, test_helpers::node_creds};
#[test]
fn set_then_get() {
let mut dict = ReadcapDict::new().unwrap();
let creds = node_creds();
let expected = SymKey::generate(SymKeyKind::Aes256Ctr).unwrap();
dict.set(creds, &expected).unwrap();
let actual = dict.get(creds).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn get_returns_no_readcap_when_key_not_found() {
let dict = ReadcapDict::new().unwrap();
let result = dict.get(node_creds());
let err = result.err().unwrap().downcast::<crypto::Error>().unwrap();
let passed = if let crypto::Error::NoReadCap = err {
true
} else {
false
};
assert!(passed);
}
#[test]
fn remove_after_set_returns_true() {
let mut dict = ReadcapDict::new().unwrap();
let creds = node_creds();
let sym_key = SymKey::generate(SymKeyKind::Aes256Cbc).unwrap();
dict.set(creds, &sym_key).unwrap();
let was_present = dict.remove(creds).unwrap();
let get_result = dict.get(creds);
assert!(was_present);
let err = get_result
.err()
.unwrap()
.downcast::<crypto::Error>()
.unwrap();
let passed = if let crypto::Error::NoReadCap = err {
true
} else {
false
};
assert!(passed);
}
#[test]
fn remove_when_not_set_returns_false() {
let mut dict = ReadcapDict::new().unwrap();
let was_present = dict.remove(node_creds()).unwrap();
assert!(!was_present);
}
}