1use aes_gcm_siv::aead::Aead;
6use aes_gcm_siv::{Aes256GcmSiv, KeyInit as _, Nonce};
7use async_trait::async_trait;
8use fuchsia_sync::Mutex;
9use fxfs_crypto::{
10 Crypt, EncryptionKey, FscryptKeyIdentifierAndNonce, KeyPurpose, ObjectType, UnwrappedKey,
11 WrappedKey, WrappingKeyId,
12};
13use rand::rngs::StdRng;
14use rand::{RngCore, SeedableRng};
15use std::collections::hash_map::{Entry, HashMap};
16use std::sync::atomic::{AtomicBool, Ordering};
17use zx_status as zx;
18
19fn zero_extended_nonce(val: u64) -> Nonce {
20 let mut nonce = Nonce::default();
21 nonce.as_mut_slice()[..8].copy_from_slice(&val.to_le_bytes());
22 nonce
23}
24
25struct Cipher {
26 aes_gcm_siv: Aes256GcmSiv,
28 wrapping_key: [u8; 32],
30}
31
32impl Cipher {
33 fn new(wrapping_key: [u8; 32]) -> Self {
34 Self { aes_gcm_siv: Aes256GcmSiv::new_from_slice(&wrapping_key).unwrap(), wrapping_key }
35 }
36
37 fn encrypt(&self, nonce: &Nonce, plaintext: &[u8]) -> Result<Vec<u8>, zx::Status> {
38 self.aes_gcm_siv.encrypt(nonce, plaintext).map_err(|_e| zx::Status::INTERNAL)
39 }
40
41 fn decrypt(&self, nonce: &Nonce, ciphertext: &[u8]) -> Result<Vec<u8>, zx::Status> {
42 self.aes_gcm_siv.decrypt(nonce, ciphertext).map_err(|_e| zx::Status::INTERNAL)
43 }
44}
45
46struct CryptBaseInner {
47 ciphers: HashMap<WrappingKeyId, Cipher>,
48 active_data_key: Option<WrappingKeyId>,
49 active_metadata_key: Option<WrappingKeyId>,
50}
51
52pub struct CryptBase {
54 inner: Mutex<CryptBaseInner>,
55 shutdown: AtomicBool,
56 filesystem_uuid: [u8; 16],
59}
60
61impl CryptBase {
62 pub fn new() -> Self {
63 Self {
64 inner: Mutex::new(CryptBaseInner {
65 ciphers: HashMap::new(),
66 active_data_key: None,
67 active_metadata_key: None,
68 }),
69 shutdown: AtomicBool::new(false),
70 filesystem_uuid: [0; 16],
71 }
72 }
73
74 pub fn add_wrapping_key(&self, id: WrappingKeyId, key: [u8; 32]) -> Result<(), zx::Status> {
75 let mut inner = self.inner.lock();
76 match inner.ciphers.entry(id) {
77 Entry::Occupied(_) => Err(zx::Status::ALREADY_EXISTS),
78 Entry::Vacant(v) => {
79 v.insert(Cipher::new(key));
80 Ok(())
81 }
82 }
83 }
84
85 pub fn set_active_key(&self, purpose: KeyPurpose, id: WrappingKeyId) -> Result<(), zx::Status> {
86 let mut inner = self.inner.lock();
87 if !inner.ciphers.contains_key(&id) {
88 return Err(zx::Status::NOT_FOUND);
89 }
90 match purpose {
91 KeyPurpose::Data => inner.active_data_key = Some(id),
92 KeyPurpose::Metadata => inner.active_metadata_key = Some(id),
93 }
94 Ok(())
95 }
96
97 pub fn forget_wrapping_key(&self, id: &WrappingKeyId) -> Result<(), zx::Status> {
98 let mut inner = self.inner.lock();
99 if let Some(active_id) = inner.active_data_key {
100 if active_id == *id {
101 return Err(zx::Status::INVALID_ARGS);
102 }
103 }
104 if let Some(active_id) = inner.active_metadata_key {
105 if active_id == *id {
106 return Err(zx::Status::INVALID_ARGS);
107 }
108 }
109 inner.ciphers.remove(id);
110 Ok(())
111 }
112
113 pub fn shutdown(&self) {
114 self.shutdown.store(true, Ordering::Relaxed);
115 }
116
117 pub fn set_filesystem_uuid(&mut self, uuid: &[u8; 16]) {
121 self.filesystem_uuid = *uuid;
122 }
123}
124
125#[async_trait]
126impl Crypt for CryptBase {
127 async fn create_key(
128 &self,
129 owner: u64,
130 purpose: KeyPurpose,
131 ) -> Result<(fxfs_crypto::FxfsKey, UnwrappedKey), zx::Status> {
132 if self.shutdown.load(Ordering::Relaxed) {
133 return Err(zx::Status::INTERNAL);
134 }
135 let inner = self.inner.lock();
136 let wrapping_key_id = match purpose {
137 KeyPurpose::Data => inner.active_data_key,
138 KeyPurpose::Metadata => inner.active_metadata_key,
139 }
140 .ok_or(zx::Status::INVALID_ARGS)?;
141
142 let cipher = inner.ciphers.get(&wrapping_key_id).ok_or(zx::Status::UNAVAILABLE)?;
143
144 let nonce = zero_extended_nonce(owner);
145
146 let mut uwnrapped_key = [0u8; 32];
147 StdRng::from_os_rng().fill_bytes(&mut uwnrapped_key);
148
149 let wrapped_key = cipher.encrypt(&nonce, &uwnrapped_key[..])?;
150 Ok((
151 fxfs_crypto::FxfsKey {
152 wrapping_key_id,
153 key: wrapped_key.try_into().map_err(|_| zx::Status::INTERNAL)?,
154 },
155 UnwrappedKey::new(uwnrapped_key.to_vec()),
156 ))
157 }
158
159 async fn create_key_with_id(
160 &self,
161 owner: u64,
162 wrapping_key_id: WrappingKeyId,
163 object_type: ObjectType,
164 ) -> Result<(EncryptionKey, UnwrappedKey), zx::Status> {
165 if self.shutdown.load(Ordering::Relaxed) {
166 return Err(zx::Status::INTERNAL);
167 }
168
169 match object_type {
170 ObjectType::Directory | ObjectType::Symlink => {
171 let mut nonce = [0; 16];
172 StdRng::from_os_rng().fill_bytes(&mut nonce);
173 let inner = self.inner.lock();
174 let cipher = inner.ciphers.get(&wrapping_key_id).ok_or(zx::Status::UNAVAILABLE)?;
175 let mut unwrapped_key = [0u8; 96];
176 fscrypt::hkdf::hkdf(&cipher.wrapping_key, &nonce, &mut unwrapped_key);
177 Ok((
178 EncryptionKey::FscryptInoLblk32Dir {
179 key_identifier: wrapping_key_id,
180 nonce: nonce.try_into().map_err(|_| zx::Status::INTERNAL)?,
181 },
182 UnwrappedKey::new(unwrapped_key.to_vec()),
183 ))
184 }
185 _ => {
186 let inner = self.inner.lock();
187 let cipher = inner.ciphers.get(&wrapping_key_id).ok_or(zx::Status::UNAVAILABLE)?;
188 let nonce = zero_extended_nonce(owner);
189 let mut unwrapped_key = [0u8; 32];
190 StdRng::from_os_rng().fill_bytes(&mut unwrapped_key);
191 let wrapped = cipher.encrypt(&nonce, &unwrapped_key[..])?;
192 Ok((
193 EncryptionKey::Fxfs(fxfs_crypto::FxfsKey {
194 wrapping_key_id,
195 key: wrapped.try_into().map_err(|_| zx::Status::INTERNAL)?,
196 }),
197 UnwrappedKey::new(unwrapped_key.to_vec()),
198 ))
199 }
200 }
201 }
202
203 async fn unwrap_key(
204 &self,
205 wrapped_key: &WrappedKey,
206 owner: u64,
207 ) -> Result<UnwrappedKey, zx::Status> {
208 if self.shutdown.load(Ordering::Relaxed) {
209 return Err(zx::Status::INTERNAL);
210 }
211
212 match wrapped_key {
213 WrappedKey::FscryptInoLblk32Dir(FscryptKeyIdentifierAndNonce {
214 key_identifier,
215 nonce,
216 }) => {
217 let inner = self.inner.lock();
218 let cipher = inner.ciphers.get(key_identifier).ok_or(zx::Status::UNAVAILABLE)?;
219 let mut unwrapped_key = [0u8; 96];
220 fscrypt::hkdf::hkdf(&cipher.wrapping_key, nonce, &mut unwrapped_key);
221 Ok(UnwrappedKey::new(unwrapped_key.to_vec()))
222 }
223 WrappedKey::Fxfs(fidl_fuchsia_fxfs::FxfsKey { wrapping_key_id, wrapped_key }) => {
224 let inner = self.inner.lock();
225 let cipher = inner.ciphers.get(wrapping_key_id).ok_or(zx::Status::UNAVAILABLE)?;
226 let mut nonce = Nonce::default();
227 nonce.as_mut_slice()[..8].copy_from_slice(&owner.to_le_bytes());
228 Ok(UnwrappedKey::new(cipher.decrypt(&nonce, wrapped_key)?))
229 }
230 _ => Err(zx::Status::NOT_SUPPORTED),
231 }
232 }
233}
234#[cfg(test)]
235mod tests {
236 use super::*;
237
238 #[fuchsia::test]
239 async fn test_wrap_unwrap() {
240 let crypt = CryptBase::new();
241 let key = [0xABu8; 32];
242 let id = [1u8; 16];
243 crypt.add_wrapping_key(id, key).expect("add_wrapping_key failed");
244 crypt.set_active_key(KeyPurpose::Data, id).expect("set_active_key failed");
245
246 let (fxfs_key, unwrapped_key) =
247 crypt.create_key(0, KeyPurpose::Data).await.expect("create_key failed");
248 assert_eq!(fxfs_key.wrapping_key_id, id);
249 assert_eq!(unwrapped_key.len(), 32);
250
251 let unwrapped_back = crypt
252 .unwrap_key(&WrappedKey::Fxfs(fxfs_key.into()), 0)
253 .await
254 .expect("unwrap_key failed");
255 assert_eq!(*unwrapped_key, *unwrapped_back);
256 }
257
258 #[fuchsia::test]
259 async fn test_forget_wrapping_key() {
260 let crypt = CryptBase::new();
261 let key = [0xABu8; 32];
262 let id = [1u8; 16];
263 crypt.add_wrapping_key(id, key).expect("add_wrapping_key failed");
264 assert_eq!(crypt.add_wrapping_key(id, key), Err(zx::Status::ALREADY_EXISTS));
265 crypt.forget_wrapping_key(&id).unwrap();
266 assert_eq!(
267 crypt
268 .unwrap_key(
269 &WrappedKey::Fxfs(fidl_fuchsia_fxfs::FxfsKey {
270 wrapping_key_id: id,
271 wrapped_key: [0u8; 48]
272 }),
273 0
274 )
275 .await
276 .expect_err("unwrap_key should fail when wrapping key is forgotten"),
277 zx::Status::UNAVAILABLE
278 );
279 crypt.add_wrapping_key(id, key).expect("add_wrapping_key failed");
280 }
281
282 #[fuchsia::test]
283 async fn test_active_key_management() {
284 let crypt = CryptBase::new();
285 let key = [0xABu8; 32];
286 let id1 = [0u8; 16];
287 let id2 = [1u8; 16];
288 crypt.add_wrapping_key(id1, key).expect("add_wrapping_key failed");
289 crypt.add_wrapping_key(id2, key).expect("add_wrapping_key failed");
290
291 crypt.set_active_key(KeyPurpose::Data, id1).expect("set_active_key failed");
292 crypt.set_active_key(KeyPurpose::Metadata, id2).expect("set_active_key failed");
293
294 assert_eq!(crypt.forget_wrapping_key(&id1), Err(zx::Status::INVALID_ARGS));
295 assert_eq!(crypt.forget_wrapping_key(&id2), Err(zx::Status::INVALID_ARGS));
296 }
297
298 #[fuchsia::test]
299 async fn test_shutdown() {
300 let crypt = CryptBase::new();
301 let key = [0xABu8; 32];
302 let id = [1u8; 16];
303 crypt.add_wrapping_key(id, key).expect("add_wrapping_key failed");
304 crypt.set_active_key(KeyPurpose::Data, id).expect("set_active_key failed");
305
306 crypt.shutdown();
307
308 assert_eq!(
309 crypt
310 .create_key(0, KeyPurpose::Data)
311 .await
312 .expect_err("create_key should fail when crypt has shut down"),
313 zx::Status::INTERNAL
314 );
315 assert_eq!(
316 crypt
317 .create_key_with_id(0, id, ObjectType::File)
318 .await
319 .expect_err("create_key_with_id should fail when crypt has shut down"),
320 zx::Status::INTERNAL
321 );
322 assert_eq!(
323 crypt
324 .unwrap_key(
325 &WrappedKey::Fxfs(fidl_fuchsia_fxfs::FxfsKey {
326 wrapping_key_id: id,
327 wrapped_key: [0u8; 48]
328 }),
329 0,
330 )
331 .await
332 .expect_err("unwrap_key should fail when crypt has shut down"),
333 zx::Status::INTERNAL
334 );
335 }
336
337 #[fuchsia::test]
338 async fn test_create_key_no_active_key() {
339 let crypt = CryptBase::new();
340 assert_eq!(
341 crypt
342 .create_key(0, KeyPurpose::Data)
343 .await
344 .expect_err("create_key should fail when no active key is set"),
345 zx::Status::INVALID_ARGS
346 );
347 }
348
349 #[fuchsia::test]
350 async fn test_create_key_with_id_not_found() {
351 let crypt = CryptBase::new();
352 let id = [1u8; 16];
353 assert_eq!(
354 crypt.create_key_with_id(0, id, ObjectType::File).await.expect_err(
355 "create_key_with_id should fail when no active key is set at wrapping key id"
356 ),
357 zx::Status::UNAVAILABLE
358 );
359 }
360
361 #[fuchsia::test]
362 async fn test_unwrap_key_not_found() {
363 let crypt = CryptBase::new();
364 let id = [1u8; 16];
365 assert_eq!(
366 crypt
367 .unwrap_key(
368 &WrappedKey::Fxfs(fidl_fuchsia_fxfs::FxfsKey {
369 wrapping_key_id: id,
370 wrapped_key: [0u8; 48]
371 }),
372 0,
373 )
374 .await
375 .expect_err("unwrap_key should fail when no active key is set at wrapping key id"),
376 zx::Status::UNAVAILABLE
377 );
378 }
379
380 #[fuchsia::test]
381 async fn test_unwrap_key_wrong_owner() {
382 let crypt = CryptBase::new();
383 let key = [0xABu8; 32];
384 let id = [0u8; 16];
385 crypt.add_wrapping_key(id, key).expect("add_wrapping_key failed");
386 crypt.set_active_key(KeyPurpose::Data, id).expect("set_active_key failed");
387
388 let (fxfs_key, _unwrapped_key) =
389 crypt.create_key(0, KeyPurpose::Data).await.expect("create_key failed");
390 assert_eq!(
392 crypt
393 .unwrap_key(&WrappedKey::Fxfs(fxfs_key.into()), 1)
394 .await
395 .expect_err("unwrap_key should fail when owner does not match"),
396 zx::Status::INTERNAL
397 );
398 }
399
400 #[fuchsia::test]
401 async fn test_wrap_unwrap_key_with_arbitrary_wrapping_key_id() {
402 let crypt = CryptBase::new();
403 let key = [0xABu8; 32];
404 let id = [2u8; 16];
405 crypt.add_wrapping_key(id, key).expect("add_key failed");
406
407 let (wrapped_key, unwrapped_key) = crypt
408 .create_key_with_id(0, id, ObjectType::File)
409 .await
410 .expect("create_key_with_id failed");
411 let unwrap_result =
412 crypt.unwrap_key(&WrappedKey::from(wrapped_key), 0).await.expect("unwrap_key failed");
413 assert_eq!(*unwrap_result, *unwrapped_key);
414
415 let (wrapped_key, unwrapped_key) = crypt
417 .create_key_with_id(1, id, ObjectType::File)
418 .await
419 .expect("create_key_with_id failed");
420 let unwrap_result =
421 crypt.unwrap_key(&WrappedKey::from(wrapped_key), 1).await.expect("unwrap_key failed");
422 assert_eq!(*unwrap_result, *unwrapped_key);
423 }
424
425 #[fuchsia::test]
426 async fn test_unwrap_key_wrong_key() {
427 let crypt = CryptBase::new();
428 let key = [0xABu8; 32];
429 let id = [0u8; 16];
430 crypt.add_wrapping_key(id, key).expect("add_key failed");
431 crypt.set_active_key(KeyPurpose::Data, id).expect("set_active_key failed");
432
433 let (fxfs_key, _unwrapped_key) =
434 crypt.create_key(0, KeyPurpose::Data).await.expect("create_key failed");
435 let mut modified_wrapped_key = fxfs_key.key.to_vec();
436 for byte in &mut modified_wrapped_key {
437 *byte ^= 0xff;
438 }
439 assert_eq!(
440 crypt
441 .unwrap_key(
442 &WrappedKey::Fxfs(fidl_fuchsia_fxfs::FxfsKey {
443 wrapping_key_id: fxfs_key.wrapping_key_id,
444 wrapped_key: modified_wrapped_key.clone().try_into().unwrap(),
445 }),
446 0,
447 )
448 .await
449 .is_err(),
450 true
451 );
452 }
453}