Skip to main content

fake_keymint/
lib.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! A fake implementation of fuchsia.security.keymint.SealingKeys and
6//! fuchsia.security.keymint.Admin for testing purposes.
7//!
8//! IMPORTANT: This implementation is insecure!
9
10use aes_gcm_siv::aead::Aead as _;
11use aes_gcm_siv::{Aes128GcmSiv, Key, KeyInit as _, Nonce};
12use anyhow::{anyhow, bail};
13use fidl::endpoints::{ClientEnd, create_request_stream};
14use fidl_fuchsia_security_keymint::{
15    AdminMarker, AdminRequest, AdminRequestStream, DeleteError, SealError, SealingKeysMarker,
16    SealingKeysRequest, SealingKeysRequestStream, UnsealError, UpgradeError,
17};
18use fuchsia_sync::Mutex;
19use futures::{FutureExt as _, TryStreamExt as _};
20use log::warn;
21use std::collections::BTreeMap;
22use std::collections::btree_map::Entry;
23use std::future::Future;
24use std::pin::pin;
25
26type KeyInfo = Vec<u8>;
27
28struct SealingKey {
29    cipher: Aes128GcmSiv,
30    key_blob: Vec<u8>,
31}
32
33#[derive(Default)]
34struct Inner {
35    sealing_keys: BTreeMap<KeyInfo, SealingKey>,
36    // Epoch is mixed into keys, and is incremented each time DeleteAllKeys is called.  This ensures
37    // that previously deleted keys cannot be reused.
38    epoch: u64,
39}
40
41impl Inner {
42    const IV: [u8; 12] = [0u8; 12];
43
44    // NB: A real Keymint implementation would return a different sealing key each time this is
45    // called, and remember the keys that are created.  Since we don't have anywhere to persist
46    // them, we just derive the sealing key from the key info directly, and then store the key info
47    // as the sealed key blob.  Obviously, this is not secure.
48    fn derive_key(key_info: &KeyInfo, epoch: u64) -> SealingKey {
49        let mut key_bytes = [0u8; 16];
50        let len = key_info.len().min(16);
51        key_bytes[..len].copy_from_slice(&key_info[..len]);
52
53        // XOR epoch into the first 8 bytes to ensure keys change with epoch.
54        let epoch_bytes = epoch.to_le_bytes();
55        for i in 0..8 {
56            key_bytes[i] ^= epoch_bytes[i];
57        }
58
59        let cipher = Aes128GcmSiv::new(Key::<Aes128GcmSiv>::from_slice(&key_bytes));
60        SealingKey { cipher, key_blob: key_info.clone() }
61    }
62
63    fn handle_create_request(&mut self, key_info: KeyInfo) -> Vec<u8> {
64        match self.sealing_keys.entry(key_info.clone()) {
65            Entry::Vacant(vacant) => {
66                vacant.insert(Self::derive_key(&key_info, self.epoch)).key_blob.clone()
67            }
68            Entry::Occupied(occupied) => occupied.get().key_blob.clone(),
69        }
70    }
71
72    fn handle_seal_request(
73        &mut self,
74        key_info: KeyInfo,
75        key_blob: Vec<u8>,
76        secret: Vec<u8>,
77    ) -> anyhow::Result<Vec<u8>> {
78        let sealing_key =
79            self.sealing_keys.get(&key_info).ok_or_else(|| anyhow!("No sealing key"))?;
80        if key_blob != sealing_key.key_blob {
81            bail!("Wrong key blob");
82        }
83        let sealed_secret =
84            sealing_key.cipher.encrypt(&Nonce::from_slice(&Self::IV), &secret[..])?;
85        Ok(sealed_secret)
86    }
87
88    fn handle_unseal_request(
89        &mut self,
90        key_info: KeyInfo,
91        key_blob: Vec<u8>,
92        sealed_secret: Vec<u8>,
93    ) -> anyhow::Result<Vec<u8>> {
94        let sealing_key = self
95            .sealing_keys
96            .entry(key_info.clone())
97            .or_insert_with(|| Self::derive_key(&key_info, self.epoch));
98        if key_blob != sealing_key.key_blob {
99            bail!("Wrong key blob");
100        }
101        let secret =
102            sealing_key.cipher.decrypt(&Nonce::from_slice(&Self::IV), &sealed_secret[..])?;
103        Ok(secret)
104    }
105
106    fn handle_delete_all_keys_request(&mut self) {
107        self.sealing_keys.clear();
108        self.epoch += 1;
109    }
110
111    fn handle_upgrade_request(
112        &mut self,
113        _key_info: KeyInfo,
114        _key_blob: Vec<u8>,
115    ) -> anyhow::Result<Vec<u8>> {
116        // Since we store key blobs in a way that is 1-to-1 with `key_info`, do not upgrade keys.
117        // This is consistent with keymint behaviour when a client attempts to upgrade a key that
118        // does not require an upgrade.
119        Ok(vec![])
120    }
121
122    fn handle_delete_request(&mut self, key_blob: Vec<u8>) -> anyhow::Result<()> {
123        let initial_len = self.sealing_keys.len();
124        self.sealing_keys.retain(|_info, sealing_key| &sealing_key.key_blob != &key_blob);
125        let modified_len = self.sealing_keys.len();
126        if initial_len == modified_len {
127            bail!("Failed to locate matching key for deletion");
128        }
129        if modified_len < initial_len - 1 {
130            bail!("Key blob matched multiple key entries");
131        }
132        Ok(())
133    }
134}
135
136/// A fake (insecure) implementation of the Keymint FIDL.
137#[derive(Default)]
138pub struct FakeKeymint {
139    inner: Mutex<Inner>,
140}
141
142impl FakeKeymint {
143    /// Handles [`SealingKeysRequestStream`] to completion.
144    pub async fn run_sealing_keys_service(
145        &self,
146        stream: SealingKeysRequestStream,
147    ) -> Result<(), fidl::Error> {
148        stream
149            .try_for_each_concurrent(None, move |request| async move {
150                match request {
151                    SealingKeysRequest::CreateSealingKey { key_info, responder } => {
152                        responder.send(Ok(&*self.inner.lock().handle_create_request(key_info)))?;
153                    }
154                    SealingKeysRequest::Seal { key_info, key_blob, secret, responder } => {
155                        match self.inner.lock().handle_seal_request(key_info, key_blob, secret) {
156                            Ok(sealed_secret) => responder.send(Ok(&*sealed_secret))?,
157                            Err(err) => {
158                                warn!(err:?; "Failed to seal secret");
159                                responder.send(Err(SealError::FailedSeal))?
160                            }
161                        }
162                    }
163                    SealingKeysRequest::Unseal { key_info, key_blob, sealed_secret, responder } => {
164                        match self.inner.lock().handle_unseal_request(
165                            key_info,
166                            key_blob,
167                            sealed_secret,
168                        ) {
169                            Ok(secret) => responder.send(Ok(&*secret))?,
170                            Err(err) => {
171                                warn!(err:?; "Failed to unseal secret");
172                                responder.send(Err(UnsealError::FailedUnseal))?
173                            }
174                        }
175                    }
176                    SealingKeysRequest::UpgradeSealingKey { key_info, key_blob, responder } => {
177                        match self.inner.lock().handle_upgrade_request(key_info, key_blob) {
178                            Ok(key) => responder.send(Ok(&*key))?,
179                            Err(err) => {
180                                warn!(err:?; "Failed to upgrade key");
181                                responder.send(Err(UpgradeError::FailedUpgrade))?
182                            }
183                        }
184                    }
185                    SealingKeysRequest::DeleteSealingKey { key_blob, responder } => {
186                        match self.inner.lock().handle_delete_request(key_blob) {
187                            Ok(()) => responder.send(Ok(()))?,
188                            Err(err) => {
189                                warn!(err:?; "Failed to delete key");
190                                responder.send(Err(DeleteError::FailedDelete))?
191                            }
192                        }
193                    }
194                }
195                Ok(())
196            })
197            .await
198    }
199
200    /// Handles [`AdminRequestStream`] to completion.
201    pub async fn run_admin_service(&self, stream: AdminRequestStream) -> Result<(), fidl::Error> {
202        stream
203            .try_for_each_concurrent(None, move |request| async move {
204                match request {
205                    AdminRequest::DeleteAllKeys { responder } => {
206                        responder.send(Ok(self.inner.lock().handle_delete_all_keys_request()))?;
207                    }
208                }
209                Ok(())
210            })
211            .await
212    }
213}
214
215/// Runs `f` with a scoped FakeKeymint instance.  The instance will be automatically terminated on
216/// completion.
217pub async fn with_keymint_service<R, Fut: Future<Output = anyhow::Result<R>>>(
218    f: impl FnOnce(ClientEnd<SealingKeysMarker>, ClientEnd<AdminMarker>) -> Fut,
219) -> anyhow::Result<R> {
220    let (sealing_keys_client, sealing_keys_stream) = create_request_stream::<SealingKeysMarker>();
221    let (admin_client, admin_stream) = create_request_stream::<AdminMarker>();
222    let fake_keymint = FakeKeymint::default();
223    let mut sealing_keys_service =
224        pin!(async { fake_keymint.run_sealing_keys_service(sealing_keys_stream).await }.fuse());
225    let mut admin_service =
226        pin!(async { fake_keymint.run_admin_service(admin_stream).await }.fuse());
227    let mut fut = pin!(f(sealing_keys_client, admin_client).fuse());
228
229    loop {
230        futures::select! {
231            _ = sealing_keys_service => {}
232            _ = admin_service => {}
233            result = fut => return result,
234        }
235    }
236}
237
238#[cfg(test)]
239mod tests {
240    use super::*;
241
242    #[fuchsia::test]
243    async fn create_seal_unseal() {
244        with_keymint_service(|keymint, _| async {
245            let keymint = keymint.into_proxy();
246            const KEY_INFO: [u8; 16] = [1u8; 16];
247            let key_blob = keymint
248                .create_sealing_key(&KEY_INFO[..])
249                .await
250                .expect("FIDL error")
251                .expect("create error");
252            const SECRET: [u8; 16] = [0xffu8; 16];
253            let sealed = keymint
254                .seal(&KEY_INFO[..], &key_blob[..], &SECRET[..])
255                .await
256                .expect("FIDL error")
257                .expect("seal error");
258            assert_ne!(sealed, SECRET);
259            let unsealed = keymint
260                .unseal(&KEY_INFO[..], &key_blob[..], &sealed[..])
261                .await
262                .expect("FIDL error")
263                .expect("unseal error");
264            assert_eq!(unsealed, SECRET);
265            Ok(())
266        })
267        .await
268        .unwrap();
269    }
270
271    #[fuchsia::test]
272    async fn seal_failure_on_wrong_key_info() {
273        with_keymint_service(|keymint, _| async {
274            let keymint = keymint.into_proxy();
275            const KEY_INFO: [u8; 16] = [1u8; 16];
276            let key_blob = keymint
277                .create_sealing_key(&KEY_INFO[..])
278                .await
279                .expect("FIDL error")
280                .expect("create error");
281            const SECRET: [u8; 16] = [0xffu8; 16];
282            keymint
283                .seal(&[2u8; 16], &key_blob[..], &SECRET[..])
284                .await
285                .expect("FIDL error")
286                .expect_err("seal should fail");
287            Ok(())
288        })
289        .await
290        .unwrap();
291    }
292
293    #[fuchsia::test]
294    async fn unseal_failure_on_wrong_sealing_key() {
295        with_keymint_service(|keymint, _| async {
296            let keymint = keymint.into_proxy();
297            const KEY_INFO: [u8; 16] = [1u8; 16];
298            let key_blob = keymint
299                .create_sealing_key(&KEY_INFO[..])
300                .await
301                .expect("FIDL error")
302                .expect("create error");
303            const SECRET: [u8; 16] = [0xffu8; 16];
304            let sealed = keymint
305                .seal(&KEY_INFO[..], &key_blob[..], &SECRET[..])
306                .await
307                .expect("FIDL error")
308                .expect("seal error");
309            assert_ne!(sealed, SECRET);
310            keymint
311                .unseal(&[2u8; 16], &key_blob[..], &sealed[..])
312                .await
313                .expect("FIDL error")
314                .expect_err("unseal should fail");
315            Ok(())
316        })
317        .await
318        .unwrap();
319    }
320
321    #[fuchsia::test]
322    async fn unseal_failure_on_wrong_secret_blob() {
323        with_keymint_service(|keymint, _| async {
324            let keymint = keymint.into_proxy();
325            const KEY_INFO: [u8; 16] = [1u8; 16];
326            let key_blob = keymint
327                .create_sealing_key(&KEY_INFO[..])
328                .await
329                .expect("FIDL error")
330                .expect("create error");
331            const SECRET: [u8; 16] = [0xffu8; 16];
332            let _ = keymint
333                .seal(&KEY_INFO[..], &key_blob[..], &SECRET[..])
334                .await
335                .expect("FIDL error")
336                .expect("seal error");
337            keymint
338                .unseal(&KEY_INFO[..], &key_blob[..], &[0u8; 16])
339                .await
340                .expect("FIDL error")
341                .expect_err("unseal should fail");
342            Ok(())
343        })
344        .await
345        .unwrap();
346    }
347
348    #[fuchsia::test]
349    async fn delete_all_keys_renders_key_unusable() {
350        with_keymint_service(|keymint, admin| async {
351            let keymint = keymint.into_proxy();
352            const KEY_INFO: [u8; 16] = [1u8; 16];
353            let key_blob = keymint
354                .create_sealing_key(&KEY_INFO[..])
355                .await
356                .expect("FIDL error")
357                .expect("create error");
358
359            let admin = admin.into_proxy();
360            admin.delete_all_keys().await.expect("FIDL error").expect("create error");
361
362            const SECRET: [u8; 16] = [0xffu8; 16];
363            let _ = keymint
364                .seal(&KEY_INFO[..], &key_blob[..], &SECRET[..])
365                .await
366                .expect("FIDL error")
367                .expect_err("seal should fail");
368            Ok(())
369        })
370        .await
371        .unwrap();
372    }
373
374    #[fuchsia::test]
375    async fn delete_keys_succeeds() {
376        with_keymint_service(|keymint, _admin| async {
377            let keymint = keymint.into_proxy();
378            const KEY_INFO: [u8; 16] = [1u8; 16];
379            let key_blob = keymint
380                .create_sealing_key(&KEY_INFO[..])
381                .await
382                .expect("FIDL error")
383                .expect("create error");
384
385            keymint.delete_sealing_key(&key_blob).await.expect("FIDL error").expect("delete error");
386            Ok(())
387        })
388        .await
389        .unwrap();
390    }
391
392    #[fuchsia::test]
393    async fn delete_keys_fails_bad_key_blob() {
394        with_keymint_service(|keymint, _admin| async {
395            let keymint = keymint.into_proxy();
396            const KEY_INFO: [u8; 16] = [1u8; 16];
397            let mut key_blob = keymint
398                .create_sealing_key(&KEY_INFO[..])
399                .await
400                .expect("FIDL error")
401                .expect("create error");
402
403            key_blob[0] ^= 0xffu8;
404
405            keymint
406                .delete_sealing_key(&key_blob)
407                .await
408                .expect("FIDL error")
409                .expect_err("delete success");
410            Ok(())
411        })
412        .await
413        .unwrap();
414    }
415}