1use crate::error::{CapError, CapResult};
9use crate::table::CapSlot;
10use crate::DEFAULT_MAX_DELEGATION_DEPTH;
11use rvm_types::{CapRights, CapToken};
12
13#[derive(Debug, Clone, Copy)]
15pub struct GrantPolicy {
16 pub max_depth: u8,
18 pub allow_grant_once: bool,
20}
21
22impl GrantPolicy {
23 #[must_use]
25 pub const fn new() -> Self {
26 Self {
27 max_depth: DEFAULT_MAX_DELEGATION_DEPTH,
28 allow_grant_once: true,
29 }
30 }
31
32 #[must_use]
34 pub const fn with_max_depth(max_depth: u8) -> Self {
35 Self {
36 max_depth,
37 allow_grant_once: true,
38 }
39 }
40}
41
42impl Default for GrantPolicy {
43 fn default() -> Self {
44 Self::new()
45 }
46}
47
48pub fn validate_grant(
56 source: &CapSlot,
57 requested_rights: CapRights,
58 new_id: u64,
59 badge: u64,
60 epoch: u32,
61 policy: GrantPolicy,
62) -> CapResult<(CapToken, u8, bool)> {
63 let source_rights = source.token.rights();
64
65 let has_grant = source_rights.contains(CapRights::GRANT);
66 let has_grant_once = policy.allow_grant_once && source_rights.contains(CapRights::GRANT_ONCE);
67
68 if !has_grant && !has_grant_once {
70 return Err(CapError::GrantNotPermitted);
71 }
72
73 if !source_rights.contains(requested_rights) {
75 return Err(CapError::RightsEscalation);
76 }
77
78 let new_depth = source
80 .depth
81 .checked_add(1)
82 .ok_or(CapError::DelegationDepthExceeded)?;
83 if new_depth > policy.max_depth {
84 return Err(CapError::DelegationDepthExceeded);
85 }
86
87 let _ = badge; let derived_token = CapToken::new(new_id, source.token.cap_type(), requested_rights, epoch);
90
91 let consume = !has_grant && has_grant_once;
94
95 Ok((derived_token, new_depth, consume))
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use rvm_types::{CapType, PartitionId};
102
103 fn make_source(rights: CapRights, depth: u8) -> CapSlot {
104 CapSlot {
105 token: CapToken::new(1, CapType::Region, rights, 0),
106 generation: 1,
107 owner: PartitionId::new(1),
108 depth,
109 parent_index: u32::MAX,
110 badge: 0,
111 }
112 }
113
114 fn all_rights() -> CapRights {
115 CapRights::READ
116 .union(CapRights::WRITE)
117 .union(CapRights::EXECUTE)
118 .union(CapRights::GRANT)
119 .union(CapRights::REVOKE)
120 }
121
122 #[test]
123 fn test_valid_grant() {
124 let source = make_source(all_rights(), 0);
125 let policy = GrantPolicy::new();
126 let (token, depth, consume) =
127 validate_grant(&source, CapRights::READ, 10, 42, 0, policy).unwrap();
128 assert_eq!(token.rights(), CapRights::READ);
129 assert_eq!(depth, 1);
130 assert!(!consume); }
132
133 #[test]
134 fn test_grant_without_grant_right() {
135 let source = make_source(CapRights::READ, 0);
136 let policy = GrantPolicy::new();
137 let result = validate_grant(&source, CapRights::READ, 10, 0, 0, policy);
138 assert_eq!(result, Err(CapError::GrantNotPermitted));
139 }
140
141 #[test]
142 fn test_rights_escalation() {
143 let source = make_source(CapRights::READ.union(CapRights::GRANT), 0);
144 let policy = GrantPolicy::new();
145 let result = validate_grant(&source, CapRights::WRITE, 10, 0, 0, policy);
146 assert_eq!(result, Err(CapError::RightsEscalation));
147 }
148
149 #[test]
150 fn test_depth_limit() {
151 let source = make_source(all_rights(), 8);
152 let policy = GrantPolicy::new();
153 let result = validate_grant(&source, CapRights::READ, 10, 0, 0, policy);
154 assert_eq!(result, Err(CapError::DelegationDepthExceeded));
155 }
156
157 #[test]
158 fn test_grant_preserves_type() {
159 let source = CapSlot {
160 token: CapToken::new(1, CapType::CommEdge, all_rights(), 5),
161 generation: 1,
162 owner: PartitionId::new(1),
163 depth: 0,
164 parent_index: u32::MAX,
165 badge: 0,
166 };
167 let policy = GrantPolicy::new();
168 let (token, _, _) = validate_grant(&source, CapRights::READ, 10, 0, 5, policy).unwrap();
169 assert_eq!(token.cap_type(), CapType::CommEdge);
170 assert_eq!(token.epoch(), 5);
171 }
172
173 #[test]
174 fn test_grant_at_max_minus_one() {
175 let source = make_source(all_rights(), 7);
176 let policy = GrantPolicy::new();
177 let (_, depth, _) = validate_grant(&source, CapRights::READ, 10, 0, 0, policy).unwrap();
178 assert_eq!(depth, 8);
179 }
180
181 #[test]
182 fn test_grant_once_consumed() {
183 let rights = CapRights::READ.union(CapRights::GRANT_ONCE);
185 let source = make_source(rights, 0);
186 let policy = GrantPolicy::new();
187 let (token, depth, consume) =
188 validate_grant(&source, CapRights::READ, 10, 0, 0, policy).unwrap();
189 assert_eq!(token.rights(), CapRights::READ);
190 assert_eq!(depth, 1);
191 assert!(consume); }
193
194 #[test]
195 fn test_grant_once_not_consumed_when_grant_also_present() {
196 let rights = CapRights::READ
198 .union(CapRights::GRANT)
199 .union(CapRights::GRANT_ONCE);
200 let source = make_source(rights, 0);
201 let policy = GrantPolicy::new();
202 let (_, _, consume) = validate_grant(&source, CapRights::READ, 10, 0, 0, policy).unwrap();
203 assert!(!consume);
204 }
205
206 #[test]
207 fn test_depth_overflow_protection() {
208 let source = make_source(all_rights(), u8::MAX);
209 let policy = GrantPolicy::with_max_depth(u8::MAX);
210 let result = validate_grant(&source, CapRights::READ, 10, 0, 0, policy);
211 assert_eq!(result, Err(CapError::DelegationDepthExceeded));
212 }
213}