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 `SIGNATURE_LEN`).
26pub struct AppCheckerSignature<
27    'a,
28    S: hil::public_key_crypto::signature::SignatureVerify<'static, HASH_LEN, SIGNATURE_LEN>
29        + hil::public_key_crypto::keys::SelectKey<'a>,
30    H: hil::digest::DigestDataHash<'a, HASH_LEN>,
31    const HASH_LEN: usize,
32    const SIGNATURE_LEN: usize,
33> {
34    hasher: &'a H,
35    verifier: &'a S,
36    hash: MapCell<&'static mut [u8; HASH_LEN]>,
37    signature: MapCell<&'static mut [u8; SIGNATURE_LEN]>,
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, HASH_LEN, SIGNATURE_LEN>
48            + hil::public_key_crypto::keys::SelectKey<'a>,
49        H: hil::digest::DigestDataHash<'a, HASH_LEN>,
50        const HASH_LEN: usize,
51        const SIGNATURE_LEN: usize,
52    > AppCheckerSignature<'a, S, H, HASH_LEN, SIGNATURE_LEN>
53{
54    pub fn new(
55        hasher: &'a H,
56        verifier: &'a S,
57        hash_buffer: &'static mut [u8; HASH_LEN],
58        signature_buffer: &'static mut [u8; SIGNATURE_LEN],
59        credential_type: TbfFooterV2CredentialsType,
60    ) -> AppCheckerSignature<'a, S, H, HASH_LEN, SIGNATURE_LEN> {
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, HASH_LEN, SIGNATURE_LEN>
109            + hil::public_key_crypto::keys::SelectKey<'a>,
110        H: hil::digest::DigestDataHash<'a, HASH_LEN>,
111        const HASH_LEN: usize,
112        const SIGNATURE_LEN: usize,
113    > hil::digest::ClientData<HASH_LEN> for AppCheckerSignature<'a, S, H, HASH_LEN, SIGNATURE_LEN>
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, HASH_LEN, SIGNATURE_LEN>
147            + hil::public_key_crypto::keys::SelectKey<'a>,
148        H: hil::digest::DigestDataHash<'a, HASH_LEN>,
149        const HASH_LEN: usize,
150        const SIGNATURE_LEN: usize,
151    > hil::public_key_crypto::keys::SelectKeyClient
152    for AppCheckerSignature<'a, S, H, HASH_LEN, SIGNATURE_LEN>
153{
154    fn get_key_count_done(&self, count: usize) {
155        // We have the hash, we know how many keys, now we need to select the
156        // first key to check. Activate the first key.
157        self.active_key_index.set((0, count));
158        if let Err(_e) = self.verifier.select_key(0) {
159            self.client.map(|c| {
160                let binary = self.binary.take().unwrap();
161                let cred = self.credentials.take().unwrap();
162                c.check_done(Err(ErrorCode::FAIL), cred, binary)
163            });
164        }
165    }
166
167    fn select_key_done(&self, _index: usize, error: Result<(), ErrorCode>) {
168        match error {
169            Err(e) => {
170                // Could not switch to the requested key.
171                self.client.map(|c| {
172                    let binary = self.binary.take().unwrap();
173                    let cred = self.credentials.take().unwrap();
174                    c.check_done(Err(e), cred, binary)
175                });
176            }
177            Ok(()) => self.do_verify(),
178        }
179    }
180}
181
182impl<
183        'a,
184        S: hil::public_key_crypto::signature::SignatureVerify<'static, HASH_LEN, SIGNATURE_LEN>
185            + hil::public_key_crypto::keys::SelectKey<'a>,
186        H: hil::digest::DigestDataHash<'a, HASH_LEN>,
187        const HASH_LEN: usize,
188        const SIGNATURE_LEN: usize,
189    > hil::digest::ClientHash<HASH_LEN> for AppCheckerSignature<'a, S, H, HASH_LEN, SIGNATURE_LEN>
190{
191    fn hash_done(&self, result: Result<(), ErrorCode>, digest: &'static mut [u8; HASH_LEN]) {
192        // Save the hash buffer in all cases. If there was an error then we just
193        // need to save the buffer, on success we need to keep the correct
194        // hash digest.
195        self.hash.replace(digest);
196
197        match result {
198            Err(e) => {
199                // Could not compute the hash.
200                self.client.map(|c| {
201                    let binary = self.binary.take().unwrap();
202                    let cred = self.credentials.take().unwrap();
203                    c.check_done(Err(e), cred, binary)
204                });
205            }
206            Ok(()) => {
207                // We got a hash. Next we need to figure out how many keys we
208                // have.
209                if self.verifier.get_key_count().is_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            }
217        }
218    }
219}
220
221impl<
222        'a,
223        S: hil::public_key_crypto::signature::SignatureVerify<'static, HASH_LEN, SIGNATURE_LEN>
224            + hil::public_key_crypto::keys::SelectKey<'a>,
225        H: hil::digest::DigestDataHash<'a, HASH_LEN>,
226        const HASH_LEN: usize,
227        const SIGNATURE_LEN: usize,
228    > hil::digest::ClientVerify<HASH_LEN>
229    for AppCheckerSignature<'a, S, H, HASH_LEN, SIGNATURE_LEN>
230{
231    fn verification_done(
232        &self,
233        _result: Result<bool, ErrorCode>,
234        _compare: &'static mut [u8; HASH_LEN],
235    ) {
236        // Unused for this checker.
237        // Needed to make the sha256 client work.
238    }
239}
240
241impl<
242        'a,
243        S: hil::public_key_crypto::signature::SignatureVerify<'static, HASH_LEN, SIGNATURE_LEN>
244            + hil::public_key_crypto::keys::SelectKey<'a>,
245        H: hil::digest::DigestDataHash<'a, HASH_LEN>,
246        const HASH_LEN: usize,
247        const SIGNATURE_LEN: usize,
248    > hil::public_key_crypto::signature::ClientVerify<HASH_LEN, SIGNATURE_LEN>
249    for AppCheckerSignature<'a, S, H, HASH_LEN, SIGNATURE_LEN>
250{
251    fn verification_done(
252        &self,
253        result: Result<bool, ErrorCode>,
254        hash: &'static mut [u8; HASH_LEN],
255        signature: &'static mut [u8; SIGNATURE_LEN],
256    ) {
257        self.hash.replace(hash);
258        self.signature.replace(signature);
259
260        let (current_key, number_keys) = self.active_key_index.get();
261
262        // Check if the verification was successful. If so, we can issue the
263        // callback.
264        if result.unwrap_or(false) {
265            self.client.map(|c| {
266                let binary = self.binary.take().unwrap();
267                let cred = self.credentials.take().unwrap();
268                c.check_done(
269                    Ok(CheckResult::Accept(Some(
270                        kernel::process_checker::CheckResultAcceptMetadata {
271                            metadata: current_key,
272                        },
273                    ))),
274                    cred,
275                    binary,
276                )
277            });
278        } else {
279            // The current key did not verify the signature. Check if there are
280            // more keys we can try or return `Pass`.
281
282            let next_key = current_key + 1;
283            if next_key == number_keys {
284                // No more keys to try so we can report that we couldn't verify
285                // the key.
286                self.client.map(|c| {
287                    let binary = self.binary.take().unwrap();
288                    let cred = self.credentials.take().unwrap();
289                    c.check_done(Ok(CheckResult::Pass), cred, binary)
290                });
291            } else {
292                // Activate the next key.
293                self.active_key_index.set((next_key, number_keys));
294                if self.verifier.select_key(next_key).is_err() {
295                    self.client.map(|c| {
296                        let binary = self.binary.take().unwrap();
297                        let cred = self.credentials.take().unwrap();
298                        c.check_done(Err(ErrorCode::FAIL), cred, binary)
299                    });
300                }
301            }
302        }
303    }
304}
305
306impl<
307        'a,
308        S: hil::public_key_crypto::signature::SignatureVerify<'static, HASH_LEN, SIGNATURE_LEN>
309            + hil::public_key_crypto::keys::SelectKey<'a>,
310        H: hil::digest::DigestDataHash<'a, HASH_LEN>,
311        const HASH_LEN: usize,
312        const SIGNATURE_LEN: usize,
313    > AppCredentialsPolicy<'static> for AppCheckerSignature<'a, S, H, HASH_LEN, SIGNATURE_LEN>
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()[..SIGNATURE_LEN]
330                    .copy_from_slice(&credentials.data()[..SIGNATURE_LEN]);
331            });
332
333            // Add the process binary to compute the hash.
334            self.hasher.clear_data();
335            match self.hasher.add_data(SubSlice::new(binary)) {
336                Ok(()) => Ok(()),
337                Err((e, b)) => Err((e, credentials, b.take())),
338            }
339        } else {
340            Err((ErrorCode::NOSUPPORT, credentials, binary))
341        }
342    }
343
344    fn set_client(&self, client: &'static dyn AppCredentialsPolicyClient<'static>) {
345        self.client.replace(client);
346    }
347}