extern crate rmp_serde;
use std::cell::RefCell;
use std::convert::TryInto;
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use serde_repr::{Serialize_repr, Deserialize_repr};
use super::{
try_into,
CURRENT_VERSION,
crypto::{
CryptoManager,
CryptoMac,
BoxCryptoManager,
},
chunker::Rollsum,
error::{
Error,
Result,
},
utils::{
buffer_pad_small,
buffer_pad,
buffer_unpad,
buffer_pad_fixed,
buffer_unpad_fixed,
shuffle,
memcmp,
randombytes,
from_base64,
to_base64,
StringBase64,
MsgPackSerilization,
SYMMETRIC_KEY_SIZE,
},
};
pub fn gen_uid_base64() -> StringBase64 {
to_base64(&randombytes(24)).unwrap()
}
#[derive(Serialize, Deserialize)]
pub struct CachedContent {
version: u8,
data: Vec<u8>,
}
pub struct AccountCryptoManager(pub CryptoManager);
impl AccountCryptoManager {
const COLTYPE_PAD_SIZE: usize = 32;
pub fn new(key: &[u8; 32], version: u8) -> Result<Self> {
let context = b"Acct ";
Ok(Self {
0: CryptoManager::new(key, &context, version)?,
})
}
pub fn collection_type_to_uid(&self, collection_type: &str) -> Result<Vec<u8>> {
self.0.deterministic_encrypt(&buffer_pad_fixed(collection_type.as_bytes(), Self::COLTYPE_PAD_SIZE)?, None)
}
pub fn collection_type_from_uid(&self, collection_type_uid: &[u8]) -> Result<String> {
buffer_unpad_fixed(&self.0.deterministic_decrypt(collection_type_uid, None)?, Self::COLTYPE_PAD_SIZE).map(|x| String::from_utf8(x).unwrap_or_else(|_| "BAD TYPE".to_owned()))
}
}
pub struct CollectionCryptoManager(CryptoManager);
impl CollectionCryptoManager {
pub fn new(key: &[u8; 32], version: u8) -> Result<Self> {
let context = b"Col ";
Ok(Self {
0: CryptoManager::new(key, &context, version)?,
})
}
}
pub struct ItemCryptoManager(CryptoManager);
impl ItemCryptoManager {
pub fn new(key: &[u8; 32], version: u8) -> Result<Self> {
let context = b"ColItem ";
Ok(Self {
0: CryptoManager::new(key, &context, version)?,
})
}
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct ItemMetadata {
#[serde(rename = "type")]
#[serde(skip_serializing_if = "Option::is_none")]
type_: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
mtime: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
color: Option<String>,
}
impl ItemMetadata {
pub fn new() -> Self {
Self {
type_: None,
name: None,
mtime: None,
color: None,
description: None,
}
}
pub fn set_item_type(&mut self, type_: Option<&str>) -> &mut Self {
self.type_ = type_.map(|x| x.to_string());
self
}
pub fn item_type(&self) -> Option<&str> {
self.type_.as_deref()
}
pub fn set_name(&mut self, name: Option<&str>) -> &mut Self {
self.name = name.map(|x| x.to_string());
self
}
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn set_mtime(&mut self, mtime: Option<i64>) -> &mut Self {
self.mtime = mtime;
self
}
pub fn mtime(&self) -> Option<i64> {
self.mtime
}
pub fn set_description(&mut self, description: Option<&str>) -> &mut Self {
self.description = description.map(|x| x.to_string());
self
}
pub fn description(&self) -> Option<&str> {
self.description.as_deref()
}
pub fn set_color(&mut self, color: Option<&str>) -> &mut Self {
self.color = color.map(|x| x.to_string());
self
}
pub fn color(&self) -> Option<&str> {
self.color.as_deref()
}
}
impl MsgPackSerilization for ItemMetadata {
type Output = ItemMetadata;
fn to_msgpack(&self) -> Result<Vec<u8>> {
Ok(rmp_serde::to_vec_named(self)?)
}
fn from_msgpack(data: &[u8]) -> Result<Self::Output> {
Ok(rmp_serde::from_read_ref(data)?)
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SignedInvitationContent {
#[serde(with = "serde_bytes")]
pub encryption_key: Vec<u8>,
pub collection_type: String,
}
#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct SignedInvitation {
uid: StringBase64,
version: u8,
username: String,
collection: String,
access_level: CollectionAccessLevel,
#[serde(with = "serde_bytes")]
signed_encryption_key: Vec<u8>,
from_username: Option<String>,
#[serde(with = "serde_bytes", skip_serializing)]
from_pubkey: Option<Vec<u8>>,
}
impl SignedInvitation {
pub fn uid(&self) -> &str {
&self.uid
}
pub fn username(&self) -> &str {
&self.username
}
pub fn collection(&self) -> &str {
&self.collection
}
pub fn access_level(&self) -> CollectionAccessLevel {
self.access_level
}
pub fn from_username(&self) -> Option<&str> {
self.from_username.as_deref()
}
pub fn from_pubkey(&self) -> &[u8] {
match self.from_pubkey.as_deref() {
Some(from_pubkey) => from_pubkey,
None => panic!("Can never happen. Tried getting empty pubkey."),
}
}
pub(crate) fn decrypted_encryption_key(&self, identity_crypto_manager: &BoxCryptoManager) -> Result<Vec<u8>> {
let from_pubkey = match self.from_pubkey.as_deref() {
Some(from_pubkey) => from_pubkey,
None => return Err(Error::ProgrammingError("Missing invitation encryption key.")),
};
identity_crypto_manager.decrypt(&self.signed_encryption_key, try_into!(from_pubkey)?)
}
}
#[derive(Serialize_repr, Deserialize_repr, Clone, Copy, PartialEq, Debug)]
#[repr(u32)]
pub enum CollectionAccessLevel {
ReadOnly,
Admin,
ReadWrite,
}
#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct EncryptedCollection {
item: EncryptedItem,
access_level: CollectionAccessLevel,
#[serde(with = "serde_bytes")]
collection_key: Vec<u8>,
#[serde(with = "serde_bytes")]
collection_type: Option<Vec<u8>>,
stoken: Option<String>,
}
impl EncryptedCollection {
pub fn new(parent_crypto_manager: &AccountCryptoManager, collection_type: &str, meta: &[u8], content: &[u8]) -> Result<Self> {
let version = CURRENT_VERSION;
let collection_type = parent_crypto_manager.collection_type_to_uid(collection_type)?;
let collection_key = parent_crypto_manager.0.encrypt(&randombytes(SYMMETRIC_KEY_SIZE), Some(&collection_type))?;
let crypto_manager = Self::crypto_manager_static(parent_crypto_manager, version, &collection_key, Some(&collection_type))?;
let item = EncryptedItem::new(&crypto_manager, &meta, content)?;
Ok(Self {
item,
access_level: CollectionAccessLevel::Admin,
collection_key,
collection_type: Some(collection_type),
stoken: None,
})
}
pub fn cache_load(cached: &[u8]) -> Result<Self> {
let cached: CachedContent = rmp_serde::from_read_ref(cached)?;
let ret: std::result::Result<Self, _> = rmp_serde::from_read_ref(&cached.data);
Ok(match ret {
Ok(ret) => ret,
Err(_) => {
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct EncryptedCollectionLegacy {
item: EncryptedItem,
access_level: CollectionAccessLevel,
#[serde(with = "serde_bytes")]
collection_key: Vec<u8>,
stoken: Option<String>,
}
let ret: EncryptedCollectionLegacy = rmp_serde::from_read_ref(&cached.data)?;
Self {
item: ret.item,
access_level: ret.access_level,
collection_key: ret.collection_key,
stoken: ret.stoken,
collection_type: None,
}
}
})
}
pub fn cache_save(&self) -> Result<Vec<u8>> {
let data = rmp_serde::to_vec(self)?;
let content = CachedContent {
version: 1,
data,
};
Ok(rmp_serde::to_vec(&content)?)
}
pub fn cache_save_with_content(&self) -> Result<Vec<u8>> {
let data = rmp_serde::to_vec(self)?;
let content = CachedContent {
version: 1,
data,
};
Ok(rmp_serde::to_vec(&content)?)
}
pub(crate) fn mark_saved(&self) {
self.item.mark_saved();
}
pub fn verify(&self, crypto_manager: &CollectionCryptoManager) -> Result<bool> {
let item_crypto_manager = self.item.crypto_manager(crypto_manager)?;
self.item.verify(&item_crypto_manager)
}
pub fn set_meta(&mut self, crypto_manager: &CollectionCryptoManager, meta: &[u8]) -> Result<()> {
let item_crypto_manager = self.item.crypto_manager(crypto_manager)?;
self.item.set_meta(&item_crypto_manager, &meta)
}
pub fn meta(&self, crypto_manager: &CollectionCryptoManager) -> Result<Vec<u8>> {
self.verify(crypto_manager)?;
let item_crypto_manager = self.item.crypto_manager(crypto_manager)?;
self.item.meta(&item_crypto_manager)
}
pub fn set_content(&mut self, crypto_manager: &CollectionCryptoManager, content: &[u8]) -> Result<()> {
let item_crypto_manager = self.item.crypto_manager(crypto_manager)?;
self.item.set_content(&item_crypto_manager, content)
}
pub fn content(&self, crypto_manager: &CollectionCryptoManager) -> Result<Vec<u8>> {
self.verify(crypto_manager)?;
let item_crypto_manager = self.item.crypto_manager(crypto_manager)?;
self.item.content(&item_crypto_manager)
}
pub fn delete(&mut self, crypto_manager: &CollectionCryptoManager) -> Result<()> {
let item_crypto_manager = self.item.crypto_manager(crypto_manager)?;
self.item.delete(&item_crypto_manager)
}
pub fn is_deleted(&self) -> bool {
self.item.is_deleted()
}
pub fn uid(&self) -> &str {
self.item.uid()
}
pub fn etag(&self) -> &str {
self.item.etag()
}
pub fn _is_new(&self) -> bool {
self.item.etag.borrow().is_none()
}
pub fn stoken(&self) -> Option<&str> {
self.stoken.as_deref()
}
pub fn access_level(&self) -> CollectionAccessLevel {
self.access_level
}
pub fn item(&self) -> &EncryptedItem {
&self.item
}
pub fn collection_type(&self, account_crypto_manager: &AccountCryptoManager) -> Result<String> {
match &self.collection_type {
Some(collection_type) => account_crypto_manager.collection_type_from_uid(collection_type),
None => {
let crypto_manager = self.crypto_manager(account_crypto_manager)?;
let meta_raw = self.meta(&crypto_manager)?;
Ok(ItemMetadata::from_msgpack(&meta_raw)?.item_type().unwrap_or("BAD TYPE").to_owned())
},
}
}
pub fn create_invitation(&self, account_crypto_manager: &AccountCryptoManager, identity_crypto_manager: &BoxCryptoManager, username: &str, pubkey: &[u8], access_level: CollectionAccessLevel) -> Result<SignedInvitation> {
let uid = to_base64(&randombytes(32))?;
let encryption_key = self.collection_key(account_crypto_manager)?;
let collection_type = self.collection_type(account_crypto_manager)?;
let content = SignedInvitationContent {
encryption_key,
collection_type,
};
let raw_content = rmp_serde::to_vec_named(&content)?;
let signed_encryption_key = identity_crypto_manager.encrypt(&buffer_pad_small(&raw_content)?, try_into!(pubkey)?)?;
Ok(SignedInvitation {
uid,
version: CURRENT_VERSION,
username: username.to_owned(),
collection: self.uid().to_owned(),
access_level: access_level.to_owned(),
signed_encryption_key,
from_username: None,
from_pubkey: Some(identity_crypto_manager.pubkey().to_owned()),
})
}
fn collection_key_static(account_crypto_manager: &AccountCryptoManager, encryption_key: &[u8], collection_type: Option<&[u8]>) -> Result<Vec<u8>> {
account_crypto_manager.0.decrypt(encryption_key, collection_type)
}
fn collection_key(&self, account_crypto_manager: &AccountCryptoManager) -> Result<Vec<u8>> {
Self::collection_key_static(account_crypto_manager, &self.collection_key, self.collection_type.as_deref())
}
fn crypto_manager_static(parent_crypto_manager: &AccountCryptoManager, version: u8, encryption_key: &[u8], collection_type: Option<&[u8]>) -> Result<CollectionCryptoManager> {
let encryption_key = Self::collection_key_static(parent_crypto_manager, encryption_key, collection_type)?;
CollectionCryptoManager::new(try_into!(&encryption_key[..])?, version)
}
pub fn crypto_manager(&self, parent_crypto_manager: &AccountCryptoManager) -> Result<CollectionCryptoManager> {
Self::crypto_manager_static(parent_crypto_manager, self.item.version, &self.collection_key, self.collection_type.as_deref())
}
}
#[derive(Serialize, Deserialize, Clone)]
pub(crate) struct ChunkArrayItem(
pub StringBase64,
#[serde(default)]
#[serde(with = "serde_bytes")]
pub Option<Vec<u8>>,
);
#[derive(Serialize, Deserialize, Clone)]
pub struct EncryptedRevision {
uid: StringBase64,
#[serde(with = "serde_bytes")]
meta: Vec<u8>,
deleted: bool,
chunks: Vec<ChunkArrayItem>,
}
impl EncryptedRevision {
pub fn new(crypto_manager: &ItemCryptoManager, additional_data: &[u8], meta: &[u8], content: &[u8]) -> Result<Self> {
let mut ret = Self {
uid: "".to_owned(),
meta: vec![],
deleted: false,
chunks: vec![],
};
ret.set_meta(&crypto_manager, additional_data, meta)?;
ret.set_content(&crypto_manager, additional_data, content)?;
Ok(ret)
}
fn calculate_hash(&self, crypto_manager: &ItemCryptoManager, additional_data: &[u8]) -> Result<Vec<u8>> {
let mut crypto_mac = crypto_manager.0.crypto_mac()?;
crypto_mac.update(&[self.deleted as u8])?;
crypto_mac.update_with_len_prefix(additional_data)?;
let mut chunks_hash = CryptoMac::new(None)?;
for chunk in self.chunks.iter() {
chunks_hash.update(&from_base64(&chunk.0)?)?;
}
crypto_mac.update(&chunks_hash.finalize()?)?;
crypto_mac.finalize()
}
pub fn verify(&self, crypto_manager: &ItemCryptoManager, additional_data: &[u8]) -> Result<bool> {
let mac = from_base64(&self.uid)?;
let ad_hash = self.calculate_hash(crypto_manager, additional_data)?;
crypto_manager.0.verify(&self.meta, try_into!(&mac[..])?, Some(&ad_hash))
}
pub fn set_meta(&mut self, crypto_manager: &ItemCryptoManager, additional_data: &[u8], meta: &[u8]) -> Result<()> {
let ad_hash = self.calculate_hash(crypto_manager, additional_data)?;
let msg = buffer_pad_small(meta)?;
let enc_content = crypto_manager.0.encrypt_detached(&msg, Some(&ad_hash))?;
self.uid = to_base64(&enc_content.0)?;
self.meta = enc_content.1;
Ok(())
}
pub fn meta(&self, crypto_manager: &ItemCryptoManager, additional_data: &[u8]) -> Result<Vec<u8>> {
let mac = from_base64(&self.uid)?;
let ad_hash = self.calculate_hash(crypto_manager, additional_data)?;
buffer_unpad(&crypto_manager.0.decrypt_detached(&self.meta, try_into!(&mac[..])?, Some(&ad_hash))?)
}
pub fn set_content(&mut self, crypto_manager: &ItemCryptoManager, additional_data: &[u8], content: &[u8]) -> Result<()> {
let meta = self.meta(crypto_manager, additional_data)?;
let mut chunks: Vec<ChunkArrayItem> = vec![];
let min_chunk = 1 << 14;
let max_chunk = 1 << 16;
let mut chunk_start = 0;
let content_length = content.len();
if content_length > min_chunk {
let mask = (1 << 12) - 1;
let mut chunker = Rollsum::new();
let mut pos = 0;
while pos < content_length {
chunker.update(content[pos]);
let offset = pos - chunk_start;
if offset >= min_chunk {
if (offset >= max_chunk) || chunker.split(mask) {
let buf = &content[chunk_start..pos];
let hash = to_base64(&crypto_manager.0.calculate_mac(buf)?)?;
chunks.push(ChunkArrayItem(hash, Some(buf.to_vec())));
chunk_start = pos;
}
}
pos += 1;
}
}
if chunk_start < content.len() {
let buf = &content[chunk_start..];
let hash = to_base64(&crypto_manager.0.calculate_mac(buf)?)?;
chunks.push(ChunkArrayItem(hash, Some(buf.to_vec())));
}
if !chunks.is_empty() {
let mut indices = shuffle(&mut chunks);
let mut uid_indices: HashMap<String, usize> = HashMap::new();
chunks = chunks
.into_iter()
.enumerate()
.filter_map(|(i, chunk)| {
let uid = &chunk.0;
match uid_indices.get(uid) {
Some(previous_index) => {
indices[i] = *previous_index;
None
},
None => {
uid_indices.insert(uid.to_string(), i);
Some(chunk)
}
}
})
.collect();
if indices.len() > 1 {
let buf = rmp_serde::to_vec_named(&(indices, ))?;
let hash = to_base64(&crypto_manager.0.calculate_mac(&buf)?)?;
chunks.push(ChunkArrayItem(hash, Some(buf)));
}
}
let encrypt_item = |item: ChunkArrayItem| -> Result<ChunkArrayItem> {
let hash = item.0;
let buf = item.1;
let ret = match buf {
Some(buf) => Some(crypto_manager.0.encrypt(&buffer_pad(&buf)?, None)?),
None => None,
};
Ok(ChunkArrayItem(hash, ret))
};
let encrypted_chunks: Result<Vec<_>> = chunks
.into_iter()
.map(encrypt_item)
.collect();
self.chunks = encrypted_chunks?;
self.set_meta(crypto_manager, additional_data, &meta)?;
Ok(())
}
pub fn content(&self, crypto_manager: &ItemCryptoManager) -> Result<Vec<u8>> {
let mut indices = None;
let item = |item: &ChunkArrayItem| -> Result<Vec<u8>> {
let hash_str = &item.0;
let buf = &item.1;
let buf = match buf {
Some(buf) => buffer_unpad(&crypto_manager.0.decrypt(&buf, None)?)?,
None => return Err(Error::MissingContent("Got chunk without data")),
};
let hash = from_base64(&hash_str)?;
let calculated_mac = crypto_manager.0.calculate_mac(&buf)?;
if !memcmp(&hash, &calculated_mac) {
return Err(Error::Encryption("Got a wrong mac for chunk"));
}
Ok(buf)
};
let decrypted_chunks: Result<Vec<_>> = self.chunks
.iter()
.map(item)
.collect();
let mut decrypted_chunks = decrypted_chunks?;
if self.chunks.len() > 1 {
let buf = decrypted_chunks.pop().unwrap();
let header_chunk: (Vec<usize>, ) = rmp_serde::from_read_ref(&buf)?;
indices = Some(header_chunk.0);
}
match indices {
Some(indices) => {
if indices.len() > 1 {
let sorted_chunks: Vec<u8> = indices
.into_iter()
.map(|index| &decrypted_chunks[index])
.flatten()
.copied()
.collect::<Vec<u8>>();
Ok(sorted_chunks)
} else {
Ok(decrypted_chunks.into_iter().next().unwrap_or_default())
}
},
None => Ok(decrypted_chunks.into_iter().next().unwrap_or_default())
}
}
pub fn delete(&mut self, crypto_manager: &ItemCryptoManager, additional_data: &[u8]) -> Result<()> {
let meta = self.meta(crypto_manager, additional_data)?;
self.deleted = true;
self.set_meta(crypto_manager, additional_data, &meta)?;
Ok(())
}
}
#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct EncryptedItem {
uid: StringBase64,
version: u8,
#[serde(with = "serde_bytes")]
encryption_key: Option<Vec<u8>>,
content: EncryptedRevision,
etag: RefCell<Option<String>>,
}
impl EncryptedItem {
pub fn new(parent_crypto_manager: &CollectionCryptoManager, meta: &[u8], content: &[u8]) -> Result<Self> {
let uid = gen_uid_base64();
let version = CURRENT_VERSION;
let crypto_manager = Self::crypto_manager_static(parent_crypto_manager, &uid, version, None)?;
let content = EncryptedRevision::new(&crypto_manager, Self::additional_mac_data_static(&uid), &meta, content)?;
Ok(Self {
uid,
version,
encryption_key: None,
content,
etag: RefCell::new(None),
})
}
pub(crate) fn clone_with_revision(&self, revision: EncryptedRevision) -> Self {
let ret = Self {
uid: self.uid.to_string(),
version: self.version,
encryption_key: self.encryption_key.as_ref().map(|x| x.to_vec()),
content: revision,
etag: RefCell::new(None),
};
ret.mark_saved();
ret
}
pub fn cache_load(cached: &[u8]) -> Result<Self> {
let cached: CachedContent = rmp_serde::from_read_ref(cached)?;
Ok(rmp_serde::from_read_ref(&cached.data)?)
}
pub fn cache_save(&self) -> Result<Vec<u8>> {
let data = rmp_serde::to_vec(self)?;
let content = CachedContent {
version: 1,
data,
};
Ok(rmp_serde::to_vec(&content)?)
}
pub fn cache_save_with_content(&self) -> Result<Vec<u8>> {
let data = rmp_serde::to_vec(self)?;
let content = CachedContent {
version: 1,
data,
};
Ok(rmp_serde::to_vec(&content)?)
}
pub(crate) fn mark_saved(&self) {
*self.etag.borrow_mut() = Some(self.content.uid.clone());
}
pub fn is_locally_changed(&self) -> bool {
match self.etag.borrow().as_deref() {
Some(etag) => etag == self.content.uid,
None => false,
}
}
pub fn verify(&self, crypto_manager: &ItemCryptoManager) -> Result<bool> {
self.content.verify(crypto_manager, self.additional_mac_data())
}
pub fn set_meta(&mut self, crypto_manager: &ItemCryptoManager, meta: &[u8]) -> Result<()> {
let ad_mac_data = Self::additional_mac_data_static(&self.uid);
if self.is_locally_changed() {
self.content.set_meta(crypto_manager, ad_mac_data, meta)?;
} else {
let mut rev = self.content.clone();
rev.set_meta(crypto_manager, ad_mac_data, meta)?;
self.content = rev;
};
Ok(())
}
pub fn meta(&self, crypto_manager: &ItemCryptoManager) -> Result<Vec<u8>> {
self.verify(crypto_manager)?;
self.content.meta(crypto_manager, self.additional_mac_data())
}
pub fn set_content(&mut self, crypto_manager: &ItemCryptoManager, content: &[u8]) -> Result<()> {
let ad_mac_data = Self::additional_mac_data_static(&self.uid);
if self.is_locally_changed() {
self.content.set_content(crypto_manager, ad_mac_data, content)?;
} else {
let mut rev = self.content.clone();
rev.set_content(crypto_manager, ad_mac_data, content)?;
self.content = rev;
};
Ok(())
}
pub fn content(&self, crypto_manager: &ItemCryptoManager) -> Result<Vec<u8>> {
self.verify(crypto_manager)?;
self.content.content(crypto_manager)
}
pub fn delete(&mut self, crypto_manager: &ItemCryptoManager) -> Result<()> {
let ad_mac_data = Self::additional_mac_data_static(&self.uid);
if self.is_locally_changed() {
self.content.delete(crypto_manager, ad_mac_data)?;
} else {
let mut rev = self.content.clone();
rev.delete(crypto_manager, ad_mac_data)?;
self.content = rev;
};
Ok(())
}
pub fn is_deleted(&self) -> bool {
self.content.deleted
}
pub fn uid(&self) -> &str {
&self.uid
}
pub fn etag(&self) -> &str {
&self.content.uid
}
pub(crate) fn last_etag(&self) -> Option<String> {
self.etag.borrow().to_owned()
}
fn crypto_manager_static(parent_crypto_manager: &CollectionCryptoManager, uid: &str, version: u8, encryption_key: Option<&[u8]>) -> Result<ItemCryptoManager> {
let encryption_key = match encryption_key {
Some(encryption_key) => parent_crypto_manager.0.decrypt(encryption_key, None)?,
None => parent_crypto_manager.0.derive_subkey(uid.as_bytes())?,
};
ItemCryptoManager::new(try_into!(&encryption_key[..])?, version)
}
pub fn crypto_manager(&self, parent_crypto_manager: &CollectionCryptoManager) -> Result<ItemCryptoManager> {
let encryption_key = self.encryption_key.as_deref().map(|x| &x[..]);
Self::crypto_manager_static(parent_crypto_manager, &self.uid, self.version, encryption_key)
}
fn additional_mac_data_static(uid: &str) -> &[u8] {
uid.as_bytes()
}
fn additional_mac_data(&self) -> &[u8] {
Self::additional_mac_data_static(&self.uid)
}
pub(crate) fn pending_chunks(&self) -> impl Iterator<Item = &ChunkArrayItem> {
self.content.chunks.iter()
}
pub(crate) fn missing_chunks(&mut self) -> impl Iterator<Item = &mut ChunkArrayItem> {
self.content.chunks.iter_mut().filter(|x| x.1.is_none())
}
pub fn is_missing_content(&self) -> bool {
self.content.chunks.iter().any(|x| x.1.is_none())
}
pub(crate) fn test_chunk_uids(&self) -> Vec<String> {
self.content.chunks
.iter()
.map(|x| x.0.clone())
.collect()
}
}