capsules_system/process_checker/
signature.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 2024.
4
5//! Signature credential checker for checking process credentials.
6
7use core::cell::Cell;
8use kernel::hil;
9use kernel::process_checker::CheckResult;
10use kernel::process_checker::{AppCredentialsPolicy, AppCredentialsPolicyClient};
11use kernel::utilities::cells::MapCell;
12use kernel::utilities::cells::OptionalCell;
13use kernel::utilities::leasable_buffer::{SubSlice, SubSliceMut};
14use kernel::ErrorCode;
15use tock_tbf::types::TbfFooterV2Credentials;
16use tock_tbf::types::TbfFooterV2CredentialsType;
17
18/// Checker that validates a correct signature credential.
19///
20/// This checker provides the scaffolding on top of a hasher (`&H`) and a
21/// verifier (`&S`) for a given `TbfFooterV2CredentialsType`.
22///
23/// This assumes the `TbfFooterV2CredentialsType` data format only contains the
24/// signature (i.e. the data length of the credential in the TBF footer is the
25/// same as `SL`).
26pub struct AppCheckerSignature<
27    'a,
28    S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>
29        + hil::public_key_crypto::keys::SelectKey<'a>,
30    H: hil::digest::DigestDataHash<'a, HL>,
31    const HL: usize,
32    const SL: usize,
33> {
34    hasher: &'a H,
35    verifier: &'a S,
36    hash: MapCell<&'static mut [u8; HL]>,
37    signature: MapCell<&'static mut [u8; SL]>,
38    client: OptionalCell<&'static dyn AppCredentialsPolicyClient<'static>>,
39    credential_type: TbfFooterV2CredentialsType,
40    credentials: OptionalCell<TbfFooterV2Credentials>,
41    binary: OptionalCell<&'static [u8]>,
42    active_key_index: Cell<(usize, usize)>,
43}
44
45impl<
46        'a,
47        S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>
48            + hil::public_key_crypto::keys::SelectKey<'a>,
49        H: hil::digest::DigestDataHash<'a, HL>,
50        const HL: usize,
51        const SL: usize,
52    > AppCheckerSignature<'a, S, H, HL, SL>
53{
54    pub fn new(
55        hasher: &'a H,
56        verifier: &'a S,
57        hash_buffer: &'static mut [u8; HL],
58        signature_buffer: &'static mut [u8; SL],
59        credential_type: TbfFooterV2CredentialsType,
60    ) -> AppCheckerSignature<'a, S, H, HL, SL> {
61        Self {
62            hasher,
63            verifier,
64            hash: MapCell::new(hash_buffer),
65            signature: MapCell::new(signature_buffer),
66            client: OptionalCell::empty(),
67            credential_type,
68            credentials: OptionalCell::empty(),
69            binary: OptionalCell::empty(),
70            active_key_index: Cell::new((0, 0)),
71        }
72    }
73
74    fn do_verify(&self) {
75        match (self.signature.take(), self.hash.take()) {
76            // Switched to the desired key, now use that key to do the
77            // verification.
78            (Some(sig), Some(digest)) => {
79                if let Err((e, d, s)) = self.verifier.verify(digest, sig) {
80                    self.hash.replace(d);
81                    self.signature.replace(s);
82                    self.client.map(|c| {
83                        let binary = self.binary.take().unwrap();
84                        let cred = self.credentials.take().unwrap();
85                        c.check_done(Err(e), cred, binary)
86                    });
87                }
88            }
89            (sig_option, digest_option) => {
90                if let Some(sig) = sig_option {
91                    self.signature.replace(sig);
92                }
93                if let Some(digest) = digest_option {
94                    self.hash.replace(digest);
95                }
96                self.client.map(|c| {
97                    let binary = self.binary.take().unwrap();
98                    let cred = self.credentials.take().unwrap();
99                    c.check_done(Err(ErrorCode::FAIL), cred, binary)
100                });
101            }
102        }
103    }
104}
105
106impl<
107        'a,
108        S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>
109            + hil::public_key_crypto::keys::SelectKey<'a>,
110        H: hil::digest::DigestDataHash<'a, HL>,
111        const HL: usize,
112        const SL: usize,
113    > hil::digest::ClientData<HL> for AppCheckerSignature<'a, S, H, HL, SL>
114{
115    fn add_mut_data_done(&self, _result: Result<(), ErrorCode>, _data: SubSliceMut<'static, u8>) {}
116
117    fn add_data_done(&self, result: Result<(), ErrorCode>, data: SubSlice<'static, u8>) {
118        self.binary.set(data.take());
119
120        // We added the binary data to the hasher, now we can compute the hash.
121        match result {
122            Err(e) => {
123                self.client.map(|c| {
124                    let binary = self.binary.take().unwrap();
125                    let cred = self.credentials.take().unwrap();
126                    c.check_done(Err(e), cred, binary)
127                });
128            }
129            Ok(()) => {
130                self.hash.take().map(|h| {
131                    if let Err((e, _)) = self.hasher.run(h) {
132                        self.client.map(|c| {
133                            let binary = self.binary.take().unwrap();
134                            let cred = self.credentials.take().unwrap();
135                            c.check_done(Err(e), cred, binary)
136                        });
137                    }
138                });
139            }
140        }
141    }
142}
143
144impl<
145        'a,
146        S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>
147            + hil::public_key_crypto::keys::SelectKey<'a>,
148        H: hil::digest::DigestDataHash<'a, HL>,
149        const HL: usize,
150        const SL: usize,
151    > hil::public_key_crypto::keys::SelectKeyClient for AppCheckerSignature<'a, S, H, HL, SL>
152{
153    fn get_key_count_done(&self, count: usize) {
154        // We have the hash, we know how many keys, now we need to select the
155        // first key to check. Activate the first key.
156        self.active_key_index.set((0, count));
157        if let Err(_e) = self.verifier.select_key(0) {
158            self.client.map(|c| {
159                let binary = self.binary.take().unwrap();
160                let cred = self.credentials.take().unwrap();
161                c.check_done(Err(ErrorCode::FAIL), cred, binary)
162            });
163        }
164    }
165
166    fn select_key_done(&self, _index: usize, error: Result<(), ErrorCode>) {
167        match error {
168            Err(e) => {
169                // Could not switch to the requested key.
170                self.client.map(|c| {
171                    let binary = self.binary.take().unwrap();
172                    let cred = self.credentials.take().unwrap();
173                    c.check_done(Err(e), cred, binary)
174                });
175            }
176            Ok(()) => self.do_verify(),
177        }
178    }
179}
180
181impl<
182        'a,
183        S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>
184            + hil::public_key_crypto::keys::SelectKey<'a>,
185        H: hil::digest::DigestDataHash<'a, HL>,
186        const HL: usize,
187        const SL: usize,
188    > hil::digest::ClientHash<HL> for AppCheckerSignature<'a, S, H, HL, SL>
189{
190    fn hash_done(&self, result: Result<(), ErrorCode>, digest: &'static mut [u8; HL]) {
191        // Save the hash buffer in all cases. If there was an error then we just
192        // need to save the buffer, on success we need to keep the correct
193        // hash digest.
194        self.hash.replace(digest);
195
196        match result {
197            Err(e) => {
198                // Could not compute the hash.
199                self.client.map(|c| {
200                    let binary = self.binary.take().unwrap();
201                    let cred = self.credentials.take().unwrap();
202                    c.check_done(Err(e), cred, binary)
203                });
204            }
205            Ok(()) => {
206                // We got a hash. Next we need to figure out how many keys we
207                // have.
208                match self.verifier.get_key_count() {
209                    Err(_) => {
210                        self.client.map(|c| {
211                            let binary = self.binary.take().unwrap();
212                            let cred = self.credentials.take().unwrap();
213                            c.check_done(Err(ErrorCode::FAIL), cred, binary)
214                        });
215                    }
216                    Ok(()) => {}
217                }
218            }
219        }
220    }
221}
222
223impl<
224        'a,
225        S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>
226            + hil::public_key_crypto::keys::SelectKey<'a>,
227        H: hil::digest::DigestDataHash<'a, HL>,
228        const HL: usize,
229        const SL: usize,
230    > hil::digest::ClientVerify<HL> for AppCheckerSignature<'a, S, H, HL, SL>
231{
232    fn verification_done(&self, _result: Result<bool, ErrorCode>, _compare: &'static mut [u8; HL]) {
233        // Unused for this checker.
234        // Needed to make the sha256 client work.
235    }
236}
237
238impl<
239        'a,
240        S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>
241            + hil::public_key_crypto::keys::SelectKey<'a>,
242        H: hil::digest::DigestDataHash<'a, HL>,
243        const HL: usize,
244        const SL: usize,
245    > hil::public_key_crypto::signature::ClientVerify<HL, SL>
246    for AppCheckerSignature<'a, S, H, HL, SL>
247{
248    fn verification_done(
249        &self,
250        result: Result<bool, ErrorCode>,
251        hash: &'static mut [u8; HL],
252        signature: &'static mut [u8; SL],
253    ) {
254        self.hash.replace(hash);
255        self.signature.replace(signature);
256
257        let (current_key, number_keys) = self.active_key_index.get();
258
259        // Check if the verification was successful. If so, we can issue the
260        // callback.
261        if result.unwrap_or(false) {
262            self.client.map(|c| {
263                let binary = self.binary.take().unwrap();
264                let cred = self.credentials.take().unwrap();
265                c.check_done(
266                    Ok(CheckResult::Accept(Some(
267                        kernel::process_checker::CheckResultAcceptMetadata {
268                            metadata: current_key,
269                        },
270                    ))),
271                    cred,
272                    binary,
273                )
274            });
275        } else {
276            // The current key did not verify the signature. Check if there are
277            // more keys we can try or return `Pass`.
278
279            let next_key = current_key + 1;
280            if next_key == number_keys {
281                // No more keys to try so we can report that we couldn't verify
282                // the key.
283                self.client.map(|c| {
284                    let binary = self.binary.take().unwrap();
285                    let cred = self.credentials.take().unwrap();
286                    c.check_done(Ok(CheckResult::Pass), cred, binary)
287                });
288            } else {
289                // Activate the next key.
290                self.active_key_index.set((next_key, number_keys));
291                match self.verifier.select_key(next_key) {
292                    Err(_) => {
293                        self.client.map(|c| {
294                            let binary = self.binary.take().unwrap();
295                            let cred = self.credentials.take().unwrap();
296                            c.check_done(Err(ErrorCode::FAIL), cred, binary)
297                        });
298                    }
299                    Ok(()) => {}
300                }
301            }
302        }
303    }
304}
305
306impl<
307        'a,
308        S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>
309            + hil::public_key_crypto::keys::SelectKey<'a>,
310        H: hil::digest::DigestDataHash<'a, HL>,
311        const HL: usize,
312        const SL: usize,
313    > AppCredentialsPolicy<'static> for AppCheckerSignature<'a, S, H, HL, SL>
314{
315    fn require_credentials(&self) -> bool {
316        true
317    }
318
319    fn check_credentials(
320        &self,
321        credentials: TbfFooterV2Credentials,
322        binary: &'static [u8],
323    ) -> Result<(), (ErrorCode, TbfFooterV2Credentials, &'static [u8])> {
324        self.credentials.set(credentials);
325
326        if credentials.format() == self.credential_type {
327            // Save the signature we are trying to compare with.
328            self.signature.map(|b| {
329                b.as_mut_slice()[..SL].copy_from_slice(&credentials.data()[..SL]);
330            });
331
332            // Add the process binary to compute the hash.
333            self.hasher.clear_data();
334            match self.hasher.add_data(SubSlice::new(binary)) {
335                Ok(()) => Ok(()),
336                Err((e, b)) => Err((e, credentials, b.take())),
337            }
338        } else {
339            Err((ErrorCode::NOSUPPORT, credentials, binary))
340        }
341    }
342
343    fn set_client(&self, client: &'static dyn AppCredentialsPolicyClient<'static>) {
344        self.client.replace(client);
345    }
346}