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