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 kernel::hil;
8use kernel::process_checker::CheckResult;
9use kernel::process_checker::{AppCredentialsPolicy, AppCredentialsPolicyClient};
10use kernel::utilities::cells::MapCell;
11use kernel::utilities::cells::OptionalCell;
12use kernel::utilities::leasable_buffer::{SubSlice, SubSliceMut};
13use kernel::ErrorCode;
14use tock_tbf::types::TbfFooterV2Credentials;
15use tock_tbf::types::TbfFooterV2CredentialsType;
16
17/// Checker that validates a correct signature credential.
18///
19/// This checker provides the scaffolding on top of a hasher (`&H`) and a
20/// verifier (`&S`) for a given `TbfFooterV2CredentialsType`.
21///
22/// This assumes the `TbfFooterV2CredentialsType` data format only contains the
23/// signature (i.e. the data length of the credential in the TBF footer is the
24/// same as `SL`).
25pub struct AppCheckerSignature<
26    'a,
27    S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>,
28    H: hil::digest::DigestDataHash<'a, HL>,
29    const HL: usize,
30    const SL: usize,
31> {
32    hasher: &'a H,
33    verifier: &'a S,
34    hash: MapCell<&'static mut [u8; HL]>,
35    signature: MapCell<&'static mut [u8; SL]>,
36    client: OptionalCell<&'static dyn AppCredentialsPolicyClient<'static>>,
37    credential_type: TbfFooterV2CredentialsType,
38    credentials: OptionalCell<TbfFooterV2Credentials>,
39    binary: OptionalCell<&'static [u8]>,
40}
41
42impl<
43        'a,
44        S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>,
45        H: hil::digest::DigestDataHash<'a, HL>,
46        const HL: usize,
47        const SL: usize,
48    > AppCheckerSignature<'a, S, H, HL, SL>
49{
50    pub fn new(
51        hasher: &'a H,
52        verifier: &'a S,
53        hash_buffer: &'static mut [u8; HL],
54        signature_buffer: &'static mut [u8; SL],
55        credential_type: TbfFooterV2CredentialsType,
56    ) -> AppCheckerSignature<'a, S, H, HL, SL> {
57        Self {
58            hasher,
59            verifier,
60            hash: MapCell::new(hash_buffer),
61            signature: MapCell::new(signature_buffer),
62            client: OptionalCell::empty(),
63            credential_type,
64            credentials: OptionalCell::empty(),
65            binary: OptionalCell::empty(),
66        }
67    }
68}
69
70impl<
71        'a,
72        S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>,
73        H: hil::digest::DigestDataHash<'a, HL>,
74        const HL: usize,
75        const SL: usize,
76    > hil::digest::ClientData<HL> for AppCheckerSignature<'a, S, H, HL, SL>
77{
78    fn add_mut_data_done(&self, _result: Result<(), ErrorCode>, _data: SubSliceMut<'static, u8>) {}
79
80    fn add_data_done(&self, result: Result<(), ErrorCode>, data: SubSlice<'static, u8>) {
81        self.binary.set(data.take());
82
83        // We added the binary data to the hasher, now we can compute the hash.
84        match result {
85            Err(e) => {
86                self.client.map(|c| {
87                    let binary = self.binary.take().unwrap();
88                    let cred = self.credentials.take().unwrap();
89                    c.check_done(Err(e), cred, binary)
90                });
91            }
92            Ok(()) => {
93                self.hash.take().map(|h| {
94                    if let Err((e, _)) = self.hasher.run(h) {
95                        self.client.map(|c| {
96                            let binary = self.binary.take().unwrap();
97                            let cred = self.credentials.take().unwrap();
98                            c.check_done(Err(e), cred, binary)
99                        });
100                    }
101                });
102            }
103        }
104    }
105}
106
107impl<
108        'a,
109        S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>,
110        H: hil::digest::DigestDataHash<'a, HL>,
111        const HL: usize,
112        const SL: usize,
113    > hil::digest::ClientHash<HL> for AppCheckerSignature<'a, S, H, HL, SL>
114{
115    fn hash_done(&self, result: Result<(), ErrorCode>, digest: &'static mut [u8; HL]) {
116        match result {
117            Err(e) => {
118                self.hash.replace(digest);
119                self.client.map(|c| {
120                    let binary = self.binary.take().unwrap();
121                    let cred = self.credentials.take().unwrap();
122                    c.check_done(Err(e), cred, binary)
123                });
124            }
125            Ok(()) => match self.signature.take() {
126                Some(sig) => {
127                    if let Err((e, d, s)) = self.verifier.verify(digest, sig) {
128                        self.hash.replace(d);
129                        self.signature.replace(s);
130                        self.client.map(|c| {
131                            let binary = self.binary.take().unwrap();
132                            let cred = self.credentials.take().unwrap();
133                            c.check_done(Err(e), cred, binary)
134                        });
135                    }
136                }
137                None => {
138                    self.hash.replace(digest);
139                    self.client.map(|c| {
140                        let binary = self.binary.take().unwrap();
141                        let cred = self.credentials.take().unwrap();
142                        c.check_done(Err(ErrorCode::FAIL), cred, binary)
143                    });
144                }
145            },
146        }
147    }
148}
149
150impl<
151        'a,
152        S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>,
153        H: hil::digest::DigestDataHash<'a, HL>,
154        const HL: usize,
155        const SL: usize,
156    > hil::digest::ClientVerify<HL> for AppCheckerSignature<'a, S, H, HL, SL>
157{
158    fn verification_done(&self, _result: Result<bool, ErrorCode>, _compare: &'static mut [u8; HL]) {
159        // Unused for this checker.
160        // Needed to make the sha256 client work.
161    }
162}
163
164impl<
165        'a,
166        S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>,
167        H: hil::digest::DigestDataHash<'a, HL>,
168        const HL: usize,
169        const SL: usize,
170    > hil::public_key_crypto::signature::ClientVerify<HL, SL>
171    for AppCheckerSignature<'a, S, H, HL, SL>
172{
173    fn verification_done(
174        &self,
175        result: Result<bool, ErrorCode>,
176        hash: &'static mut [u8; HL],
177        signature: &'static mut [u8; SL],
178    ) {
179        self.hash.replace(hash);
180        self.signature.replace(signature);
181
182        self.client.map(|c| {
183            let binary = self.binary.take().unwrap();
184            let cred = self.credentials.take().unwrap();
185            let check_result = if result.unwrap_or(false) {
186                Ok(CheckResult::Accept(None))
187            } else {
188                Ok(CheckResult::Pass)
189            };
190
191            c.check_done(check_result, cred, binary)
192        });
193    }
194}
195
196impl<
197        'a,
198        S: hil::public_key_crypto::signature::SignatureVerify<'static, HL, SL>,
199        H: hil::digest::DigestDataHash<'a, HL>,
200        const HL: usize,
201        const SL: usize,
202    > AppCredentialsPolicy<'static> for AppCheckerSignature<'a, S, H, HL, SL>
203{
204    fn require_credentials(&self) -> bool {
205        true
206    }
207
208    fn check_credentials(
209        &self,
210        credentials: TbfFooterV2Credentials,
211        binary: &'static [u8],
212    ) -> Result<(), (ErrorCode, TbfFooterV2Credentials, &'static [u8])> {
213        self.credentials.set(credentials);
214
215        if credentials.format() == self.credential_type {
216            // Save the signature we are trying to compare with.
217            self.signature.map(|b| {
218                b.as_mut_slice()[..SL].copy_from_slice(&credentials.data()[..SL]);
219            });
220
221            // Add the process binary to compute the hash.
222            self.hasher.clear_data();
223            match self.hasher.add_data(SubSlice::new(binary)) {
224                Ok(()) => Ok(()),
225                Err((e, b)) => Err((e, credentials, b.take())),
226            }
227        } else {
228            Err((ErrorCode::NOSUPPORT, credentials, binary))
229        }
230    }
231
232    fn set_client(&self, client: &'static dyn AppCredentialsPolicyClient<'static>) {
233        self.client.replace(client);
234    }
235}