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    Gates, 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, Copy, 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    gates: Gates,
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: Gates,
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            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.gates,
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.gates.offset {
157            return Err(SecurityError::GateDenied);
158        }
159
160        // the access is beyond the "end" of the gate
161        if self.gates.offset + self.gates.length < ptr_offset {
162            return Err(SecurityError::GateDenied);
163        }
164
165        //NOTE: not completely sure this is how you check alignment.
166        if self.gates.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: Gates,
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(feature = "user")]
196#[allow(unused_imports)]
197mod tests {
198
199    use crate::*;
200
201    extern crate test;
202
203    use twizzler::object::TypedObject;
204    use twizzler_abi::{object::Protections, syscall::ObjectCreate};
205    fn default_capability(s_key: &SigningKey) -> Cap {
206        Cap::new(
207            0x123.into(),
208            0x321.into(),
209            Protections::all(),
210            s_key,
211            Revoc::default(),
212            Gates::default(),
213            HashingAlgo::Sha256,
214        )
215        .expect("Capability should have been created.")
216    }
217
218    #[test]
219    fn test_capability_creation() {
220        let (s, _v) = SigningKey::new_keypair(&SigningScheme::Ecdsa, ObjectCreate::default())
221            .expect("keypair creation should not have errored!");
222        let _cap = default_capability(s.base());
223    }
224
225    #[test]
226    fn test_capability_verification() {
227        let (s, v) = SigningKey::new_keypair(&SigningScheme::Ecdsa, ObjectCreate::default())
228            .expect("keypair creation should not have errored!");
229
230        let cap = default_capability(s.base());
231
232        cap.verify_sig(v.base())
233            .expect("capability should have been verified.")
234    }
235
236    #[test]
237    fn test_capability_gates() {
238        struct Input {
239            /// gates that the capability will hold
240            capability_gates: Gates,
241            /// values you test
242            ptr_offset: u64,
243            align: u64,
244        }
245
246        // yeah i dont need an enum for this but honestly just makes it clear when im writing
247        // the table / makes it clear when reading the table.
248        #[derive(PartialEq, PartialOrd, Ord, Eq, Debug)]
249        enum Expected {
250            Fail,
251            Pass,
252        }
253
254        use Expected::*;
255
256        let table: [(Input, Expected); 7] = [
257            (
258                Input {
259                    capability_gates: Gates::new(0, 100, 1),
260                    ptr_offset: 3,
261                    align: 1,
262                },
263                Pass,
264            ),
265            (
266                Input {
267                    capability_gates: Gates::new(0, 100, 1),
268                    ptr_offset: 100,
269                    align: 1,
270                },
271                Pass,
272            ),
273            (
274                Input {
275                    capability_gates: Gates::new(0, 10_000, 1),
276                    ptr_offset: 5_000,
277                    align: 1,
278                },
279                Pass,
280            ),
281            (
282                Input {
283                    capability_gates: Gates::new(0, 100, 1),
284                    ptr_offset: 50,
285                    align: 1,
286                },
287                Pass,
288            ),
289            (
290                Input {
291                    capability_gates: Gates::new(5, 10000, 1),
292                    ptr_offset: 0, // ptr_offset too small
293                    align: 1,
294                },
295                Fail,
296            ),
297            (
298                Input {
299                    capability_gates: Gates::new(0, 100, 1),
300                    ptr_offset: 105, // ptr_offset too large
301                    align: 1,
302                },
303                Fail,
304            ),
305            (
306                Input {
307                    capability_gates: Gates::new(0, 100, 1),
308                    ptr_offset: 66,
309                    align: 4, // bad alignment
310                },
311                Fail,
312            ),
313        ];
314
315        let (s, _v) = SigningKey::new_keypair(&SigningScheme::Ecdsa, ObjectCreate::default())
316            .expect("keypair creation should not have errored!");
317
318        for (test_number, (input, expected)) in table.into_iter().enumerate() {
319            let cap = Cap::new(
320                0x123.into(),
321                0x321.into(),
322                Protections::all(),
323                s.base(),
324                Revoc::default(),
325                input.capability_gates,
326                HashingAlgo::Sha256,
327            )
328            .expect("Capability should have been created properly.");
329
330            let actual = match cap.check_gate(input.ptr_offset, input.align).is_ok() {
331                true => Pass,
332                false => Fail,
333            };
334
335            assert_eq!(
336                actual,
337                expected,
338                "
339                 \n Test {:?}
340                 expected: {:?}
341                 actual: {:?},
342                 Failed for capability gates = {:#?}, where
343                 testing against: ptr_offset = {}, align = {})",
344                test_number,
345                expected,
346                actual,
347                input.capability_gates,
348                input.ptr_offset,
349                input.align
350            )
351        }
352    }
353}