use crate::{Error, KeyId};
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Permissions {
pub app: AppPermissions,
pub key: KeyPermissions,
}
impl Permissions {
pub const FACTORY_DEFAULT: Self = Self {
app: AppPermissions {
can_change_key_settings: true,
can_change_root_key: true,
anyone_can_delete: true,
anyone_can_list: true,
},
key: KeyPermissions::RequiresRoot,
};
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct AppPermissions {
pub can_change_key_settings: bool,
pub can_change_root_key: bool,
pub anyone_can_delete: bool,
pub anyone_can_list: bool,
}
impl AppPermissions {
const CAN_CHANGE_ROOT_KEY: u8 = 0x01;
const ANYONE_CAN_LIST: u8 = 0x02;
const ANYONE_CAN_DELETE: u8 = 0x04;
const CAN_CHANGE_KEY_SETTINGS: u8 = 0x08;
fn from_u8(ks: u8) -> Self {
Self {
can_change_root_key: (ks & Self::CAN_CHANGE_ROOT_KEY) != 0,
anyone_can_list: (ks & Self::ANYONE_CAN_LIST) != 0,
anyone_can_delete: (ks & Self::ANYONE_CAN_DELETE) != 0,
can_change_key_settings: (ks & Self::CAN_CHANGE_KEY_SETTINGS) != 0,
}
}
pub fn as_u8(&self) -> u8 {
(if self.can_change_root_key {
Self::CAN_CHANGE_ROOT_KEY
} else {
0
}) | (if self.anyone_can_list {
Self::ANYONE_CAN_LIST
} else {
0
}) | (if self.anyone_can_delete {
Self::ANYONE_CAN_DELETE
} else {
0
}) | (if self.can_change_key_settings {
Self::CAN_CHANGE_KEY_SETTINGS
} else {
0
})
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum KeyPermissions {
RequiresRoot,
RequiresAppKey(KeyId),
RequiresTargetedAppKey,
Frozen,
}
impl KeyPermissions {
fn from_u8(ks: u8) -> Self {
match ks >> 4 {
0x00 => Self::RequiresRoot,
0x0E => Self::RequiresTargetedAppKey,
0x0F => Self::Frozen,
ks => Self::RequiresAppKey(ks),
}
}
fn as_u8<IoBackendErrorT>(&self) -> Result<u8, Error<IoBackendErrorT>> {
Ok((match &self {
Self::RequiresRoot => 0x00,
Self::RequiresAppKey(key @ 0x01..=0x0D) => *key,
Self::RequiresAppKey(_) => return Err(Error::BadKeyId),
Self::RequiresTargetedAppKey => 0x0E,
Self::Frozen => 0x0F,
}) << 4)
}
}
impl Permissions {
pub fn from_u8(ks: u8) -> Self {
Self {
app: AppPermissions::from_u8(ks),
key: KeyPermissions::from_u8(ks),
}
}
pub fn as_u8<IoBackendErrorT>(&self) -> Result<u8, Error<IoBackendErrorT>> {
Ok(self.app.as_u8() | self.key.as_u8()?)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_settings_round_trips() {
for key_settings in 0..0xff {
let key_settings_parsed = Permissions::from_u8(key_settings);
let key_settings_rt = key_settings_parsed.as_u8::<()>().unwrap();
assert_eq!(key_settings, key_settings_rt);
}
}
macro_rules! test_key_settings {
($name:ident { $settings:expr, $key_settings:expr }) => {
#[test]
fn $name() {
let key_settings = Permissions::from_u8($settings);
assert_eq!($key_settings, key_settings);
let key_settings_u8 = key_settings.as_u8::<()>().unwrap();
assert_eq!($settings, key_settings_u8);
}
};
}
test_key_settings!(test_factory_default { 0x0F, Permissions::FACTORY_DEFAULT });
test_key_settings!(test_app_frozen { 0x00, Permissions {
app: AppPermissions {
can_change_key_settings: false,
can_change_root_key: false,
anyone_can_delete: false,
anyone_can_list: false,
},
key: KeyPermissions::RequiresRoot,
} });
test_key_settings!(test_app_a { 0xAF, Permissions {
app: AppPermissions {
can_change_key_settings: true,
can_change_root_key: true,
anyone_can_delete: true,
anyone_can_list: true,
},
key: KeyPermissions::RequiresAppKey(10),
} });
macro_rules! test_bad_key_settings {
($name:ident { $key_settings:expr }) => {
#[test]
fn $name() {
assert!($key_settings.as_u8::<()>().is_err());
}
};
}
test_bad_key_settings!(test_bad_app_0 { Permissions {
app: AppPermissions {
can_change_key_settings: true,
can_change_root_key: true,
anyone_can_delete: true,
anyone_can_list: true,
},
key: KeyPermissions::RequiresAppKey(0),
} });
test_bad_key_settings!(test_bad_app_e { Permissions {
app: AppPermissions {
can_change_key_settings: true,
can_change_root_key: true,
anyone_can_delete: true,
anyone_can_list: true,
},
key: KeyPermissions::RequiresAppKey(0xE),
} });
}