use crate::error::PrivacyError;
use crate::keyhop::{aes_gcm_encrypt, hkdf_derive};
use sha2::{Digest, Sha256};
use serde::{Deserialize, Serialize};
extern crate alloc;
use alloc::{collections::BTreeMap, string::String, vec::Vec};
pub const PAGE_SIZE: usize = 65_536;
pub const MAX_KEYS: u8 = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PagePerm {
None,
Read,
Write,
ReadWrite,
Execute,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryPage {
pub index: usize,
pub key: u8,
pub perm: PagePerm,
pub data: Vec<u8>,
pub tenant: String,
}
pub struct WavenEnclave {
pages: BTreeMap<usize, MemoryPage>,
keys: BTreeMap<u8, String>,
next_key: u8,
}
impl WavenEnclave {
pub fn new() -> Self {
Self {
pages: BTreeMap::new(),
keys: BTreeMap::new(),
next_key: 0,
}
}
pub fn register_tenant(
&mut self,
tenant_did: impl Into<String>,
) -> Result<u8, PrivacyError> {
if self.next_key >= MAX_KEYS {
return Err(PrivacyError::AttestationFailed(
"Maximum tenants reached".into()
));
}
let key = self.next_key;
self.keys.insert(key, tenant_did.into());
self.next_key += 1;
Ok(key)
}
pub fn allocate_page(
&mut self,
page_index: usize,
tenant_key: u8,
perm: PagePerm,
chaos_seed: &[u8; 32],
) -> Result<(), PrivacyError> {
let tenant = self.keys.get(&tenant_key)
.ok_or(PrivacyError::PageAccessDenied { key: tenant_key, page: page_index })?
.clone();
let mut hasher = Sha256::new();
hasher.update(page_index.to_le_bytes());
hasher.update([tenant_key]);
hasher.update(chaos_seed);
hasher.update(b"page-init-v1");
let init_data: Vec<u8> = hasher.finalize().to_vec();
self.pages.insert(page_index, MemoryPage {
index: page_index,
key: tenant_key,
perm,
data: init_data,
tenant,
});
Ok(())
}
pub fn read_page(
&self,
page_index: usize,
tenant_key: u8,
) -> Result<&[u8], PrivacyError> {
let page = self.pages.get(&page_index)
.ok_or(PrivacyError::PageAccessDenied { key: tenant_key, page: page_index })?;
if page.key != tenant_key {
return Err(PrivacyError::PageAccessDenied { key: tenant_key, page: page_index });
}
match page.perm {
PagePerm::Read | PagePerm::ReadWrite => Ok(&page.data),
_ => Err(PrivacyError::PageAccessDenied { key: tenant_key, page: page_index }),
}
}
pub fn write_page(
&mut self,
page_index: usize,
tenant_key: u8,
data: Vec<u8>,
chaos_seed: &[u8; 32],
) -> Result<(), PrivacyError> {
let page = self.pages.get_mut(&page_index)
.ok_or(PrivacyError::PageAccessDenied { key: tenant_key, page: page_index })?;
if page.key != tenant_key {
return Err(PrivacyError::PageAccessDenied { key: tenant_key, page: page_index });
}
match page.perm {
PagePerm::Write | PagePerm::ReadWrite => {
let _ = chaos_seed[0]; page.data = data;
Ok(())
}
_ => Err(PrivacyError::PageAccessDenied { key: tenant_key, page: page_index }),
}
}
pub fn seal_on_exit(&mut self, chaos_seed: &[u8; 32]) {
let page_indices: alloc::vec::Vec<usize> = self.pages.keys().copied().collect();
for page_index in page_indices {
if let Some(page) = self.pages.get_mut(&page_index) {
let mut info = [0u8; 9]; info[..8].copy_from_slice(&(page_index as u64).to_le_bytes());
info[8] = page.key;
let mut seal_key = [0u8; 32];
if hkdf_derive(chaos_seed, &info, b"enclave-seal-v1", &mut seal_key).is_ok() {
match aes_gcm_encrypt(&seal_key, &page.data) {
Ok(sealed) => page.data = sealed,
Err(_) => {
let mut hasher = Sha256::new();
hasher.update(&page.data);
hasher.update(chaos_seed);
page.data = hasher.finalize().to_vec();
}
}
}
}
}
}
pub fn share_page(
&mut self,
page_index: usize,
from_key: u8,
to_key: u8,
) -> Result<(), PrivacyError> {
let page = self.pages.get(&page_index)
.ok_or(PrivacyError::PageAccessDenied { key: from_key, page: page_index })?;
if page.key != from_key {
return Err(PrivacyError::PageAccessDenied { key: from_key, page: page_index });
}
if !self.keys.contains_key(&to_key) {
return Err(PrivacyError::PageAccessDenied { key: to_key, page: page_index });
}
let shared_data = page.data.clone();
let to_tenant = self.keys[&to_key].clone();
let shared_page = MemoryPage {
index: page_index + 10000, key: to_key,
perm: PagePerm::ReadWrite,
data: shared_data,
tenant: to_tenant,
};
self.pages.insert(shared_page.index, shared_page);
Ok(())
}
pub fn page_count(&self) -> usize {
self.pages.len()
}
pub fn tenant_count(&self) -> usize {
self.keys.len()
}
}
impl Default for WavenEnclave {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_register_allocate_read() {
let mut enclave = WavenEnclave::new();
let seed = [0u8; 32];
let key = enclave.register_tenant("did:wyqcc:tenant1").unwrap();
enclave.allocate_page(0, key, PagePerm::ReadWrite, &seed).unwrap();
let data = enclave.read_page(0, key).unwrap();
assert!(!data.is_empty());
}
#[test]
fn test_access_denied() {
let mut enclave = WavenEnclave::new();
let seed = [0u8; 32];
let key1 = enclave.register_tenant("did:wyqcc:t1").unwrap();
let key2 = enclave.register_tenant("did:wyqcc:t2").unwrap();
enclave.allocate_page(0, key1, PagePerm::ReadWrite, &seed).unwrap();
assert!(enclave.read_page(0, key2).is_err());
}
#[test]
fn test_seal_on_exit() {
let mut enclave = WavenEnclave::new();
let seed = [0u8; 32];
let key = enclave.register_tenant("did:wyqcc:t1").unwrap();
enclave.allocate_page(0, key, PagePerm::ReadWrite, &seed).unwrap();
enclave.seal_on_exit(&seed);
assert_eq!(enclave.page_count(), 1);
}
}