twizzler_security/
capability.rs

1#[cfg(feature = "log")]
2use log::debug;
3use sha2::{Digest, Sha256};
4use twizzler_abi::object::{ObjID, Protections};
5
6use crate::{
7    flags::{CapFlags, HashingAlgo},
8    Gate, Revoc, SecurityError, Signature, SigningKey, VerifyingKey,
9};
10
11/// A capability that represents authorization for a [Security Context](`crate::sec_ctx::SecCtx`) to
12/// access an object.
13///
14/// Capabilities are stored inside [`crate::sec_ctx::SecCtx`], and are authenticated
15/// using cryptographic signatures. When accessing an object for the first time,
16/// the kernel searches through the attached [Security Context](`crate::sec_ctx::SecCtx`) for
17/// a usable capability. If none found it will look through inactive contexts for a valid
18/// capability and then procedes to verify its signature in order to grant access rights.
19///
20///
21/// # Fields
22///
23/// * `target` - The object ID this capability grants access to
24/// * `accessor` - The security context ID in which this capability resides
25/// * `protections` - The specific access rights this capability grants
26/// * `flags` - Specifies the cryptographic primitives used to form the signature
27/// * `gates` - Allows access into an object in a specified range
28/// * `revocation` - Specifies when the capability is invalid
29/// * `signature` - the signature of the capability
30///
31/// # Examples
32///
33/// ```
34/// // Example of creating and using a capability
35/// todo
36/// ```
37#[derive(Clone, PartialEq, Eq, Debug)]
38pub struct Cap {
39    /// Object ID this capability grants access to
40    pub target: ObjID,
41
42    /// Security context ID in which this capability resides
43    pub accessor: ObjID,
44
45    /// Specific access rights this capability grants
46    pub protections: Protections,
47
48    /// Cryptographic configuration for capability validation
49    flags: CapFlags,
50
51    /// Additional constraints on when this capability can be used
52    gate: Gate,
53
54    /// Specifies when this capability is invalid, i.e. expiration.
55    pub revocation: Revoc,
56
57    /// The signature inside the capability
58    sig: Signature,
59}
60
61const CAP_SERIALIZED_LEN: usize = 78;
62
63impl Cap {
64    /// creating a new capability, revoc specified in expiration data in ns from unix epoch
65    pub fn new(
66        target: ObjID,
67        accessor: ObjID,
68        prots: Protections,
69        target_priv_key: &SigningKey,
70        revocation: Revoc,
71        gates: Gate,
72        hashing_algo: HashingAlgo,
73    ) -> Result<Self, SecurityError> {
74        let flags: CapFlags = hashing_algo.clone().into();
75
76        #[cfg(feature = "log")]
77        debug!(
78            "Using flags: {} to create capability for target: {:?}",
79            flags, target
80        );
81
82        let hash_arr = Cap::serialize(accessor, target, prots, flags, revocation, gates);
83
84        let sig = match hashing_algo {
85            HashingAlgo::Blake3 => {
86                // unimplemented!("running into problems with blake3 compilation on aarch64");
87                let hash = blake3::hash(&hash_arr);
88                target_priv_key.sign(hash.as_bytes())?
89            }
90            HashingAlgo::Sha256 => {
91                let mut hasher = Sha256::new();
92                hasher.update(hash_arr);
93                let hash = hasher.finalize();
94                target_priv_key.sign(hash.as_slice())?
95            }
96        };
97
98        Ok(Cap {
99            accessor,
100            target,
101            protections: prots,
102            flags,
103            revocation,
104            gate: gates,
105            sig,
106        })
107    }
108
109    /// verifies signature inside capability
110
111    pub fn verify_sig(&self, verifying_key: &VerifyingKey) -> Result<(), SecurityError> {
112        let hash_arr = Self::serialize(
113            self.accessor,
114            self.target,
115            self.protections,
116            self.flags,
117            self.revocation,
118            self.gate,
119        );
120
121        let hash_algo: HashingAlgo = self.flags.try_into()?;
122
123        match hash_algo {
124            HashingAlgo::Blake3 => {
125                // #[cfg(feature = "log")]
126                // error!("running into problems with blake3 compilation on aarch64");
127                // unimplemented!("running into problems with blake3 compilation on aarch64");
128                let hash = blake3::hash(&hash_arr);
129                let bind = hash.as_bytes();
130                verifying_key.verify(bind.as_slice(), &self.sig)
131            }
132            HashingAlgo::Sha256 => {
133                #[cfg(feature = "log")]
134                debug!("Hashing via Sha256");
135                let mut hasher = sha2::Sha256::new();
136                hasher.update(&hash_arr);
137                let result = hasher.finalize();
138                verifying_key.verify(result.as_slice(), &self.sig)
139            }
140        }
141    }
142
143    /// checks to see if the specified ptr_offset falls in the capability's gate.
144    pub fn check_gate(&self, ptr_offset: u64, align: u64) -> Result<(), SecurityError> {
145        // The `offset` and `length` fields specify a region within the object. When the
146        // kernel switches a thread's active context, in addition to the validity checks described
147        // in section 3.x, it checks to see if the instruction pointer is in a valid gate
148        // for the object it points to. The instruction pointer must reside within the
149        // region specified by `offset` and `length`, and must be aligned on a value specified
150        // by `align`. If either of these is not true, the kernel will not consider that security
151        // context valid to switch to. Note that we can recover the original sematics where we did
152        // not perform this check by setting `offset` and `length` to cover the entire object, and
153        // `align` to 1.
154
155        // the pointer is less than the actual offset
156        if ptr_offset < self.gate.offset {
157            return Err(SecurityError::GateDenied);
158        }
159
160        // the access is beyond the "end" of the gate
161        if self.gate.offset + self.gate.length < ptr_offset {
162            return Err(SecurityError::GateDenied);
163        }
164
165        //NOTE: not completely sure this is how you check alignment.
166        if self.gate.align != align {
167            return Err(SecurityError::GateDenied);
168        }
169
170        Ok(())
171    }
172
173    /// returns all contents other than sig as a buffer ready to hash
174    fn serialize(
175        accessor: ObjID,
176        target: ObjID,
177        prots: Protections,
178        flags: CapFlags,
179        revocation: Revoc,
180        gates: Gate,
181    ) -> [u8; CAP_SERIALIZED_LEN] {
182        let mut hash_arr: [u8; CAP_SERIALIZED_LEN] = [0; CAP_SERIALIZED_LEN];
183        hash_arr[0..16].copy_from_slice(&accessor.raw().to_le_bytes());
184        hash_arr[16..32].copy_from_slice(&target.raw().to_le_bytes());
185        hash_arr[32..34].copy_from_slice(&prots.bits().to_le_bytes());
186        hash_arr[34..36].copy_from_slice(&flags.bits().to_le_bytes());
187        hash_arr[36..52].copy_from_slice(&revocation.to_bytes());
188        hash_arr[52..60].copy_from_slice(&gates.offset.to_le_bytes());
189        hash_arr[60..68].copy_from_slice(&gates.length.to_le_bytes());
190        hash_arr[68..76].copy_from_slice(&gates.align.to_le_bytes());
191        hash_arr
192    }
193}
194
195#[cfg(test)]
196#[cfg(feature = "user")]
197#[allow(unused_imports)]
198mod tests {
199
200    use crate::*;
201
202    extern crate test;
203
204    use twizzler::object::TypedObject;
205    use twizzler_abi::{object::Protections, syscall::ObjectCreate};
206    /// Create a default capability
207    fn default_capability(s_key: &SigningKey) -> Cap {
208        Cap::new(
209            0x123.into(),
210            0x321.into(),
211            Protections::all(),
212            s_key,
213            Revoc::default(),
214            Gate::default(),
215            HashingAlgo::Sha256,
216        )
217        .expect("Capability should have been created.")
218    }
219
220    #[test]
221    fn test_capability_creation() {
222        let (s, _v) = SigningKey::new_keypair(&SigningScheme::Ecdsa, ObjectCreate::default())
223            .expect("keypair creation should not have errored!");
224        let _cap = default_capability(s.base());
225    }
226
227    #[test]
228    fn test_capability_verification() {
229        let (s, v) = SigningKey::new_keypair(&SigningScheme::Ecdsa, ObjectCreate::default())
230            .expect("keypair creation should not have errored!");
231
232        let cap = default_capability(s.base());
233
234        cap.verify_sig(v.base())
235            .expect("capability should have been verified.")
236    }
237
238    #[test]
239    fn test_capability_gates() {
240        struct Input {
241            /// gates that the capability will hold
242            capability_gates: Gate,
243            /// values you test
244            ptr_offset: u64,
245            align: u64,
246        }
247
248        // yeah i dont need an enum for this but honestly just makes it clear when im writing
249        // the table / makes it clear when reading the table.
250        #[derive(PartialEq, PartialOrd, Ord, Eq, Debug)]
251        enum Expected {
252            Fail,
253            Pass,
254        }
255
256        use Expected::*;
257
258        let table: [(Input, Expected); 7] = [
259            (
260                Input {
261                    capability_gates: Gate::new(0, 100, 1),
262                    ptr_offset: 3,
263                    align: 1,
264                },
265                Pass,
266            ),
267            (
268                Input {
269                    capability_gates: Gate::new(0, 100, 1),
270                    ptr_offset: 100,
271                    align: 1,
272                },
273                Pass,
274            ),
275            (
276                Input {
277                    capability_gates: Gate::new(0, 10_000, 1),
278                    ptr_offset: 5_000,
279                    align: 1,
280                },
281                Pass,
282            ),
283            (
284                Input {
285                    capability_gates: Gate::new(0, 100, 1),
286                    ptr_offset: 50,
287                    align: 1,
288                },
289                Pass,
290            ),
291            (
292                Input {
293                    capability_gates: Gate::new(5, 10000, 1),
294                    ptr_offset: 0, // ptr_offset too small
295                    align: 1,
296                },
297                Fail,
298            ),
299            (
300                Input {
301                    capability_gates: Gate::new(0, 100, 1),
302                    ptr_offset: 105, // ptr_offset too large
303                    align: 1,
304                },
305                Fail,
306            ),
307            (
308                Input {
309                    capability_gates: Gate::new(0, 100, 1),
310                    ptr_offset: 66,
311                    align: 4, // bad alignment
312                },
313                Fail,
314            ),
315        ];
316
317        let (s, _v) = SigningKey::new_keypair(&SigningScheme::Ecdsa, ObjectCreate::default())
318            .expect("keypair creation should not have errored!");
319
320        for (test_number, (input, expected)) in table.into_iter().enumerate() {
321            let cap = Cap::new(
322                0x123.into(),
323                0x321.into(),
324                Protections::all(),
325                s.base(),
326                Revoc::default(),
327                input.capability_gates,
328                HashingAlgo::Sha256,
329            )
330            .expect("Capability should have been created properly.");
331
332            let actual = match cap.check_gate(input.ptr_offset, input.align).is_ok() {
333                true => Pass,
334                false => Fail,
335            };
336
337            assert_eq!(
338                actual,
339                expected,
340                "
341                 \n Test {:?}
342                 expected: {:?}
343                 actual: {:?},
344                 Failed for capability gates = {:#?}, where
345                 testing against: ptr_offset = {}, align = {})",
346                test_number,
347                expected,
348                actual,
349                input.capability_gates,
350                input.ptr_offset,
351                input.align
352            )
353        }
354    }
355}