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