use crate::cap::Cap;
use crate::error::CapSecError;
use crate::permission::Permission;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Instant;
#[derive(Debug, Clone)]
pub struct LogEntry {
pub timestamp: Instant,
pub permission: &'static str,
pub granted: bool,
}
pub struct LoggedCap<P: Permission> {
_phantom: PhantomData<P>,
_not_send: PhantomData<*const ()>,
log: Arc<Mutex<Vec<LogEntry>>>,
}
impl<P: Permission> LoggedCap<P> {
pub fn new(_cap: Cap<P>) -> Self {
Self {
_phantom: PhantomData,
_not_send: PhantomData,
log: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn try_cap(&self) -> Result<Cap<P>, CapSecError> {
let entry = LogEntry {
timestamp: Instant::now(),
permission: std::any::type_name::<P>(),
granted: true,
};
let mut log = self.log.lock().unwrap_or_else(|e| e.into_inner());
log.push(entry);
Ok(Cap::new())
}
pub fn is_active(&self) -> bool {
true
}
pub fn entries(&self) -> Vec<LogEntry> {
let log = self.log.lock().unwrap_or_else(|e| e.into_inner());
log.clone()
}
pub fn entry_count(&self) -> usize {
let log = self.log.lock().unwrap_or_else(|e| e.into_inner());
log.len()
}
pub fn make_send(self) -> LoggedSendCap<P> {
LoggedSendCap {
_phantom: PhantomData,
log: self.log,
}
}
}
impl<P: Permission> Clone for LoggedCap<P> {
fn clone(&self) -> Self {
Self {
_phantom: PhantomData,
_not_send: PhantomData,
log: Arc::clone(&self.log),
}
}
}
pub struct LoggedSendCap<P: Permission> {
_phantom: PhantomData<P>,
log: Arc<Mutex<Vec<LogEntry>>>,
}
unsafe impl<P: Permission> Send for LoggedSendCap<P> {}
unsafe impl<P: Permission> Sync for LoggedSendCap<P> {}
impl<P: Permission> LoggedSendCap<P> {
pub fn try_cap(&self) -> Result<Cap<P>, CapSecError> {
let entry = LogEntry {
timestamp: Instant::now(),
permission: std::any::type_name::<P>(),
granted: true,
};
let mut log = self.log.lock().unwrap_or_else(|e| e.into_inner());
log.push(entry);
Ok(Cap::new())
}
pub fn is_active(&self) -> bool {
true
}
pub fn entries(&self) -> Vec<LogEntry> {
let log = self.log.lock().unwrap_or_else(|e| e.into_inner());
log.clone()
}
pub fn entry_count(&self) -> usize {
let log = self.log.lock().unwrap_or_else(|e| e.into_inner());
log.len()
}
}
impl<P: Permission> Clone for LoggedSendCap<P> {
fn clone(&self) -> Self {
Self {
_phantom: PhantomData,
log: Arc::clone(&self.log),
}
}
}
pub struct DualKeyCap<P: Permission> {
_phantom: PhantomData<P>,
_not_send: PhantomData<*const ()>,
approvals: Arc<AtomicU8>,
}
impl<P: Permission> DualKeyCap<P> {
pub fn new(_cap: Cap<P>) -> (Self, ApproverA, ApproverB) {
let approvals = Arc::new(AtomicU8::new(0));
let cap = Self {
_phantom: PhantomData,
_not_send: PhantomData,
approvals: Arc::clone(&approvals),
};
let a = ApproverA {
approvals: Arc::clone(&approvals),
};
let b = ApproverB { approvals };
(cap, a, b)
}
pub fn try_cap(&self) -> Result<Cap<P>, CapSecError> {
if self.approvals.load(Ordering::Acquire) == 3 {
Ok(Cap::new())
} else {
Err(CapSecError::InsufficientApprovals)
}
}
pub fn is_active(&self) -> bool {
self.approvals.load(Ordering::Acquire) == 3
}
pub fn make_send(self) -> DualKeySendCap<P> {
DualKeySendCap {
_phantom: PhantomData,
approvals: self.approvals,
}
}
}
impl<P: Permission> Clone for DualKeyCap<P> {
fn clone(&self) -> Self {
Self {
_phantom: PhantomData,
_not_send: PhantomData,
approvals: Arc::clone(&self.approvals),
}
}
}
pub struct DualKeySendCap<P: Permission> {
_phantom: PhantomData<P>,
approvals: Arc<AtomicU8>,
}
unsafe impl<P: Permission> Send for DualKeySendCap<P> {}
unsafe impl<P: Permission> Sync for DualKeySendCap<P> {}
impl<P: Permission> DualKeySendCap<P> {
pub fn try_cap(&self) -> Result<Cap<P>, CapSecError> {
if self.approvals.load(Ordering::Acquire) == 3 {
Ok(Cap::new())
} else {
Err(CapSecError::InsufficientApprovals)
}
}
pub fn is_active(&self) -> bool {
self.approvals.load(Ordering::Acquire) == 3
}
}
impl<P: Permission> Clone for DualKeySendCap<P> {
fn clone(&self) -> Self {
Self {
_phantom: PhantomData,
approvals: Arc::clone(&self.approvals),
}
}
}
pub struct ApproverA {
approvals: Arc<AtomicU8>,
}
impl ApproverA {
pub fn approve(&self) {
self.approvals.fetch_or(0b01, Ordering::Release);
}
pub fn is_approved(&self) -> bool {
self.approvals.load(Ordering::Acquire) & 0b01 != 0
}
}
pub struct ApproverB {
approvals: Arc<AtomicU8>,
}
impl ApproverB {
pub fn approve(&self) {
self.approvals.fetch_or(0b10, Ordering::Release);
}
pub fn is_approved(&self) -> bool {
self.approvals.load(Ordering::Acquire) & 0b10 != 0
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::permission::FsRead;
use std::mem::size_of;
#[test]
fn logged_cap_try_cap_succeeds() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let lcap = LoggedCap::new(cap);
assert!(lcap.try_cap().is_ok());
}
#[test]
fn logged_cap_records_entry() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let lcap = LoggedCap::new(cap);
let _ = lcap.try_cap();
let entries = lcap.entries();
assert_eq!(entries.len(), 1);
assert!(entries[0].permission.contains("FsRead"));
assert!(entries[0].granted);
}
#[test]
fn logged_cap_multiple_entries() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let lcap = LoggedCap::new(cap);
let _ = lcap.try_cap();
let _ = lcap.try_cap();
let _ = lcap.try_cap();
assert_eq!(lcap.entry_count(), 3);
}
#[test]
fn logged_cap_entries_snapshot() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let lcap = LoggedCap::new(cap);
let _ = lcap.try_cap();
let snapshot = lcap.entries();
let _ = lcap.try_cap();
assert_eq!(snapshot.len(), 1);
assert_eq!(lcap.entry_count(), 2);
}
#[test]
fn logged_send_cap_crosses_threads() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let lcap = LoggedCap::new(cap);
let send_cap = lcap.make_send();
std::thread::spawn(move || {
assert!(send_cap.try_cap().is_ok());
})
.join()
.unwrap();
}
#[test]
fn cloned_logged_cap_shares_log() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let lcap = LoggedCap::new(cap);
let lcap2 = lcap.clone();
let _ = lcap.try_cap();
let _ = lcap2.try_cap();
assert_eq!(lcap.entry_count(), 2);
assert_eq!(lcap2.entry_count(), 2);
}
#[test]
fn logged_cap_is_small() {
assert!(size_of::<LoggedCap<FsRead>>() <= 2 * size_of::<usize>());
}
#[test]
fn logged_cap_entry_has_correct_permission_name() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let lcap = LoggedCap::new(cap);
let _ = lcap.try_cap();
let entries = lcap.entries();
assert!(entries[0].permission.contains("FsRead"));
}
#[test]
fn dual_key_try_cap_fails_without_approvals() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let (dcap, _a, _b) = DualKeyCap::new(cap);
assert!(matches!(
dcap.try_cap(),
Err(CapSecError::InsufficientApprovals)
));
}
#[test]
fn dual_key_try_cap_fails_with_one_approval() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let (dcap, a, _b) = DualKeyCap::new(cap);
a.approve();
assert!(matches!(
dcap.try_cap(),
Err(CapSecError::InsufficientApprovals)
));
}
#[test]
fn dual_key_try_cap_succeeds_with_both_approvals() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let (dcap, a, b) = DualKeyCap::new(cap);
a.approve();
b.approve();
assert!(dcap.try_cap().is_ok());
}
#[test]
fn dual_key_approval_order_irrelevant() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let (dcap, a, b) = DualKeyCap::new(cap);
b.approve();
a.approve();
assert!(dcap.try_cap().is_ok());
}
#[test]
fn dual_key_approve_is_idempotent() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let (dcap, a, _b) = DualKeyCap::new(cap);
a.approve();
a.approve(); assert!(matches!(
dcap.try_cap(),
Err(CapSecError::InsufficientApprovals)
));
}
#[test]
fn dual_key_approvers_are_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<ApproverA>();
assert_send_sync::<ApproverB>();
}
#[test]
fn dual_key_send_cap_crosses_threads() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let (dcap, a, b) = DualKeyCap::new(cap);
a.approve();
b.approve();
let send_cap = dcap.make_send();
std::thread::spawn(move || {
assert!(send_cap.try_cap().is_ok());
})
.join()
.unwrap();
}
#[test]
fn dual_key_approval_crosses_threads() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let (dcap, a, b) = DualKeyCap::new(cap);
a.approve();
std::thread::spawn(move || {
b.approve();
})
.join()
.unwrap();
assert!(dcap.try_cap().is_ok());
}
#[test]
fn cloned_dual_key_shares_approval() {
let root = crate::root::test_root();
let cap = root.grant::<FsRead>();
let (dcap, a, b) = DualKeyCap::new(cap);
let dcap2 = dcap.clone();
a.approve();
b.approve();
assert!(dcap.try_cap().is_ok());
assert!(dcap2.try_cap().is_ok());
}
#[test]
fn dual_key_cap_is_small() {
assert!(size_of::<DualKeyCap<FsRead>>() <= 2 * size_of::<usize>());
}
}