fxfs_crypto/
lib.rs

1// Copyright 2023 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
5use aes::cipher::{KeyIvInit, StreamCipher as _, StreamCipherSeek};
6use anyhow::anyhow;
7use async_trait::async_trait;
8use chacha20::{self, ChaCha20};
9use cipher::ToKeyType;
10use fprint::TypeFingerprint;
11use futures::TryStreamExt as _;
12use futures::stream::FuturesUnordered;
13use serde::de::{Error as SerdeError, Visitor};
14use serde::{Deserialize, Deserializer, Serialize, Serializer};
15use std::collections::BTreeMap;
16use zx_status as zx;
17
18mod cipher;
19pub mod ff1;
20
21pub use cipher::fscrypt_ino_lblk32::FscryptSoftwareInoLblk32FileCipher;
22pub use cipher::fxfs::FxfsCipher;
23pub use cipher::{Cipher, CipherHolder, CipherSet, FindKeyResult, KeyType, key_to_cipher};
24pub use fidl_fuchsia_fxfs::{
25    EmptyStruct, FscryptKeyIdentifier, FscryptKeyIdentifierAndNonce, ObjectType, WrappedKey,
26};
27
28pub use cipher::FSCRYPT_PADDING;
29pub const FXFS_KEY_SIZE: usize = 256 / 8;
30pub const FXFS_WRAPPED_KEY_SIZE: usize = FXFS_KEY_SIZE + 16;
31
32/// Essentially just a vector by another name to indicate that it holds unwrapped key material.
33/// The length of an unwrapped key depends on the type of key that is wrapped.
34#[derive(Debug)]
35pub struct UnwrappedKey(Vec<u8>);
36impl UnwrappedKey {
37    pub fn new(key: Vec<u8>) -> Self {
38        UnwrappedKey(key)
39    }
40}
41impl std::ops::Deref for UnwrappedKey {
42    type Target = Vec<u8>;
43    fn deref(&self) -> &Self::Target {
44        &self.0
45    }
46}
47
48/// A fixed length array of 48 bytes that holds an AES-256-GCM-SIV wrapped key.
49#[repr(transparent)]
50#[derive(Clone, Debug, PartialEq)]
51pub struct WrappedKeyBytes(pub [u8; FXFS_WRAPPED_KEY_SIZE]);
52impl Default for WrappedKeyBytes {
53    fn default() -> Self {
54        Self([0u8; FXFS_WRAPPED_KEY_SIZE])
55    }
56}
57impl TryFrom<Vec<u8>> for WrappedKeyBytes {
58    type Error = anyhow::Error;
59
60    fn try_from(buf: Vec<u8>) -> Result<Self, Self::Error> {
61        Ok(Self(buf.try_into().map_err(|_| anyhow!("wrapped key wrong length"))?))
62    }
63}
64impl From<[u8; FXFS_WRAPPED_KEY_SIZE]> for WrappedKeyBytes {
65    fn from(buf: [u8; FXFS_WRAPPED_KEY_SIZE]) -> Self {
66        Self(buf)
67    }
68}
69impl TypeFingerprint for WrappedKeyBytes {
70    fn fingerprint() -> String {
71        "WrappedKeyBytes".to_owned()
72    }
73}
74
75impl std::ops::Deref for WrappedKeyBytes {
76    type Target = [u8; FXFS_WRAPPED_KEY_SIZE];
77    fn deref(&self) -> &Self::Target {
78        &self.0
79    }
80}
81
82impl std::ops::DerefMut for WrappedKeyBytes {
83    fn deref_mut(&mut self) -> &mut Self::Target {
84        &mut self.0
85    }
86}
87
88// Because default impls of Serialize/Deserialize for [T; N] are only defined for N in 0..=32, we
89// have to define them ourselves.
90impl Serialize for WrappedKeyBytes {
91    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
92    where
93        S: Serializer,
94    {
95        serializer.serialize_bytes(&self[..])
96    }
97}
98
99impl<'de> Deserialize<'de> for WrappedKeyBytes {
100    fn deserialize<D>(deserializer: D) -> Result<WrappedKeyBytes, D::Error>
101    where
102        D: Deserializer<'de>,
103    {
104        struct WrappedKeyVisitor;
105
106        impl<'d> Visitor<'d> for WrappedKeyVisitor {
107            type Value = WrappedKeyBytes;
108
109            fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
110                formatter.write_str("Expected wrapped keys to be 48 bytes")
111            }
112
113            fn visit_bytes<E>(self, bytes: &[u8]) -> Result<WrappedKeyBytes, E>
114            where
115                E: SerdeError,
116            {
117                self.visit_byte_buf(bytes.to_vec())
118            }
119
120            fn visit_byte_buf<E>(self, bytes: Vec<u8>) -> Result<WrappedKeyBytes, E>
121            where
122                E: SerdeError,
123            {
124                let orig_len = bytes.len();
125                let bytes: [u8; FXFS_WRAPPED_KEY_SIZE] =
126                    bytes.try_into().map_err(|_| SerdeError::invalid_length(orig_len, &self))?;
127                Ok(WrappedKeyBytes::from(bytes))
128            }
129        }
130        deserializer.deserialize_byte_buf(WrappedKeyVisitor)
131    }
132}
133
134/// This specifies a single key to be used to encrypt/decrypt.
135#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TypeFingerprint)]
136pub enum EncryptionKey {
137    Fxfs(FxfsKey),
138    // NOTE: `key_identifier` can be thought of as the "name" of the key to use; it is not a
139    // per-file or per-directory key. It is similar to Fxfs's wrapping key ID, although it
140    // doesn't wrap anything. Files using the same `key_identifier` are encrypted using the
141    // same underlying key, with just differences in the tweak used. Directories also use the
142    // same underlying key, but some structures are further salted using the provided nonce.
143    FscryptInoLblk32File { key_identifier: [u8; 16] },
144    FscryptInoLblk32Dir { key_identifier: [u8; 16], nonce: [u8; 16] },
145}
146
147impl<'a> arbitrary::Arbitrary<'a> for EncryptionKey {
148    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
149        Ok(match u.int_in_range(0..=2)? {
150            0 => EncryptionKey::Fxfs(u.arbitrary()?),
151            1 => EncryptionKey::FscryptInoLblk32File { key_identifier: u.arbitrary()? },
152            2 => EncryptionKey::FscryptInoLblk32Dir {
153                key_identifier: u.arbitrary()?,
154                nonce: u.arbitrary()?,
155            },
156            _ => unreachable!(),
157        })
158    }
159}
160
161impl From<EncryptionKey> for WrappedKey {
162    fn from(value: EncryptionKey) -> Self {
163        match value {
164            EncryptionKey::Fxfs(key) => WrappedKey::Fxfs(key.into()),
165            EncryptionKey::FscryptInoLblk32File { key_identifier } => {
166                WrappedKey::FscryptInoLblk32File(FscryptKeyIdentifier { key_identifier })
167            }
168            EncryptionKey::FscryptInoLblk32Dir { key_identifier, nonce } => {
169                WrappedKey::FscryptInoLblk32Dir(FscryptKeyIdentifierAndNonce {
170                    key_identifier,
171                    nonce,
172                })
173            }
174        }
175    }
176}
177
178/// An Fxfs encryption key wrapped in AES-256-GCM-SIV and the associated wrapping key ID.
179/// This can be provided to Crypt::unwrap_key to obtain the unwrapped key.
180#[derive(Clone, Default, Debug, Serialize, Deserialize, TypeFingerprint, PartialEq)]
181pub struct FxfsKey {
182    /// The identifier of the wrapping key.  The identifier has meaning to whatever is doing the
183    /// unwrapping.
184    pub wrapping_key_id: WrappingKeyId,
185    /// AES 256 requires a 512 bit key, which is made of two 256 bit keys, one for the data and one
186    /// for the tweak.  It is safe to use the same 256 bit key for both (see
187    /// https://csrc.nist.gov/CSRC/media/Projects/Block-Cipher-Techniques/documents/BCM/Comments/XTS/follow-up_XTS_comments-Ball.pdf)
188    /// which is what we do here.  Since the key is wrapped with AES-GCM-SIV, there are an
189    /// additional 16 bytes paid per key (so the actual key material is 32 bytes once unwrapped).
190    pub key: WrappedKeyBytes,
191}
192
193pub type WrappingKeyId = [u8; 16];
194
195impl From<FxfsKey> for fidl_fuchsia_fxfs::FxfsKey {
196    fn from(value: FxfsKey) -> Self {
197        fidl_fuchsia_fxfs::FxfsKey {
198            wrapping_key_id: value.wrapping_key_id,
199            wrapped_key: value.key.0,
200        }
201    }
202}
203
204impl<'a> arbitrary::Arbitrary<'a> for FxfsKey {
205    fn arbitrary(_u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
206        // There doesn't seem to be much point to randomly generate crypto keys.
207        return Ok(FxfsKey::default());
208    }
209}
210
211/// A thin wrapper around a ChaCha20 stream cipher.  This will use a zero nonce. **NOTE**: Great
212/// care must be taken not to encrypt different plaintext with the same key and offset (even across
213/// multiple boots), so consider if this suits your purpose before using it.
214pub struct StreamCipher(ChaCha20);
215
216impl StreamCipher {
217    pub fn new(key: &UnwrappedKey, offset: u64) -> Self {
218        let mut cipher =
219            Self(ChaCha20::new(chacha20::Key::from_slice(key), /* nonce: */ &[0; 12].into()));
220        cipher.0.seek(offset);
221        cipher
222    }
223
224    pub fn encrypt(&mut self, buffer: &mut [u8]) {
225        fxfs_trace::duration!(c"StreamCipher::encrypt", "len" => buffer.len());
226        self.0.apply_keystream(buffer);
227    }
228
229    pub fn decrypt(&mut self, buffer: &mut [u8]) {
230        fxfs_trace::duration!(c"StreamCipher::decrypt", "len" => buffer.len());
231        self.0.apply_keystream(buffer);
232    }
233
234    pub fn offset(&self) -> u64 {
235        self.0.current_pos()
236    }
237}
238
239/// Different keys are used for metadata and data in order to make certain operations requiring a
240/// metadata key rotation (e.g. secure erase) more efficient.
241pub enum KeyPurpose {
242    /// The key will be used to wrap user data.
243    Data,
244    /// The key will be used to wrap internal metadata.
245    Metadata,
246}
247
248impl TryFrom<fidl_fuchsia_fxfs::KeyPurpose> for KeyPurpose {
249    type Error = zx::Status;
250
251    fn try_from(purpose: fidl_fuchsia_fxfs::KeyPurpose) -> Result<Self, Self::Error> {
252        match purpose {
253            fidl_fuchsia_fxfs::KeyPurpose::Data => Ok(KeyPurpose::Data),
254            fidl_fuchsia_fxfs::KeyPurpose::Metadata => Ok(KeyPurpose::Metadata),
255            _ => Err(zx::Status::INVALID_ARGS),
256        }
257    }
258}
259
260/// The `Crypt` trait below provides a mechanism to unwrap a key or set of keys.
261/// The wrapping keys can be one of these types.
262pub enum WrappingKey {
263    /// This is used for keys of the type WrappedKey::Fxfs.
264    Aes256GcmSiv([u8; 32]),
265    /// This is used for legacy fscrypt keys that use a 64-byte main key.
266    Fscrypt([u8; 64]),
267}
268impl From<[u8; 32]> for WrappingKey {
269    fn from(value: [u8; 32]) -> Self {
270        WrappingKey::Aes256GcmSiv(value)
271    }
272}
273impl From<[u8; 64]> for WrappingKey {
274    fn from(value: [u8; 64]) -> Self {
275        WrappingKey::Fscrypt(value)
276    }
277}
278
279/// The keys it unwraps can be wrapped with either Aes256GcmSiv (ideally) or using via
280/// legacy fscrypt master key + HKDF.
281
282/// An interface trait with the ability to wrap and unwrap encryption keys.
283///
284/// Note that existence of this trait does not imply that an object will **securely**
285/// wrap and unwrap keys; rather just that it presents an interface for wrapping operations.
286#[async_trait]
287pub trait Crypt: Send + Sync {
288    /// `owner` is intended to be used such that when the key is wrapped, it appears to be different
289    /// to that of the same key wrapped by a different owner.  In this way, keys can be shared
290    /// amongst different filesystem objects (e.g. for clones), but it is not possible to tell just
291    /// by looking at the wrapped keys.
292    async fn create_key(
293        &self,
294        owner: u64,
295        purpose: KeyPurpose,
296    ) -> Result<(FxfsKey, UnwrappedKey), zx::Status>;
297
298    /// `owner` is intended to be used such that when the key is wrapped, it appears to be different
299    /// to that of the same key wrapped by a different owner.  In this way, keys can be shared
300    /// amongst different filesystem objects (e.g. for clones), but it is not possible to tell just
301    /// by looking at the wrapped keys.
302    async fn create_key_with_id(
303        &self,
304        owner: u64,
305        wrapping_key_id: WrappingKeyId,
306        object_type: ObjectType,
307    ) -> Result<(EncryptionKey, UnwrappedKey), zx::Status>;
308
309    /// Unwraps a single key, returning a raw unwrapped key.
310    /// This method is generally only used with StreamCipher and FF1.
311    /// Returns `zx::Status::UNAVAILABLE` if the key is known but cannot be unwrapped (e.g. it is
312    /// locked).
313    /// Returns `zx::Status::NOT_FOUND` if the key is not known.
314    async fn unwrap_key(
315        &self,
316        wrapped_key: &WrappedKey,
317        owner: u64,
318    ) -> Result<UnwrappedKey, zx::Status>;
319
320    /// Unwraps object keys and stores the result as a CipherSet mapping key_id to:
321    ///   - Some(cipher) if unwrapping key was found or
322    ///   - None if unwrapping key was missing.
323    /// The cipher can be used directly to encrypt/decrypt data.
324    async fn unwrap_keys(
325        &self,
326        keys: &BTreeMap<u64, WrappedKey>,
327        owner: u64,
328    ) -> Result<CipherSet, zx::Status> {
329        let futures: FuturesUnordered<_> = keys
330            .iter()
331            .map(|(key_id, key)| {
332                let key_id = *key_id;
333                let owner = owner;
334                async move {
335                    let unwrapped_key = match self.unwrap_key(&key, owner).await {
336                        Ok(unwrapped_key) => unwrapped_key,
337                        Err(zx::Status::UNAVAILABLE) => {
338                            let key_type = key.to_key_type().ok_or(zx::Status::INTERNAL)?;
339                            return Ok((key_id, cipher::CipherHolder::Unavailable(key_type)));
340                        }
341                        Err(e) => return Err(e),
342                    };
343
344                    cipher::key_to_cipher(key, &unwrapped_key)
345                        .map(|c| (key_id, cipher::CipherHolder::Cipher(c)))
346                }
347            })
348            .collect();
349        let result = futures.try_collect::<BTreeMap<u64, _>>().await?;
350        Ok(result.into())
351    }
352}
353
354#[cfg(test)]
355mod tests {
356    use super::{StreamCipher, UnwrappedKey};
357
358    #[test]
359    fn test_stream_cipher_offset() {
360        let key = UnwrappedKey::new(vec![
361            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
362            25, 26, 27, 28, 29, 30, 31, 32,
363        ]);
364        let mut cipher1 = StreamCipher::new(&key, 0);
365        let mut p1 = [1, 2, 3, 4];
366        let mut c1 = p1.clone();
367        cipher1.encrypt(&mut c1);
368
369        let mut cipher2 = StreamCipher::new(&key, 1);
370        let p2 = [5, 6, 7, 8];
371        let mut c2 = p2.clone();
372        cipher2.encrypt(&mut c2);
373
374        let xor_fn = |buf1: &mut [u8], buf2| {
375            for (b1, b2) in buf1.iter_mut().zip(buf2) {
376                *b1 ^= b2;
377            }
378        };
379
380        // Check that c1 ^ c2 != p1 ^ p2 (which would be the case if the same offset was used for
381        // both ciphers).
382        xor_fn(&mut c1, &c2);
383        xor_fn(&mut p1, &p2);
384        assert_ne!(c1, p1);
385    }
386}