1use aes::Aes256;
6use aes::cipher::{BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
7use anyhow::anyhow;
8use async_trait::async_trait;
9use chacha20::cipher::{KeyIvInit, StreamCipher as _, StreamCipherSeek};
10use chacha20::{self, ChaCha20};
11use fprint::TypeFingerprint;
12use futures::TryStreamExt as _;
13use futures::stream::FuturesUnordered;
14use serde::de::{Error as SerdeError, Visitor};
15use serde::{Deserialize, Deserializer, Serialize, Serializer};
16use std::collections::BTreeMap;
17use storage_xts::{Tweak, XtsCtsProcessor};
18use zerocopy::IntoBytes;
19use zx_status as zx;
20
21mod cipher;
22pub mod ff1;
23
24pub use cipher::fscrypt_ino_lblk32::FscryptSoftwareInoLblk32FileCipher;
25pub use cipher::fxfs::FxfsCipher;
26pub use cipher::{
27 Cipher, CipherHolder, CipherSet, FindKeyResult, KeyType, MutPtrByteSlice, PtrByteSlice,
28 key_to_cipher,
29};
30pub use fidl_fuchsia_fxfs::{
31 EmptyStruct, FscryptKeyIdentifier, FscryptKeyIdentifierAndNonce, ObjectType, WrappedKey,
32};
33
34pub use cipher::FSCRYPT_PADDING;
35pub const FXFS_KEY_SIZE: usize = 256 / 8;
36pub const FXFS_WRAPPED_KEY_SIZE: usize = FXFS_KEY_SIZE + 16;
37
38#[derive(Debug)]
41pub struct UnwrappedKey(Vec<u8>);
42impl UnwrappedKey {
43 pub fn new(key: Vec<u8>) -> Self {
44 UnwrappedKey(key)
45 }
46}
47impl std::ops::Deref for UnwrappedKey {
48 type Target = Vec<u8>;
49 fn deref(&self) -> &Self::Target {
50 &self.0
51 }
52}
53
54#[repr(transparent)]
56#[derive(Clone, Debug, PartialEq)]
57pub struct WrappedKeyBytes(pub [u8; FXFS_WRAPPED_KEY_SIZE]);
58impl Default for WrappedKeyBytes {
59 fn default() -> Self {
60 Self([0u8; FXFS_WRAPPED_KEY_SIZE])
61 }
62}
63impl TryFrom<Vec<u8>> for WrappedKeyBytes {
64 type Error = anyhow::Error;
65
66 fn try_from(buf: Vec<u8>) -> Result<Self, Self::Error> {
67 Ok(Self(buf.try_into().map_err(|_| anyhow!("wrapped key wrong length"))?))
68 }
69}
70impl From<[u8; FXFS_WRAPPED_KEY_SIZE]> for WrappedKeyBytes {
71 fn from(buf: [u8; FXFS_WRAPPED_KEY_SIZE]) -> Self {
72 Self(buf)
73 }
74}
75impl TypeFingerprint for WrappedKeyBytes {
76 fn fingerprint() -> String {
77 "WrappedKeyBytes".to_owned()
78 }
79}
80
81impl std::ops::Deref for WrappedKeyBytes {
82 type Target = [u8; FXFS_WRAPPED_KEY_SIZE];
83 fn deref(&self) -> &Self::Target {
84 &self.0
85 }
86}
87
88impl std::ops::DerefMut for WrappedKeyBytes {
89 fn deref_mut(&mut self) -> &mut Self::Target {
90 &mut self.0
91 }
92}
93
94impl Serialize for WrappedKeyBytes {
97 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
98 where
99 S: Serializer,
100 {
101 serializer.serialize_bytes(&self[..])
102 }
103}
104
105impl<'de> Deserialize<'de> for WrappedKeyBytes {
106 fn deserialize<D>(deserializer: D) -> Result<WrappedKeyBytes, D::Error>
107 where
108 D: Deserializer<'de>,
109 {
110 struct WrappedKeyVisitor;
111
112 impl<'d> Visitor<'d> for WrappedKeyVisitor {
113 type Value = WrappedKeyBytes;
114
115 fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
116 formatter.write_str("Expected wrapped keys to be 48 bytes")
117 }
118
119 fn visit_bytes<E>(self, bytes: &[u8]) -> Result<WrappedKeyBytes, E>
120 where
121 E: SerdeError,
122 {
123 self.visit_byte_buf(bytes.to_vec())
124 }
125
126 fn visit_byte_buf<E>(self, bytes: Vec<u8>) -> Result<WrappedKeyBytes, E>
127 where
128 E: SerdeError,
129 {
130 let orig_len = bytes.len();
131 let bytes: [u8; FXFS_WRAPPED_KEY_SIZE] =
132 bytes.try_into().map_err(|_| SerdeError::invalid_length(orig_len, &self))?;
133 Ok(WrappedKeyBytes::from(bytes))
134 }
135 }
136 deserializer.deserialize_byte_buf(WrappedKeyVisitor)
137 }
138}
139
140fn reject_legacy_key<'de, D: Deserializer<'de>>(_: D) -> Result<FxfsKey, D::Error> {
141 Err(SerdeError::custom("LegacyFxfs keys are no longer supported"))
142}
143
144#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TypeFingerprint)]
146pub enum EncryptionKey {
147 LegacyFxfs(#[serde(deserialize_with = "reject_legacy_key")] FxfsKey),
149 FscryptInoLblk32File {
155 key_identifier: [u8; 16],
156 },
157 FscryptInoLblk32Dir {
158 key_identifier: [u8; 16],
159 nonce: [u8; 16],
160 },
161 Fxfs(FxfsKey),
163}
164
165impl EncryptionKey {
166 pub fn wrapping_key_id(&self) -> Option<WrappingKeyId> {
167 match self {
168 EncryptionKey::LegacyFxfs(_) => unreachable!(),
169 EncryptionKey::Fxfs(key) => Some(key.wrapping_key_id),
170 EncryptionKey::FscryptInoLblk32File { key_identifier }
171 | EncryptionKey::FscryptInoLblk32Dir { key_identifier, .. } => Some(*key_identifier),
172 }
173 }
174}
175
176impl<'a> arbitrary::Arbitrary<'a> for EncryptionKey {
177 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
178 Ok(match u.int_in_range(0..=2)? {
179 0 => EncryptionKey::FscryptInoLblk32File { key_identifier: u.arbitrary()? },
180 1 => EncryptionKey::FscryptInoLblk32Dir {
181 key_identifier: u.arbitrary()?,
182 nonce: u.arbitrary()?,
183 },
184 2 => EncryptionKey::Fxfs(u.arbitrary()?),
185 _ => unreachable!(),
186 })
187 }
188}
189
190impl From<EncryptionKey> for WrappedKey {
191 fn from(value: EncryptionKey) -> Self {
192 match value {
193 EncryptionKey::LegacyFxfs(_) => unreachable!(),
194 EncryptionKey::Fxfs(key) => WrappedKey::Fxfs(key.into()),
195 EncryptionKey::FscryptInoLblk32File { key_identifier } => {
196 WrappedKey::FscryptInoLblk32File(FscryptKeyIdentifier { key_identifier })
197 }
198 EncryptionKey::FscryptInoLblk32Dir { key_identifier, nonce } => {
199 WrappedKey::FscryptInoLblk32Dir(FscryptKeyIdentifierAndNonce {
200 key_identifier,
201 nonce,
202 })
203 }
204 }
205 }
206}
207
208impl From<&EncryptionKey> for KeyType {
209 fn from(value: &EncryptionKey) -> Self {
210 match value {
211 EncryptionKey::LegacyFxfs(_) => unreachable!(),
212 EncryptionKey::Fxfs(_) => KeyType::Fxfs,
213 EncryptionKey::FscryptInoLblk32File { .. } => KeyType::FscryptInoLblk32File,
214 EncryptionKey::FscryptInoLblk32Dir { .. } => KeyType::FscryptInoLblk32Dir,
215 }
216 }
217}
218
219impl TryFrom<WrappedKey> for EncryptionKey {
220 type Error = zx::Status;
221
222 fn try_from(value: WrappedKey) -> Result<Self, Self::Error> {
223 Ok(match value {
224 WrappedKey::Fxfs(fidl_fuchsia_fxfs::FxfsKey { wrapping_key_id, wrapped_key }) => {
225 EncryptionKey::Fxfs(FxfsKey { wrapping_key_id, key: WrappedKeyBytes(wrapped_key) })
226 }
227 WrappedKey::FscryptInoLblk32File(FscryptKeyIdentifier { key_identifier }) => {
228 EncryptionKey::FscryptInoLblk32File { key_identifier }
229 }
230 WrappedKey::FscryptInoLblk32Dir(FscryptKeyIdentifierAndNonce {
231 key_identifier,
232 nonce,
233 }) => EncryptionKey::FscryptInoLblk32Dir { key_identifier, nonce },
234 _ => return Err(zx::Status::NOT_SUPPORTED),
235 })
236 }
237}
238
239#[derive(Clone, Default, Debug, Serialize, Deserialize, TypeFingerprint, PartialEq)]
242pub struct FxfsKey {
243 pub wrapping_key_id: WrappingKeyId,
246 pub key: WrappedKeyBytes,
252}
253
254pub type WrappingKeyId = [u8; 16];
255
256impl From<FxfsKey> for fidl_fuchsia_fxfs::FxfsKey {
257 fn from(value: FxfsKey) -> Self {
258 fidl_fuchsia_fxfs::FxfsKey {
259 wrapping_key_id: value.wrapping_key_id,
260 wrapped_key: value.key.0,
261 }
262 }
263}
264
265impl<'a> arbitrary::Arbitrary<'a> for FxfsKey {
266 fn arbitrary(_u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
267 return Ok(FxfsKey::default());
269 }
270}
271
272pub struct StreamCipher(ChaCha20);
276
277impl StreamCipher {
278 pub fn new(key: &UnwrappedKey, offset: u64) -> Self {
279 let mut cipher = Self(ChaCha20::new(
280 &chacha20::Key::try_from(&key[..]).expect("Invalid StreamCipher key length"),
281 &[0; 12].into(),
282 ));
283 cipher.0.seek(offset);
284 cipher
285 }
286
287 pub fn encrypt(&mut self, buffer: &mut [u8]) {
288 fxfs_trace::duration!("StreamCipher::encrypt", "len" => buffer.len());
289 self.0.apply_keystream(buffer);
290 }
291
292 pub fn decrypt(&mut self, buffer: &mut [u8]) {
293 fxfs_trace::duration!("StreamCipher::decrypt", "len" => buffer.len());
294 self.0.apply_keystream(buffer);
295 }
296
297 pub fn offset(&self) -> u64 {
298 self.0.current_pos()
299 }
300
301 pub fn key_is_new(&self) -> bool {
302 0 == self.0.current_pos()
303 }
304}
305
306pub struct JournalXtsCipher {
308 key: Aes256,
309 aligned_buf: Vec<u128>,
311 tweak: u64,
312 key_start_tweak: u64,
313}
314
315impl JournalXtsCipher {
316 pub fn new(key: &UnwrappedKey, tweak: u64) -> Self {
317 Self {
318 key: Aes256::new(key.as_slice().try_into().expect("Invalid key length")),
319 aligned_buf: Vec::new(),
320 tweak,
321 key_start_tweak: tweak,
322 }
323 }
324
325 pub fn encrypt(&mut self, buffer: &[u8]) -> Vec<u8> {
328 let out_len = std::cmp::max(buffer.len(), 16);
329 let mut out_buf = vec![0u8; out_len];
330 let mut tweak = Tweak::new(self.tweak as u128);
331 self.key.encrypt_block(tweak.as_mut_bytes().try_into().unwrap());
332 if buffer.as_ptr().cast::<u128>().is_aligned()
333 && buffer.len() >= 16
334 && out_buf.as_ptr().cast::<u128>().is_aligned()
335 {
336 let slice = PtrByteSlice::from(buffer);
337 let out_slice = MutPtrByteSlice::from(&mut out_buf[..]);
338 self.key.encrypt_with_backend(XtsCtsProcessor::new(tweak, slice, out_slice));
339 } else {
340 self.aligned_buf.resize(out_len.div_ceil(16), 0u128);
341 let aligned_bytes = &mut self.aligned_buf.as_mut_bytes()[..out_len];
342 if buffer.len() < 16 {
343 aligned_bytes.fill(0);
344 }
345 aligned_bytes[..buffer.len()].copy_from_slice(buffer);
346 let slice = MutPtrByteSlice::from(&mut *aligned_bytes);
347 self.key.encrypt_with_backend(XtsCtsProcessor::new_in_place(tweak, slice));
348 out_buf.copy_from_slice(&aligned_bytes[..out_len]);
349 }
350 self.tweak += 1;
351 out_buf
352 }
353
354 pub fn decrypt(&mut self, buffer: &mut [u8]) {
357 assert!(buffer.len() >= 16);
358 let len = buffer.len();
359 let mut tweak = Tweak::new(self.tweak as u128);
360 self.key.encrypt_block(tweak.as_mut_bytes().try_into().unwrap());
361
362 if buffer.as_ptr().cast::<u128>().is_aligned() {
363 let slice = MutPtrByteSlice::from(buffer);
364 self.key.decrypt_with_backend(XtsCtsProcessor::new_in_place(tweak, slice));
365 } else {
366 self.aligned_buf.resize(buffer.len().div_ceil(16), 0u128);
367 let aligned_bytes = &mut self.aligned_buf.as_mut_bytes()[..len];
368 aligned_bytes.copy_from_slice(buffer);
369 let slice = MutPtrByteSlice::from(aligned_bytes);
370 self.key.decrypt_with_backend(XtsCtsProcessor::new_in_place(tweak, slice));
371 buffer.copy_from_slice(&self.aligned_buf.as_bytes()[..len]);
372 }
373 self.tweak += 1;
374 }
375
376 pub fn current_tweak(&self) -> u64 {
377 self.tweak
378 }
379
380 pub fn key_is_new(&self) -> bool {
381 self.tweak - self.key_start_tweak == 0
382 }
383}
384
385pub enum JournalCipher {
386 ChaCha20(StreamCipher),
387 Aes256Xts(Box<JournalXtsCipher>),
388}
389
390impl JournalCipher {
391 pub fn new_chacha20(key: &UnwrappedKey, offset: u64) -> Self {
392 JournalCipher::ChaCha20(StreamCipher::new(key, offset))
393 }
394
395 pub fn new_aes256_xts(key: &UnwrappedKey, current_tweak: u64) -> Self {
396 JournalCipher::Aes256Xts(Box::new(JournalXtsCipher::new(key, current_tweak)))
397 }
398
399 pub fn encrypt(&mut self, buffer: &[u8]) -> Vec<u8> {
400 match self {
401 JournalCipher::ChaCha20(_) => unreachable!(),
402 JournalCipher::Aes256Xts(c) => c.encrypt(buffer),
403 }
404 }
405
406 pub fn decrypt(&mut self, buffer: &mut [u8]) {
407 match self {
408 JournalCipher::ChaCha20(c) => c.decrypt(buffer),
409 JournalCipher::Aes256Xts(c) => c.decrypt(buffer),
410 }
411 }
412
413 pub fn sequence_number(&self) -> u64 {
414 match self {
415 JournalCipher::ChaCha20(c) => c.offset(),
416 JournalCipher::Aes256Xts(c) => c.current_tweak(),
417 }
418 }
419
420 pub fn key_is_new(&self) -> bool {
422 match self {
423 JournalCipher::ChaCha20(c) => c.key_is_new(),
424 JournalCipher::Aes256Xts(c) => c.key_is_new(),
425 }
426 }
427}
428
429pub enum KeyPurpose {
432 Data,
434 Metadata,
436}
437
438impl TryFrom<fidl_fuchsia_fxfs::KeyPurpose> for KeyPurpose {
439 type Error = zx::Status;
440
441 fn try_from(purpose: fidl_fuchsia_fxfs::KeyPurpose) -> Result<Self, Self::Error> {
442 match purpose {
443 fidl_fuchsia_fxfs::KeyPurpose::Data => Ok(KeyPurpose::Data),
444 fidl_fuchsia_fxfs::KeyPurpose::Metadata => Ok(KeyPurpose::Metadata),
445 _ => Err(zx::Status::INVALID_ARGS),
446 }
447 }
448}
449
450pub enum WrappingKey {
453 Aes256GcmSiv([u8; 32]),
455 Fscrypt([u8; 64]),
457}
458impl From<[u8; 32]> for WrappingKey {
459 fn from(value: [u8; 32]) -> Self {
460 WrappingKey::Aes256GcmSiv(value)
461 }
462}
463impl From<[u8; 64]> for WrappingKey {
464 fn from(value: [u8; 64]) -> Self {
465 WrappingKey::Fscrypt(value)
466 }
467}
468
469#[async_trait]
477pub trait Crypt: Send + Sync {
478 async fn create_key(
483 &self,
484 owner: u64,
485 purpose: KeyPurpose,
486 ) -> Result<(FxfsKey, UnwrappedKey), zx::Status>;
487
488 async fn create_key_with_id(
493 &self,
494 owner: u64,
495 wrapping_key_id: WrappingKeyId,
496 object_type: ObjectType,
497 ) -> Result<(EncryptionKey, UnwrappedKey), zx::Status>;
498
499 async fn unwrap_key(
505 &self,
506 wrapped_key: &WrappedKey,
507 owner: u64,
508 ) -> Result<UnwrappedKey, zx::Status>;
509
510 async fn unwrap_keys(
515 &self,
516 keys: &[(u64, EncryptionKey)],
517 owner: u64,
518 ) -> Result<CipherSet, zx::Status> {
519 let futures: FuturesUnordered<_> = keys
520 .iter()
521 .map(|(key_id, key)| {
522 let key_id = *key_id;
523 let wrapped_key = WrappedKey::from(key.clone());
524 let owner = owner;
525 async move {
526 match self.unwrap_key(&wrapped_key, owner).await {
527 Ok(unwrapped_key) => cipher::key_to_cipher(key, &unwrapped_key)
528 .map(|c| (key_id, cipher::CipherHolder::Cipher(c))),
529 Err(zx::Status::UNAVAILABLE) => {
530 Ok((key_id, cipher::CipherHolder::Unavailable))
531 }
532 Err(e) => Err(e),
533 }
534 }
535 })
536 .collect();
537 let result = futures.try_collect::<BTreeMap<u64, _>>().await?;
538 Ok(result.into())
539 }
540}
541
542#[cfg(test)]
543mod tests {
544 use super::{JournalCipher, JournalXtsCipher, StreamCipher, UnwrappedKey};
545
546 #[test]
547 fn test_journal_xts_cipher_roundtrip() {
548 let key = UnwrappedKey::new(vec![0x42; 32]);
549 let mut enc = JournalXtsCipher::new(&key, 0);
550 let mut dec = JournalXtsCipher::new(&key, 0);
551
552 let buf_short = vec![1, 2, 3, 4, 5];
554 let mut enc_short = enc.encrypt(&buf_short);
555 assert_eq!(enc_short.len(), 16);
556 dec.decrypt(&mut enc_short);
557 assert_eq!(&enc_short[..5], &[1, 2, 3, 4, 5]);
558 assert_eq!(&enc_short[5..], &[0; 11]);
559 assert_eq!(enc.current_tweak(), 1);
560 assert_eq!(dec.current_tweak(), 1);
561
562 let buf_16 = vec![7u8; 16];
564 let mut enc_16 = enc.encrypt(&buf_16);
565 assert_eq!(enc_16.len(), 16);
566 dec.decrypt(&mut enc_16);
567 assert_eq!(enc_16, vec![7u8; 16]);
568 assert_eq!(enc.current_tweak(), 2);
569 assert_eq!(dec.current_tweak(), 2);
570
571 let buf_25: Vec<u8> = (0..25).collect();
573 let mut enc_25 = enc.encrypt(&buf_25);
574 assert_eq!(enc_25.len(), 25);
575 dec.decrypt(&mut enc_25);
576 assert_eq!(enc_25, (0..25).collect::<Vec<u8>>());
577 assert_eq!(enc.current_tweak(), 3);
578 assert_eq!(dec.current_tweak(), 3);
579 }
580
581 #[test]
582 fn test_journal_cipher_enum() {
583 let key = UnwrappedKey::new(vec![0x33; 32]);
584 let mut enc = JournalCipher::new_aes256_xts(&key, 0);
585 let mut dec = JournalCipher::new_aes256_xts(&key, 0);
586
587 let buf = vec![9u8; 20];
588 let mut enc_buf = enc.encrypt(&buf);
589 assert_eq!(enc_buf.len(), 20);
590 dec.decrypt(&mut enc_buf);
591 assert_eq!(enc_buf, vec![9u8; 20]);
592 assert_eq!(enc.sequence_number(), 1);
593 }
594
595 #[test]
596 fn test_stream_cipher_offset() {
597 let key = UnwrappedKey::new(vec![
598 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
599 25, 26, 27, 28, 29, 30, 31, 32,
600 ]);
601 let mut cipher1 = StreamCipher::new(&key, 0);
602 let mut p1 = [1, 2, 3, 4];
603 let mut c1 = p1.clone();
604 cipher1.encrypt(&mut c1);
605
606 let mut cipher2 = StreamCipher::new(&key, 1);
607 let p2 = [5, 6, 7, 8];
608 let mut c2 = p2.clone();
609 cipher2.encrypt(&mut c2);
610
611 let xor_fn = |buf1: &mut [u8], buf2| {
612 for (b1, b2) in buf1.iter_mut().zip(buf2) {
613 *b1 ^= b2;
614 }
615 };
616
617 xor_fn(&mut c1, &c2);
620 xor_fn(&mut p1, &p2);
621 assert_ne!(c1, p1);
622 }
623
624 #[test]
625 fn test_journal_xts_cipher_aligned_and_unaligned_allocations() {
626 let key = UnwrappedKey::new(vec![0x5a; 32]);
627 let test_lengths = [1, 5, 16, 25];
628 let backing_size: usize = test_lengths.iter().max().unwrap() + 1;
630 let mut backing_vector = vec![0u128; backing_size.div_ceil(16)];
632
633 for &len in &test_lengths {
634 let plaintext = vec![42u8; len];
635 let mut expected_plaintext = plaintext.clone();
636 if expected_plaintext.len() < 16 {
637 expected_plaintext.resize(16, 0);
638 }
639
640 {
642 let mut buf = std::mem::ManuallyDrop::new(unsafe {
643 Vec::<u8>::from_raw_parts(
644 backing_vector.as_mut_ptr().cast::<u8>(),
645 len,
646 backing_size,
647 )
648 });
649 assert!(buf.as_ptr().cast::<u128>().is_aligned());
650 buf.copy_from_slice(plaintext.as_slice());
651 let mut encrypted = {
652 let mut enc = JournalXtsCipher::new(&key, 13);
653 enc.encrypt(&buf)
654 };
655 {
656 let mut dec = JournalXtsCipher::new(&key, 13);
657 dec.decrypt(&mut encrypted);
658 }
659 assert_eq!(encrypted, expected_plaintext);
660 }
661
662 {
664 let mut buf = std::mem::ManuallyDrop::new(unsafe {
665 Vec::<u8>::from_raw_parts(
666 backing_vector.as_mut_ptr().cast::<u8>().wrapping_byte_add(1),
667 len,
668 backing_size,
669 )
670 });
671 assert!(!buf.as_ptr().cast::<u128>().is_aligned());
672 buf.copy_from_slice(plaintext.as_slice());
673 let mut encrypted = {
674 let mut enc = JournalXtsCipher::new(&key, 13);
675 enc.encrypt(&buf)
676 };
677 {
678 let mut dec = JournalXtsCipher::new(&key, 13);
679 dec.decrypt(&mut encrypted);
680 }
681 assert_eq!(encrypted, expected_plaintext);
682 }
683 }
684 }
685}