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::generic_array::GenericArray;
6use aes::cipher::inout::InOut;
7use aes::cipher::typenum::consts::U16;
8use aes::cipher::{
9    BlockBackend, BlockClosure, BlockDecrypt, BlockEncrypt, BlockSizeUser, KeyInit, KeyIvInit,
10    StreamCipher as _, StreamCipherSeek,
11};
12use aes::Aes256;
13use anyhow::{anyhow, Error};
14use async_trait::async_trait;
15use chacha20::{self, ChaCha20};
16use fprint::TypeFingerprint;
17use futures::stream::FuturesUnordered;
18use futures::TryStreamExt as _;
19use fxfs_macros::{migrate_nodefault, Migrate};
20use serde::de::{Error as SerdeError, Visitor};
21use serde::{Deserialize, Deserializer, Serialize, Serializer};
22use static_assertions::assert_cfg;
23use std::sync::Arc;
24use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
25use zx_status as zx;
26
27pub mod ff1;
28
29pub const KEY_SIZE: usize = 256 / 8;
30pub const WRAPPED_KEY_SIZE: usize = KEY_SIZE + 16;
31// TODO(https://fxbug.dev/375700939): Support different padding sizes based on SET_ENCRYPTION_POLICY
32// flags.
33pub const FSCRYPT_PADDING: usize = 16;
34
35// Fxfs will always use a block size >= 512 bytes, so we just assume a sector size of 512 bytes,
36// which will work fine even if a different block size is used by Fxfs or the underlying device.
37const SECTOR_SIZE: u64 = 512;
38
39pub type KeyBytes = [u8; KEY_SIZE];
40
41#[derive(Debug)]
42pub struct UnwrappedKey {
43    key: KeyBytes,
44}
45
46impl UnwrappedKey {
47    pub fn new(key: KeyBytes) -> Self {
48        UnwrappedKey { key }
49    }
50
51    pub fn key(&self) -> &KeyBytes {
52        &self.key
53    }
54}
55
56pub type UnwrappedKeys = Vec<(u64, Option<UnwrappedKey>)>;
57
58pub type WrappedKeyBytes = WrappedKeyBytesV32;
59
60#[repr(transparent)]
61#[derive(Clone, Debug, PartialEq)]
62pub struct WrappedKeyBytesV32(pub [u8; WRAPPED_KEY_SIZE]);
63
64impl Default for WrappedKeyBytes {
65    fn default() -> Self {
66        Self([0u8; WRAPPED_KEY_SIZE])
67    }
68}
69
70impl TryFrom<Vec<u8>> for WrappedKeyBytes {
71    type Error = anyhow::Error;
72
73    fn try_from(buf: Vec<u8>) -> Result<Self, Self::Error> {
74        Ok(Self(buf.try_into().map_err(|_| anyhow!("wrapped key wrong length"))?))
75    }
76}
77
78impl From<[u8; WRAPPED_KEY_SIZE]> for WrappedKeyBytes {
79    fn from(buf: [u8; WRAPPED_KEY_SIZE]) -> Self {
80        Self(buf)
81    }
82}
83
84impl TypeFingerprint for WrappedKeyBytes {
85    fn fingerprint() -> String {
86        "WrappedKeyBytes".to_owned()
87    }
88}
89
90impl std::ops::Deref for WrappedKeyBytes {
91    type Target = [u8; WRAPPED_KEY_SIZE];
92    fn deref(&self) -> &Self::Target {
93        &self.0
94    }
95}
96
97impl std::ops::DerefMut for WrappedKeyBytes {
98    fn deref_mut(&mut self) -> &mut Self::Target {
99        &mut self.0
100    }
101}
102
103// Because default impls of Serialize/Deserialize for [T; N] are only defined for N in 0..=32, we
104// have to define them ourselves.
105impl Serialize for WrappedKeyBytes {
106    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
107    where
108        S: Serializer,
109    {
110        serializer.serialize_bytes(&self[..])
111    }
112}
113
114impl<'de> Deserialize<'de> for WrappedKeyBytes {
115    fn deserialize<D>(deserializer: D) -> Result<WrappedKeyBytes, D::Error>
116    where
117        D: Deserializer<'de>,
118    {
119        struct WrappedKeyVisitor;
120
121        impl<'d> Visitor<'d> for WrappedKeyVisitor {
122            type Value = WrappedKeyBytes;
123
124            fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
125                formatter.write_str("Expected wrapped keys to be 48 bytes")
126            }
127
128            fn visit_bytes<E>(self, bytes: &[u8]) -> Result<WrappedKeyBytes, E>
129            where
130                E: SerdeError,
131            {
132                self.visit_byte_buf(bytes.to_vec())
133            }
134
135            fn visit_byte_buf<E>(self, bytes: Vec<u8>) -> Result<WrappedKeyBytes, E>
136            where
137                E: SerdeError,
138            {
139                let orig_len = bytes.len();
140                let bytes: [u8; WRAPPED_KEY_SIZE] =
141                    bytes.try_into().map_err(|_| SerdeError::invalid_length(orig_len, &self))?;
142                Ok(WrappedKeyBytes::from(bytes))
143            }
144        }
145        deserializer.deserialize_byte_buf(WrappedKeyVisitor)
146    }
147}
148
149pub type WrappedKey = WrappedKeyV40;
150
151#[derive(Clone, Debug, Serialize, Deserialize, TypeFingerprint, PartialEq)]
152pub struct WrappedKeyV40 {
153    /// The identifier of the wrapping key.  The identifier has meaning to whatever is doing the
154    /// unwrapping.
155    pub wrapping_key_id: u128,
156    /// AES 256 requires a 512 bit key, which is made of two 256 bit keys, one for the data and one
157    /// for the tweak.  It is safe to use the same 256 bit key for both (see
158    /// https://csrc.nist.gov/CSRC/media/Projects/Block-Cipher-Techniques/documents/BCM/Comments/XTS/follow-up_XTS_comments-Ball.pdf)
159    /// which is what we do here.  Since the key is wrapped with AES-GCM-SIV, there are an
160    /// additional 16 bytes paid per key (so the actual key material is 32 bytes once unwrapped).
161    pub key: WrappedKeyBytesV32,
162}
163
164#[derive(Default, Clone, Migrate, Debug, Serialize, Deserialize, TypeFingerprint)]
165#[migrate_nodefault]
166pub struct WrappedKeyV32 {
167    pub wrapping_key_id: u64,
168    pub key: WrappedKeyBytesV32,
169}
170
171/// To support key rolling and clones, a file can have more than one key.  Each key has an ID that
172/// unique to the file.
173pub type WrappedKeys = WrappedKeysV40;
174
175#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, TypeFingerprint)]
176pub struct WrappedKeysV40(Vec<(u64, WrappedKeyV40)>);
177
178impl From<WrappedKeysV32> for WrappedKeysV40 {
179    fn from(value: WrappedKeysV32) -> Self {
180        Self(value.0.into_iter().map(|(id, key)| (id, key.into())).collect())
181    }
182}
183#[derive(Clone, Debug, Serialize, Deserialize, TypeFingerprint)]
184pub struct WrappedKeysV32(pub Vec<(u64, WrappedKeyV32)>);
185
186impl From<Vec<(u64, WrappedKey)>> for WrappedKeys {
187    fn from(buf: Vec<(u64, WrappedKey)>) -> Self {
188        Self(buf)
189    }
190}
191
192impl std::ops::Deref for WrappedKeys {
193    type Target = Vec<(u64, WrappedKey)>;
194    fn deref(&self) -> &Self::Target {
195        &self.0
196    }
197}
198
199impl std::ops::DerefMut for WrappedKeys {
200    fn deref_mut(&mut self) -> &mut Self::Target {
201        &mut self.0
202    }
203}
204
205impl WrappedKeys {
206    pub fn get_wrapping_key_with_id(&self, key_id: u64) -> Option<[u8; 16]> {
207        let wrapped_key_entry = self.0.iter().find(|(x, _)| *x == key_id);
208        wrapped_key_entry.map(|(_, wrapped_key)| wrapped_key.wrapping_key_id.to_le_bytes())
209    }
210}
211
212#[derive(Clone, Debug)]
213pub struct Cipher {
214    id: u64,
215    // This is None if the key isn't present.
216    cipher: Option<Aes256>,
217}
218
219impl Cipher {
220    pub fn new(id: u64, key: &UnwrappedKey) -> Self {
221        Self { id, cipher: Some(Aes256::new(GenericArray::from_slice(key.key()))) }
222    }
223
224    pub fn unavailable(id: u64) -> Self {
225        Cipher { id, cipher: None }
226    }
227
228    pub fn key(&self) -> Option<&Aes256> {
229        self.cipher.as_ref()
230    }
231}
232
233/// References a specific key in the cipher set.
234pub struct Key {
235    keys: Arc<CipherSet>,
236    // Index in the CipherSet array for the key.
237    index: usize,
238}
239
240impl Key {
241    fn key(&self) -> &Aes256 {
242        self.keys.0[self.index].cipher.as_ref().unwrap()
243    }
244
245    pub fn key_id(&self) -> u64 {
246        self.keys.0[self.index].id
247    }
248
249    /// Encrypts data in the `buffer`.
250    ///
251    /// * `offset` is the byte offset within the file.
252    /// * `buffer` is mutated in place.
253    ///
254    /// `buffer` *must* be 16 byte aligned.
255    pub fn encrypt(&self, offset: u64, buffer: &mut [u8]) -> Result<(), Error> {
256        fxfs_trace::duration!(c"encrypt", "len" => buffer.len());
257        assert_eq!(offset % SECTOR_SIZE, 0);
258        let cipher = &self.key();
259        let mut sector_offset = offset / SECTOR_SIZE;
260        for sector in buffer.chunks_exact_mut(SECTOR_SIZE as usize) {
261            let mut tweak = Tweak(sector_offset as u128);
262            // The same key is used for encrypting the data and computing the tweak.
263            cipher.encrypt_block(GenericArray::from_mut_slice(tweak.as_mut_bytes()));
264            cipher.encrypt_with_backend(XtsProcessor::new(tweak, sector));
265            sector_offset += 1;
266        }
267        Ok(())
268    }
269
270    /// Decrypt the data in `buffer`.
271    ///
272    /// * `offset` is the byte offset within the file.
273    /// * `buffer` is mutated in place.
274    ///
275    /// `buffer` *must* be 16 byte aligned.
276    pub fn decrypt(&self, offset: u64, buffer: &mut [u8]) -> Result<(), Error> {
277        fxfs_trace::duration!(c"decrypt", "len" => buffer.len());
278        assert_eq!(offset % SECTOR_SIZE, 0);
279        let cipher = &self.key();
280        let mut sector_offset = offset / SECTOR_SIZE;
281        for sector in buffer.chunks_exact_mut(SECTOR_SIZE as usize) {
282            let mut tweak = Tweak(sector_offset as u128);
283            // The same key is used for encrypting the data and computing the tweak.
284            cipher.encrypt_block(GenericArray::from_mut_slice(tweak.as_mut_bytes()));
285            cipher.decrypt_with_backend(XtsProcessor::new(tweak, sector));
286            sector_offset += 1;
287        }
288        Ok(())
289    }
290
291    /// Encrypts the filename contained in `buffer`.
292    pub fn encrypt_filename(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error> {
293        // Pad the buffer such that its length is a multiple of FSCRYPT_PADDING.
294        buffer.resize(buffer.len().next_multiple_of(FSCRYPT_PADDING), 0);
295        let cipher = self.key();
296        cipher.encrypt_with_backend(CbcEncryptProcessor::new(Tweak(object_id as u128), buffer));
297        Ok(())
298    }
299
300    /// Decrypts the filename contained in `buffer`.
301    pub fn decrypt_filename(&self, object_id: u64, buffer: &mut Vec<u8>) -> Result<(), Error> {
302        let cipher = self.key();
303        cipher.decrypt_with_backend(CbcDecryptProcessor::new(Tweak(object_id as u128), buffer));
304        // Remove the padding
305        if let Some(i) = buffer.iter().rposition(|x| *x != 0) {
306            let new_len = i + 1;
307            buffer.truncate(new_len);
308        }
309        Ok(())
310    }
311}
312
313#[derive(Clone, Debug)]
314pub struct CipherSet(Vec<Cipher>);
315
316impl From<Vec<Cipher>> for CipherSet {
317    fn from(value: Vec<Cipher>) -> Self {
318        Self(value)
319    }
320}
321
322impl CipherSet {
323    pub fn new(keys: &UnwrappedKeys) -> Self {
324        Self(
325            keys.iter()
326                .map(|(id, k)| match k {
327                    Some(k) => Cipher::new(*id, k),
328                    None => Cipher::unavailable(*id),
329                })
330                .collect(),
331        )
332    }
333
334    pub fn ciphers(&self) -> &[Cipher] {
335        &self.0
336    }
337
338    pub fn cipher(&self, id: u64) -> Option<(usize, &Cipher)> {
339        self.0.iter().enumerate().find(|(_, x)| x.id == id)
340    }
341
342    pub fn contains_key_id(&self, id: u64) -> bool {
343        self.0.iter().find(|x| x.id == id).is_some()
344    }
345
346    pub fn find_key(self: &Arc<Self>, id: u64) -> FindKeyResult {
347        let Some((index, cipher)) = self.0.iter().enumerate().find(|(_, x)| x.id == id) else {
348            return FindKeyResult::NotFound;
349        };
350        if cipher.key().is_some() {
351            FindKeyResult::Key(Key { keys: self.clone(), index })
352        } else {
353            FindKeyResult::Unavailable
354        }
355    }
356}
357
358pub enum FindKeyResult {
359    NotFound,
360    Unavailable,
361    Key(Key),
362}
363
364/// A thin wrapper around a ChaCha20 stream cipher.  This will use a zero nonce. **NOTE**: Great
365/// care must be taken not to encrypt different plaintext with the same key and offset (even across
366/// multiple boots), so consider if this suits your purpose before using it.
367pub struct StreamCipher(ChaCha20);
368
369impl StreamCipher {
370    pub fn new(key: &UnwrappedKey, offset: u64) -> Self {
371        let mut cipher = Self(ChaCha20::new(
372            chacha20::Key::from_slice(&key.key),
373            /* nonce: */ &[0; 12].into(),
374        ));
375        cipher.0.seek(offset);
376        cipher
377    }
378
379    pub fn encrypt(&mut self, buffer: &mut [u8]) {
380        fxfs_trace::duration!(c"StreamCipher::encrypt", "len" => buffer.len());
381        self.0.apply_keystream(buffer);
382    }
383
384    pub fn decrypt(&mut self, buffer: &mut [u8]) {
385        fxfs_trace::duration!(c"StreamCipher::decrypt", "len" => buffer.len());
386        self.0.apply_keystream(buffer);
387    }
388
389    pub fn offset(&self) -> u64 {
390        self.0.current_pos()
391    }
392}
393
394/// Different keys are used for metadata and data in order to make certain operations requiring a
395/// metadata key rotation (e.g. secure erase) more efficient.
396pub enum KeyPurpose {
397    /// The key will be used to wrap user data.
398    Data,
399    /// The key will be used to wrap internal metadata.
400    Metadata,
401}
402
403/// An interface trait with the ability to wrap and unwrap encryption keys.
404///
405/// Note that existence of this trait does not imply that an object will **securely**
406/// wrap and unwrap keys; rather just that it presents an interface for wrapping operations.
407#[async_trait]
408pub trait Crypt: Send + Sync {
409    /// `owner` is intended to be used such that when the key is wrapped, it appears to be different
410    /// to that of the same key wrapped by a different owner.  In this way, keys can be shared
411    /// amongst different filesystem objects (e.g. for clones), but it is not possible to tell just
412    /// by looking at the wrapped keys.
413    async fn create_key(
414        &self,
415        owner: u64,
416        purpose: KeyPurpose,
417    ) -> Result<(WrappedKey, UnwrappedKey), zx::Status>;
418
419    /// `owner` is intended to be used such that when the key is wrapped, it appears to be different
420    /// to that of the same key wrapped by a different owner.  In this way, keys can be shared
421    /// amongst different filesystem objects (e.g. for clones), but it is not possible to tell just
422    /// by looking at the wrapped keys.
423    async fn create_key_with_id(
424        &self,
425        owner: u64,
426        wrapping_key_id: u128,
427    ) -> Result<(WrappedKey, UnwrappedKey), zx::Status>;
428
429    // Unwraps a single key.
430    async fn unwrap_key(
431        &self,
432        wrapped_key: &WrappedKey,
433        owner: u64,
434    ) -> Result<UnwrappedKey, zx::Status>;
435
436    /// Unwraps the keys and stores the result in UnwrappedKeys.
437    async fn unwrap_keys(
438        &self,
439        keys: &WrappedKeys,
440        owner: u64,
441    ) -> Result<UnwrappedKeys, zx::Status> {
442        let futures = FuturesUnordered::new();
443        for (key_id, key) in keys.iter() {
444            futures.push(async move {
445                match self.unwrap_key(key, owner).await {
446                    Ok(unwrapped_key) => Ok((*key_id, Some(unwrapped_key))),
447                    Err(zx::Status::NOT_FOUND) => Ok((*key_id, None)),
448                    Err(e) => Err(e),
449                }
450            });
451        }
452        Ok(futures.try_collect::<UnwrappedKeys>().await?)
453    }
454}
455
456// This assumes little-endianness which is likely to always be the case.
457assert_cfg!(target_endian = "little");
458#[derive(IntoBytes, KnownLayout, FromBytes, Immutable)]
459#[repr(C)]
460struct Tweak(u128);
461
462pub fn xor_in_place(a: &mut [u8], b: &[u8]) {
463    for (b1, b2) in a.iter_mut().zip(b.iter()) {
464        *b1 ^= *b2;
465    }
466}
467
468// To be used with encrypt_with_backend.
469struct CbcEncryptProcessor<'a> {
470    tweak: Tweak,
471    data: &'a mut [u8],
472}
473
474impl<'a> CbcEncryptProcessor<'a> {
475    fn new(tweak: Tweak, data: &'a mut [u8]) -> Self {
476        Self { tweak, data }
477    }
478}
479
480impl BlockSizeUser for CbcEncryptProcessor<'_> {
481    type BlockSize = U16;
482}
483
484impl BlockClosure for CbcEncryptProcessor<'_> {
485    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
486        let Self { mut tweak, data } = self;
487        for block in data.chunks_exact_mut(16) {
488            xor_in_place(block, &tweak.0.to_le_bytes());
489            let chunk: &mut GenericArray<u8, _> = GenericArray::from_mut_slice(block);
490            backend.proc_block(InOut::from(chunk));
491            tweak.0 = u128::from_le_bytes(block.try_into().unwrap())
492        }
493    }
494}
495
496// To be used with decrypt_with_backend.
497struct CbcDecryptProcessor<'a> {
498    tweak: Tweak,
499    data: &'a mut [u8],
500}
501
502impl<'a> CbcDecryptProcessor<'a> {
503    fn new(tweak: Tweak, data: &'a mut [u8]) -> Self {
504        Self { tweak, data }
505    }
506}
507
508impl BlockSizeUser for CbcDecryptProcessor<'_> {
509    type BlockSize = U16;
510}
511
512impl BlockClosure for CbcDecryptProcessor<'_> {
513    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
514        let Self { mut tweak, data } = self;
515        for block in data.chunks_exact_mut(16) {
516            let ciphertext = block.to_vec();
517            let chunk = GenericArray::from_mut_slice(block);
518            backend.proc_block(InOut::from(chunk));
519            xor_in_place(block, &tweak.0.to_le_bytes());
520            tweak.0 = u128::from_le_bytes(ciphertext.try_into().unwrap());
521        }
522    }
523}
524
525// To be used with encrypt|decrypt_with_backend.
526struct XtsProcessor<'a> {
527    tweak: Tweak,
528    data: &'a mut [u8],
529}
530
531impl<'a> XtsProcessor<'a> {
532    // `tweak` should be encrypted.  `data` should be a single sector and *must* be 16 byte aligned.
533    fn new(tweak: Tweak, data: &'a mut [u8]) -> Self {
534        assert_eq!(data.as_ptr() as usize & 15, 0, "data must be 16 byte aligned");
535        Self { tweak, data }
536    }
537}
538
539impl BlockSizeUser for XtsProcessor<'_> {
540    type BlockSize = U16;
541}
542
543impl BlockClosure for XtsProcessor<'_> {
544    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
545        let Self { mut tweak, data } = self;
546        for chunk in data.chunks_exact_mut(16) {
547            let ptr = chunk.as_mut_ptr() as *mut u128;
548            // SAFETY: We know each chunk is exactly 16 bytes and it should be safe to transmute to
549            // u128 and GenericArray<u8, U16>.  There are safe ways of doing the following, but this
550            // is extremely performance sensitive, and even seemingly innocuous changes here can
551            // have an order-of-magnitude impact on what the compiler produces and that can be seen
552            // in our benchmarks.  This assumes little-endianness which is likely to always be the
553            // case.
554            unsafe {
555                *ptr ^= tweak.0;
556                let chunk = ptr as *mut GenericArray<u8, U16>;
557                backend.proc_block(InOut::from_raw(chunk, chunk));
558                *ptr ^= tweak.0;
559            }
560            tweak.0 = (tweak.0 << 1) ^ ((tweak.0 as i128 >> 127) as u128 & 0x87);
561        }
562    }
563}
564
565#[cfg(test)]
566mod tests {
567    use crate::{Cipher, CipherSet, Key};
568
569    use super::{StreamCipher, UnwrappedKey};
570
571    #[test]
572    fn test_stream_cipher_offset() {
573        let key = UnwrappedKey::new([
574            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
575            25, 26, 27, 28, 29, 30, 31, 32,
576        ]);
577        let mut cipher1 = StreamCipher::new(&key, 0);
578        let mut p1 = [1, 2, 3, 4];
579        let mut c1 = p1.clone();
580        cipher1.encrypt(&mut c1);
581
582        let mut cipher2 = StreamCipher::new(&key, 1);
583        let p2 = [5, 6, 7, 8];
584        let mut c2 = p2.clone();
585        cipher2.encrypt(&mut c2);
586
587        let xor_fn = |buf1: &mut [u8], buf2| {
588            for (b1, b2) in buf1.iter_mut().zip(buf2) {
589                *b1 ^= b2;
590            }
591        };
592
593        // Check that c1 ^ c2 != p1 ^ p2 (which would be the case if the same offset was used for
594        // both ciphers).
595        xor_fn(&mut c1, &c2);
596        xor_fn(&mut p1, &p2);
597        assert_ne!(c1, p1);
598    }
599
600    /// Output produced via:
601    /// echo -n filename > in.txt ; truncate -s 16 in.txt
602    /// openssl aes-256-cbc -e -iv 02000000000000000000000000000000 -nosalt -K 1fcdf30b7d191bd95d3161fe08513b864aa15f27f910f1c66eec8cfa93e9893b -in in.txt -out out.txt -nopad
603    /// hexdump out.txt -e "16/1 \"%02x\" \"\n\"" -v
604    #[test]
605    fn test_encrypt_filename() {
606        let raw_key_hex = "1fcdf30b7d191bd95d3161fe08513b864aa15f27f910f1c66eec8cfa93e9893b";
607        let raw_key_bytes: [u8; 32] =
608            hex::decode(raw_key_hex).expect("decode failed").try_into().unwrap();
609        let unwrapped_key = UnwrappedKey::new(raw_key_bytes);
610        let cipher_set = CipherSet::from(vec![Cipher::new(0, &unwrapped_key)]);
611        let key = Key { keys: std::sync::Arc::new(cipher_set), index: 0 };
612        let object_id = 2;
613        let mut text = "filename".to_string().as_bytes().to_vec();
614        key.encrypt_filename(object_id, &mut text).expect("encrypt filename failed");
615        assert_eq!(text, hex::decode("52d56369103a39b3ea1e09c85dd51546").expect("decode failed"));
616    }
617
618    /// Output produced via:
619    /// openssl aes-256-cbc -d -iv 02000000000000000000000000000000 -nosalt -K 1fcdf30b7d191bd95d3161fe08513b864aa15f27f910f1c66eec8cfa93e9893b -in out.txt -out in.txt
620    /// cat in.txt
621    #[test]
622    fn test_decrypt_filename() {
623        let raw_key_hex = "1fcdf30b7d191bd95d3161fe08513b864aa15f27f910f1c66eec8cfa93e9893b";
624        let raw_key_bytes: [u8; 32] =
625            hex::decode(raw_key_hex).expect("decode failed").try_into().unwrap();
626        let unwrapped_key = UnwrappedKey::new(raw_key_bytes);
627        let cipher_set = CipherSet::from(vec![Cipher::new(0, &unwrapped_key)]);
628        let key = Key { keys: std::sync::Arc::new(cipher_set), index: 0 };
629        let object_id = 2;
630        let mut text = hex::decode("52d56369103a39b3ea1e09c85dd51546").expect("decode failed");
631        key.decrypt_filename(object_id, &mut text).expect("encrypt filename failed");
632        assert_eq!(text, "filename".to_string().as_bytes().to_vec());
633    }
634}