policy.rs (6279B)
1 use crate::error::{RadrootsHostVaultRequirement, RadrootsSecretVaultError}; 2 3 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 4 pub enum RadrootsHostVaultResidency { 5 UserProfile, 6 DeviceLocalOnly, 7 } 8 9 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 10 pub enum RadrootsHostVaultUserPresencePolicy { 11 NotRequired, 12 Required, 13 } 14 15 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 16 pub enum RadrootsHostVaultHardwarePolicy { 17 Any, 18 PreferHardwareBacked, 19 RequireHardwareBacked, 20 } 21 22 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 23 pub struct RadrootsHostVaultPolicy { 24 pub residency: RadrootsHostVaultResidency, 25 pub user_presence: RadrootsHostVaultUserPresencePolicy, 26 pub hardware: RadrootsHostVaultHardwarePolicy, 27 } 28 29 impl RadrootsHostVaultPolicy { 30 #[must_use] 31 pub const fn desktop() -> Self { 32 Self { 33 residency: RadrootsHostVaultResidency::UserProfile, 34 user_presence: RadrootsHostVaultUserPresencePolicy::NotRequired, 35 hardware: RadrootsHostVaultHardwarePolicy::Any, 36 } 37 } 38 39 #[must_use] 40 pub const fn device_local() -> Self { 41 Self { 42 residency: RadrootsHostVaultResidency::DeviceLocalOnly, 43 user_presence: RadrootsHostVaultUserPresencePolicy::NotRequired, 44 hardware: RadrootsHostVaultHardwarePolicy::Any, 45 } 46 } 47 } 48 49 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 50 pub struct RadrootsHostVaultCapabilities { 51 pub available: bool, 52 pub supports_device_local_only: bool, 53 pub supports_user_presence: bool, 54 pub supports_hardware_backed: bool, 55 } 56 57 impl RadrootsHostVaultCapabilities { 58 #[must_use] 59 pub const fn unavailable() -> Self { 60 Self { 61 available: false, 62 supports_device_local_only: false, 63 supports_user_presence: false, 64 supports_hardware_backed: false, 65 } 66 } 67 68 #[must_use] 69 pub const fn desktop_keyring() -> Self { 70 Self { 71 available: true, 72 supports_device_local_only: false, 73 supports_user_presence: false, 74 supports_hardware_backed: false, 75 } 76 } 77 78 #[must_use] 79 pub const fn secure_device() -> Self { 80 Self { 81 available: true, 82 supports_device_local_only: true, 83 supports_user_presence: true, 84 supports_hardware_backed: true, 85 } 86 } 87 88 pub const fn validate( 89 self, 90 policy: RadrootsHostVaultPolicy, 91 ) -> Result<(), RadrootsSecretVaultError> { 92 if !self.available { 93 return Err(RadrootsSecretVaultError::BackendUnavailable { 94 backend: crate::backend::RadrootsSecretBackendKind::HostVault, 95 }); 96 } 97 98 if matches!( 99 policy.residency, 100 RadrootsHostVaultResidency::DeviceLocalOnly 101 ) && !self.supports_device_local_only 102 { 103 return Err(RadrootsSecretVaultError::HostVaultPolicyUnsupported { 104 requirement: RadrootsHostVaultRequirement::DeviceLocalOnly, 105 }); 106 } 107 108 if matches!( 109 policy.user_presence, 110 RadrootsHostVaultUserPresencePolicy::Required 111 ) && !self.supports_user_presence 112 { 113 return Err(RadrootsSecretVaultError::HostVaultPolicyUnsupported { 114 requirement: RadrootsHostVaultRequirement::UserPresence, 115 }); 116 } 117 118 if matches!( 119 policy.hardware, 120 RadrootsHostVaultHardwarePolicy::RequireHardwareBacked 121 ) && !self.supports_hardware_backed 122 { 123 return Err(RadrootsSecretVaultError::HostVaultPolicyUnsupported { 124 requirement: RadrootsHostVaultRequirement::HardwareBacked, 125 }); 126 } 127 128 Ok(()) 129 } 130 } 131 132 #[cfg(test)] 133 mod tests { 134 use super::*; 135 136 #[test] 137 fn device_local_policy_and_secure_device_capabilities_are_explicit() { 138 assert_eq!( 139 RadrootsHostVaultPolicy::device_local(), 140 RadrootsHostVaultPolicy { 141 residency: RadrootsHostVaultResidency::DeviceLocalOnly, 142 user_presence: RadrootsHostVaultUserPresencePolicy::NotRequired, 143 hardware: RadrootsHostVaultHardwarePolicy::Any, 144 } 145 ); 146 assert_eq!( 147 RadrootsHostVaultCapabilities::secure_device(), 148 RadrootsHostVaultCapabilities { 149 available: true, 150 supports_device_local_only: true, 151 supports_user_presence: true, 152 supports_hardware_backed: true, 153 } 154 ); 155 assert_eq!( 156 RadrootsHostVaultCapabilities::secure_device() 157 .validate(RadrootsHostVaultPolicy::device_local()), 158 Ok(()) 159 ); 160 } 161 162 #[test] 163 fn validate_reports_user_presence_and_hardware_requirements() { 164 let user_presence_policy = RadrootsHostVaultPolicy { 165 residency: RadrootsHostVaultResidency::UserProfile, 166 user_presence: RadrootsHostVaultUserPresencePolicy::Required, 167 hardware: RadrootsHostVaultHardwarePolicy::Any, 168 }; 169 assert_eq!( 170 RadrootsHostVaultCapabilities::desktop_keyring().validate(user_presence_policy), 171 Err(RadrootsSecretVaultError::HostVaultPolicyUnsupported { 172 requirement: RadrootsHostVaultRequirement::UserPresence, 173 }) 174 ); 175 assert_eq!( 176 RadrootsHostVaultCapabilities::secure_device().validate(user_presence_policy), 177 Ok(()) 178 ); 179 180 let hardware_policy = RadrootsHostVaultPolicy { 181 residency: RadrootsHostVaultResidency::UserProfile, 182 user_presence: RadrootsHostVaultUserPresencePolicy::NotRequired, 183 hardware: RadrootsHostVaultHardwarePolicy::RequireHardwareBacked, 184 }; 185 assert_eq!( 186 RadrootsHostVaultCapabilities::desktop_keyring().validate(hardware_policy), 187 Err(RadrootsSecretVaultError::HostVaultPolicyUnsupported { 188 requirement: RadrootsHostVaultRequirement::HardwareBacked, 189 }) 190 ); 191 assert_eq!( 192 RadrootsHostVaultCapabilities::secure_device().validate(hardware_policy), 193 Ok(()) 194 ); 195 } 196 }