capsules_system/process_checker/
basic.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Sample implementations of application credentials checkers, used
6//! to decide whether an application can be loaded. See
7//| the [AppID TRD](../../doc/reference/trd-appid.md).
8
9use kernel::deferred_call::{DeferredCall, DeferredCallClient};
10use kernel::hil::digest::{ClientData, ClientHash, ClientVerify};
11use kernel::hil::digest::{DigestDataVerify, Sha256};
12use kernel::process::{Process, ProcessBinary, ShortId};
13use kernel::process_checker::CheckResult;
14use kernel::process_checker::{AppCredentialsPolicy, AppCredentialsPolicyClient};
15use kernel::process_checker::{AppUniqueness, Compress};
16use kernel::utilities::cells::OptionalCell;
17use kernel::utilities::cells::TakeCell;
18use kernel::utilities::leasable_buffer::{SubSlice, SubSliceMut};
19use kernel::ErrorCode;
20use tock_tbf::types::TbfFooterV2Credentials;
21use tock_tbf::types::TbfFooterV2CredentialsType;
22
23/// A sample Credentials Checking Policy that approves all apps.
24pub struct AppCheckerNull {}
25
26impl AppCheckerNull {
27    pub fn new() -> Self {
28        Self {}
29    }
30}
31
32impl<'a> AppCredentialsPolicy<'a> for AppCheckerNull {
33    fn require_credentials(&self) -> bool {
34        false
35    }
36
37    fn check_credentials(
38        &self,
39        credentials: TbfFooterV2Credentials,
40        binary: &'a [u8],
41    ) -> Result<(), (ErrorCode, TbfFooterV2Credentials, &'a [u8])> {
42        Err((ErrorCode::NOSUPPORT, credentials, binary))
43    }
44
45    fn set_client(&self, _client: &'a dyn AppCredentialsPolicyClient<'a>) {}
46}
47
48pub struct AppIdAssignerSimulated {}
49
50impl AppUniqueness for AppIdAssignerSimulated {
51    // This checker doesn't allow you to run two processes with the
52    // same name.
53    fn different_identifier(&self, process_a: &ProcessBinary, process_b: &ProcessBinary) -> bool {
54        let a = process_a.header.get_package_name().unwrap_or("");
55        let b = process_b.header.get_package_name().unwrap_or("");
56        !a.eq(b)
57    }
58
59    fn different_identifier_process(
60        &self,
61        process_binary: &ProcessBinary,
62        process: &dyn Process,
63    ) -> bool {
64        let a = process_binary.header.get_package_name().unwrap_or("");
65        let b = process.get_process_name();
66        !a.eq(b)
67    }
68
69    fn different_identifier_processes(
70        &self,
71        process_a: &dyn Process,
72        process_b: &dyn Process,
73    ) -> bool {
74        let a = process_a.get_process_name();
75        let b = process_b.get_process_name();
76        !a.eq(b)
77    }
78}
79
80impl Compress for AppIdAssignerSimulated {
81    fn to_short_id(&self, _process: &ProcessBinary) -> ShortId {
82        ShortId::LocallyUnique
83    }
84}
85
86pub trait Sha256Verifier<'a>: DigestDataVerify<'a, 32_usize> + Sha256 {}
87impl<'a, T: DigestDataVerify<'a, 32_usize> + Sha256> Sha256Verifier<'a> for T {}
88
89/// A Credentials Checking Policy that only runs Userspace Binaries
90/// which have a unique SHA256 credential.
91///
92/// A Userspace Binary without a SHA256 credential fails checking, and
93/// only one Userspace Binary with a particular SHA256 hash runs at
94/// any time.
95pub struct AppCheckerSha256 {
96    hasher: &'static dyn Sha256Verifier<'static>,
97    client: OptionalCell<&'static dyn AppCredentialsPolicyClient<'static>>,
98    hash: TakeCell<'static, [u8; 32]>,
99    binary: OptionalCell<&'static [u8]>,
100    credentials: OptionalCell<TbfFooterV2Credentials>,
101}
102
103impl AppCheckerSha256 {
104    pub fn new(
105        hash: &'static dyn Sha256Verifier<'static>,
106        buffer: &'static mut [u8; 32],
107    ) -> AppCheckerSha256 {
108        AppCheckerSha256 {
109            hasher: hash,
110            client: OptionalCell::empty(),
111            hash: TakeCell::new(buffer),
112            credentials: OptionalCell::empty(),
113            binary: OptionalCell::empty(),
114        }
115    }
116}
117
118impl AppCredentialsPolicy<'static> for AppCheckerSha256 {
119    fn require_credentials(&self) -> bool {
120        true
121    }
122
123    fn check_credentials(
124        &self,
125        credentials: TbfFooterV2Credentials,
126        binary: &'static [u8],
127    ) -> Result<(), (ErrorCode, TbfFooterV2Credentials, &'static [u8])> {
128        self.credentials.set(credentials);
129        match credentials.format() {
130            TbfFooterV2CredentialsType::SHA256 => {
131                self.hash.map(|h| {
132                    h[..32].copy_from_slice(&credentials.data()[..32]);
133                });
134                self.hasher.clear_data();
135                match self.hasher.add_data(SubSlice::new(binary)) {
136                    Ok(()) => Ok(()),
137                    Err((e, b)) => Err((e, credentials, b.take())),
138                }
139            }
140            _ => Err((ErrorCode::NOSUPPORT, credentials, binary)),
141        }
142    }
143
144    fn set_client(&self, client: &'static dyn AppCredentialsPolicyClient<'static>) {
145        self.client.replace(client);
146    }
147}
148
149impl ClientData<32_usize> for AppCheckerSha256 {
150    fn add_mut_data_done(&self, _result: Result<(), ErrorCode>, _data: SubSliceMut<'static, u8>) {}
151
152    fn add_data_done(&self, result: Result<(), ErrorCode>, data: SubSlice<'static, u8>) {
153        match result {
154            Err(e) => panic!("Internal error during application binary checking. SHA256 engine threw error in adding data: {:?}", e),
155            Ok(()) => {
156                self.binary.set(data.take());
157                let hash: &'static mut [u8; 32_usize] = self.hash.take().unwrap();
158                if let Err((e, _)) = self.hasher.verify(hash) { panic!("Failed invoke hash verification in process credential checking: {:?}", e) }
159            }
160        }
161    }
162}
163
164impl ClientVerify<32_usize> for AppCheckerSha256 {
165    fn verification_done(
166        &self,
167        result: Result<bool, ErrorCode>,
168        compare: &'static mut [u8; 32_usize],
169    ) {
170        self.hash.replace(compare);
171        match result {
172            Ok(true) => {
173                self.client.map(|c| {
174                    c.check_done(
175                        Ok(CheckResult::Accept(None)),
176                        self.credentials.take().unwrap(),
177                        self.binary.take().unwrap(),
178                    );
179                });
180            }
181            Ok(false) => {
182                self.client.map(|c| {
183                    c.check_done(
184                        Ok(CheckResult::Reject),
185                        self.credentials.take().unwrap(),
186                        self.binary.take().unwrap(),
187                    );
188                });
189            }
190            Err(e) => {
191                panic!("Error {:?} in processing application credentials.", e);
192            }
193        }
194    }
195}
196
197impl ClientHash<32_usize> for AppCheckerSha256 {
198    fn hash_done(&self, _result: Result<(), ErrorCode>, _digest: &'static mut [u8; 32_usize]) {}
199}
200
201/// A sample AppID Assignment tool that assigns pseudo-unique AppIDs and
202/// ShortIds based on the process name.
203///
204/// ShortIds are assigned as a non-secure hash of the process name.
205///
206/// ### Usage
207///
208/// ```rust,ignore
209/// let assigner = static_init!(
210///     kernel::process_checker::basic::AppIdAssignerNames<fn(&'static str) -> u32>,
211///     kernel::process_checker::basic::AppIdAssignerNames::new(
212///         &((|s| { kernel::utilities::helpers::crc32_posix(s.as_bytes()) })
213///         as fn(&'static str) -> u32)
214///     )
215/// );
216/// ```
217pub struct AppIdAssignerNames<'a, F: Fn(&'static str) -> u32> {
218    hasher: &'a F,
219}
220
221impl<'a, F: Fn(&'static str) -> u32> AppIdAssignerNames<'a, F> {
222    pub fn new(hasher: &'a F) -> Self {
223        Self { hasher }
224    }
225}
226
227impl<F: Fn(&'static str) -> u32> AppUniqueness for AppIdAssignerNames<'_, F> {
228    fn different_identifier(&self, process_a: &ProcessBinary, process_b: &ProcessBinary) -> bool {
229        self.to_short_id(process_a) != self.to_short_id(process_b)
230    }
231
232    fn different_identifier_process(
233        &self,
234        process_a: &ProcessBinary,
235        process_b: &dyn Process,
236    ) -> bool {
237        self.to_short_id(process_a) != process_b.short_app_id()
238    }
239
240    fn different_identifier_processes(
241        &self,
242        process_a: &dyn Process,
243        process_b: &dyn Process,
244    ) -> bool {
245        process_a.short_app_id() != process_b.short_app_id()
246    }
247}
248
249impl<F: Fn(&'static str) -> u32> Compress for AppIdAssignerNames<'_, F> {
250    fn to_short_id(&self, process: &ProcessBinary) -> ShortId {
251        let name = process.header.get_package_name().unwrap_or("");
252        let sum = (self.hasher)(name);
253        core::num::NonZeroU32::new(sum).into()
254    }
255}
256
257/// A sample Credentials Checking Policy that loads and runs Userspace
258/// Binaries that have RSA3072 or RSA4096 credentials.
259///
260/// It uses the public key stored in the credentials as the
261/// Application Identifier, and the bottom 31 bits of the public key
262/// as the ShortId. WARNING: this policy does not actually check the
263/// RSA signature: it always blindly assumes it is correct. This
264/// checker exists to test that the Tock boot sequence correctly
265/// handles ID collisions and version numbers.
266pub struct AppCheckerRsaSimulated<'a> {
267    deferred_call: DeferredCall,
268    client: OptionalCell<&'a dyn AppCredentialsPolicyClient<'a>>,
269    credentials: OptionalCell<TbfFooterV2Credentials>,
270    binary: OptionalCell<&'a [u8]>,
271}
272
273impl<'a> AppCheckerRsaSimulated<'a> {
274    pub fn new() -> AppCheckerRsaSimulated<'a> {
275        Self {
276            deferred_call: DeferredCall::new(),
277            client: OptionalCell::empty(),
278            credentials: OptionalCell::empty(),
279            binary: OptionalCell::empty(),
280        }
281    }
282}
283
284impl DeferredCallClient for AppCheckerRsaSimulated<'_> {
285    fn handle_deferred_call(&self) {
286        // This checker does not actually verify the RSA signature; it
287        // assumes the signature is valid and so accepts any RSA
288        // signature. This checker is intended for testing kernel
289        // process loading logic, and not for real uses requiring
290        // integrity or authenticity.
291        self.client.map(|c| {
292            let binary = self.binary.take().unwrap();
293            let cred = self.credentials.take().unwrap();
294            let result = if cred.format() == TbfFooterV2CredentialsType::Rsa3072Key
295                || cred.format() == TbfFooterV2CredentialsType::Rsa4096Key
296            {
297                Ok(CheckResult::Accept(None))
298            } else {
299                Ok(CheckResult::Pass)
300            };
301
302            c.check_done(result, cred, binary)
303        });
304    }
305
306    fn register(&'static self) {
307        self.deferred_call.register(self);
308    }
309}
310
311impl<'a> AppCredentialsPolicy<'a> for AppCheckerRsaSimulated<'a> {
312    fn require_credentials(&self) -> bool {
313        true
314    }
315
316    fn check_credentials(
317        &self,
318        credentials: TbfFooterV2Credentials,
319        binary: &'a [u8],
320    ) -> Result<(), (ErrorCode, TbfFooterV2Credentials, &'a [u8])> {
321        if self.credentials.is_none() {
322            self.credentials.replace(credentials);
323            self.binary.replace(binary);
324            self.deferred_call.set();
325            Ok(())
326        } else {
327            Err((ErrorCode::BUSY, credentials, binary))
328        }
329    }
330
331    fn set_client(&self, client: &'a dyn AppCredentialsPolicyClient<'a>) {
332        self.client.replace(client);
333    }
334}