diff --git a/src/lib.rs b/src/lib.rs index 0679ad57..dce8be92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,6 +96,13 @@ mod test { tests::encrypt::test_encrypt_by_password(); } + #[ignore] + #[test] + #[cfg(cryptsetup24supported)] + fn test_reencrypt_by_password() { + tests::reencrypt::test_reencrypt_by_password(); + } + #[ignore] #[test] fn test_encrypt_by_keyfile() { diff --git a/src/tests/encrypt.rs b/src/tests/encrypt.rs index a25a0dfb..c492f6a4 100644 --- a/src/tests/encrypt.rs +++ b/src/tests/encrypt.rs @@ -29,53 +29,66 @@ use rand::random; /// and unencrypted devices. const WINDOW_SIZE: usize = 1024 * 1024; -fn init(dev_path: &Path, passphrase: &str) -> Result { - let mut dev = CryptInit::init(dev_path)?; - dev.context_handle().format::<()>( - EncryptionFormat::Luks2, - ("aes", "xts-plain"), - None, - Either::Right(512 / 8), - None, - )?; +fn init(dev_path: &Path, passphrase: &str) -> c_uint { + let mut dev = CryptInit::init(dev_path).unwrap(); + dev.context_handle() + .format::<()>( + EncryptionFormat::Luks2, + ("aes", "xts-plain"), + None, + Either::Right(512 / 8), + None, + ) + .unwrap(); dev.keyslot_handle() .add_by_key(None, None, passphrase.as_bytes(), CryptVolumeKey::empty()) + .unwrap() } /// This method initializes the device with no encryption as a way to test /// that the plaintext can be read vs. the plaintext not being found due to /// proper encryption in the other tests. -fn init_null_cipher(dev_path: &Path) -> Result { - let mut dev = CryptInit::init(dev_path)?; - dev.context_handle().format::<()>( - EncryptionFormat::Luks1, - ("cipher_null", "ecb"), - None, - Either::Right(32), - None, - )?; - dev.keyslot_handle().add_by_passphrase(None, b"", b"") +fn init_null_cipher(dev_path: &Path) -> c_uint { + let mut dev = CryptInit::init(dev_path).unwrap(); + dev.context_handle() + .format::<()>( + EncryptionFormat::Luks1, + ("cipher_null", "ecb"), + None, + Either::Right(32), + None, + ) + .unwrap(); + dev.keyslot_handle() + .add_by_passphrase(None, b"", b"") + .unwrap() } -fn init_by_keyfile(dev_path: &Path, keyfile_path: &Path) -> Result { - let mut dev = CryptInit::init(dev_path)?; - dev.context_handle().format::<()>( - EncryptionFormat::Luks2, - ("aes", "xts-plain"), - None, - Either::Right(512 / 8), - None, - )?; +fn init_by_keyfile(dev_path: &Path, keyfile_path: &Path) -> c_uint { + let mut dev = CryptInit::init(dev_path).unwrap(); + dev.context_handle() + .format::<()>( + EncryptionFormat::Luks2, + ("aes", "xts-plain"), + None, + Either::Right(512 / 8), + None, + ) + .unwrap(); let keyfile_contents = { let mut kf_handle = dev.keyfile_handle(); - kf_handle.device_read(keyfile_path, 0, None, CryptKeyfile::empty())? + kf_handle + .device_read(keyfile_path, 0, None, CryptKeyfile::empty()) + .unwrap() }; - dev.keyslot_handle().add_by_key( - None, - None, - keyfile_contents.as_ref(), - CryptVolumeKey::empty(), - ) + dev.keyslot_handle() + .add_by_key( + None, + None, + keyfile_contents.as_ref(), + CryptVolumeKey::empty(), + ) + .unwrap() } fn activate_without_explicit_format( @@ -83,16 +96,17 @@ fn activate_without_explicit_format( device_name: &'static str, keyslot: c_uint, passphrase: &'static str, -) -> Result<(), LibcryptErr> { - let mut dev = CryptInit::init(dev_path)?; - dev.context_handle().load::<()>(None, None)?; - dev.activate_handle().activate_by_passphrase( - Some(device_name), - Some(keyslot), - passphrase.as_bytes(), - CryptActivate::empty(), - )?; - Ok(()) +) { + let mut dev = CryptInit::init(dev_path).unwrap(); + dev.context_handle().load::<()>(None, None).unwrap(); + dev.activate_handle() + .activate_by_passphrase( + Some(device_name), + Some(keyslot), + passphrase.as_bytes(), + CryptActivate::empty(), + ) + .unwrap(); } fn activate_by_passphrase( @@ -100,25 +114,27 @@ fn activate_by_passphrase( device_name: &'static str, keyslot: c_uint, passphrase: &'static str, -) -> Result<(), LibcryptErr> { - let mut dev = CryptInit::init(dev_path)?; +) { + let mut dev = CryptInit::init(dev_path).unwrap(); dev.context_handle() - .load::<()>(Some(EncryptionFormat::Luks2), None)?; - dev.activate_handle().activate_by_passphrase( - Some(device_name), - Some(keyslot), - passphrase.as_bytes(), - CryptActivate::empty(), - )?; - Ok(()) + .load::<()>(Some(EncryptionFormat::Luks2), None) + .unwrap(); + dev.activate_handle() + .activate_by_passphrase( + Some(device_name), + Some(keyslot), + passphrase.as_bytes(), + CryptActivate::empty(), + ) + .unwrap(); } -fn create_keyfile(loopback_file_path: &Path) -> Result { +fn create_keyfile(loopback_file_path: &Path) -> PathBuf { let path = PathBuf::from(format!("{}-key", loopback_file_path.display())); - let mut f = File::create(&path).map_err(LibcryptErr::IOError)?; + let mut f = File::create(&path).unwrap(); let random: Vec<_> = (0..4096).map(|_| random::()).collect(); - f.write(&random).map_err(LibcryptErr::IOError)?; - Ok(path) + f.write(&random).unwrap(); + path } fn activate_by_keyfile( @@ -127,31 +143,29 @@ fn activate_by_keyfile( keyslot: c_uint, keyfile_path: &Path, keyfile_size: Option, -) -> Result<(), LibcryptErr> { - let mut dev = CryptInit::init(dev_path)?; +) { + let mut dev = CryptInit::init(dev_path).unwrap(); dev.context_handle() - .load::<()>(Some(EncryptionFormat::Luks2), None)?; - dev.activate_handle().activate_by_keyfile_device_offset( - Some(device_name), - Some(keyslot), - keyfile_path, - keyfile_size, - 0, - CryptActivate::empty(), - )?; - Ok(()) + .load::<()>(Some(EncryptionFormat::Luks2), None) + .unwrap(); + dev.activate_handle() + .activate_by_keyfile_device_offset( + Some(device_name), + Some(keyslot), + keyfile_path, + keyfile_size, + 0, + CryptActivate::empty(), + ) + .unwrap(); } -fn activate_null_cipher(dev_path: &Path, device_name: &'static str) -> Result<(), LibcryptErr> { - let mut dev = CryptInit::init(dev_path)?; - dev.context_handle().load::<()>(None, None)?; - dev.activate_handle().activate_by_passphrase( - Some(device_name), - None, - b"", - CryptActivate::empty(), - )?; - Ok(()) +fn activate_null_cipher(dev_path: &Path, device_name: &'static str) { + let mut dev = CryptInit::init(dev_path).unwrap(); + dev.context_handle().load::<()>(None, None).unwrap(); + dev.activate_handle() + .activate_by_passphrase(Some(device_name), None, b"", CryptActivate::empty()) + .unwrap(); } fn write_random(device_name: &str) -> Result, io::Error> { @@ -238,16 +252,13 @@ pub fn test_encrypt_by_password() { let device_name = "test-device"; let passphrase = "abadpassphrase"; - let keyslot = init(dev_path, passphrase)?; - activate_by_passphrase(dev_path, device_name, keyslot, passphrase)?; - if run_plaintext_test(file_path, device_name)? { - return Err(LibcryptErr::Other("Should not find plaintext".to_string())); + let keyslot = init(dev_path, passphrase); + activate_by_passphrase(dev_path, device_name, keyslot, passphrase); + if run_plaintext_test(file_path, device_name).unwrap() { + panic!("Should not find plaintext"); } - - Ok(()) }, ) - .expect("Should succeed"); } pub fn test_encrypt_by_keyfile() { @@ -258,17 +269,14 @@ pub fn test_encrypt_by_keyfile() { |dev_path, file_path| { let device_name = "test-device"; - let keyfile_path = create_keyfile(file_path)?; - let keyslot = init_by_keyfile(dev_path, keyfile_path.as_path())?; - activate_by_keyfile(dev_path, device_name, keyslot, keyfile_path.as_path(), None)?; - if run_plaintext_test(file_path, device_name)? { - return Err(LibcryptErr::Other("Should not find plaintext".to_string())); + let keyfile_path = create_keyfile(file_path); + let keyslot = init_by_keyfile(dev_path, keyfile_path.as_path()); + activate_by_keyfile(dev_path, device_name, keyslot, keyfile_path.as_path(), None); + if run_plaintext_test(file_path, device_name).unwrap() { + panic!("Should not find plaintext"); } - - Ok(()) }, ) - .expect("Should succeed"); } pub fn test_encrypt_by_password_without_explicit_format() { @@ -280,16 +288,13 @@ pub fn test_encrypt_by_password_without_explicit_format() { let device_name = "test-device"; let passphrase = "abadpassphrase"; - let keyslot = init(dev_path, passphrase)?; - activate_without_explicit_format(dev_path, device_name, keyslot, passphrase)?; - if run_plaintext_test(file_path, device_name)? { - return Err(LibcryptErr::Other("Should not find plaintext".to_string())); + let keyslot = init(dev_path, passphrase); + activate_without_explicit_format(dev_path, device_name, keyslot, passphrase); + if run_plaintext_test(file_path, device_name).unwrap() { + panic!("Should not find plaintext"); } - - Ok(()) }, ) - .expect("Should succeed"); } pub fn test_unencrypted() { @@ -300,14 +305,11 @@ pub fn test_unencrypted() { |dev_path, file_path| { let device_name = "test-device"; - init_null_cipher(dev_path)?; - activate_null_cipher(dev_path, device_name)?; - if !run_plaintext_test(file_path, device_name)? { - return Err(LibcryptErr::Other("Should find plaintext".to_string())); + init_null_cipher(dev_path); + activate_null_cipher(dev_path, device_name); + if !run_plaintext_test(file_path, device_name).unwrap() { + panic!("Should find plaintext"); } - - Ok(()) }, ) - .expect("Should succeed"); } diff --git a/src/tests/keyfile.rs b/src/tests/keyfile.rs index d7f93fab..9123660b 100644 --- a/src/tests/keyfile.rs +++ b/src/tests/keyfile.rs @@ -2,7 +2,7 @@ use std::{env, fs::File, io::Write, path::PathBuf}; use super::loopback; -use crate::{consts::flags::CryptKeyfile, CryptInit, LibcryptErr}; +use crate::{consts::flags::CryptKeyfile, CryptInit}; pub fn test_keyfile_cleanup() { loopback::use_loopback( @@ -10,20 +10,19 @@ pub fn test_keyfile_cleanup() { super::format_with_zeros(), super::do_cleanup(), |dev_path, _file_path| { - let mut device = CryptInit::init(dev_path)?; + let mut device = CryptInit::init(dev_path).unwrap(); let mut key_path = PathBuf::from(env::var("TEST_DIR").unwrap_or_else(|_| "/tmp".to_string())); key_path.push("safe-free-test-keyfile"); - let mut f = File::create(&key_path).map_err(LibcryptErr::IOError)?; - f.write(b"this is a test password") - .map_err(LibcryptErr::IOError)?; + let mut f = File::create(&key_path).unwrap(); + f.write(b"this is a test password").unwrap(); let keyfile_contents = device .keyfile_handle() .device_read(&key_path, 0, None, CryptKeyfile::empty()); - std::fs::remove_file(&key_path).map_err(LibcryptErr::IOError)?; + std::fs::remove_file(&key_path).unwrap(); let (keyfile_ptr, keyfile_len) = { - let keyfile_contents = keyfile_contents?; + let keyfile_contents = keyfile_contents.unwrap(); let keyfile_ref = keyfile_contents.as_ref(); assert_eq!(keyfile_ref, b"this is a test password" as &[u8]); @@ -36,9 +35,6 @@ pub fn test_keyfile_cleanup() { if dangling_buffer == b"this is a test password" { panic!("Key was not cleaned up!"); } - - Ok(()) }, ) - .expect("Should succeed"); } diff --git a/src/tests/loopback.rs b/src/tests/loopback.rs index c7dfdf61..fa2a6cee 100644 --- a/src/tests/loopback.rs +++ b/src/tests/loopback.rs @@ -6,6 +6,7 @@ use std::{ env, fs::{remove_file, File}, io::{self, Write}, + panic::{self, RefUnwindSafe}, path::{Path, PathBuf}, }; @@ -13,8 +14,6 @@ use base64::Engine; use loopdev::LoopControl; use rand::random; -use crate::err::LibcryptErr; - fn setup_backing_file(size_in_bytes: usize, with_zeros: bool) -> Result { let mut i = 0; @@ -43,36 +42,25 @@ fn setup_backing_file(size_in_bytes: usize, with_zeros: bool) -> Result( - file_size: usize, - with_zeros: bool, - cleanup: bool, - func: F, -) -> Result<(), LibcryptErr> +pub fn use_loopback(file_size: usize, with_zeros: bool, cleanup: bool, func: F) where - F: Fn(&Path, &Path) -> Result<(), LibcryptErr>, + F: Fn(&Path, &Path) + RefUnwindSafe, { if !nix::unistd::Uid::effective().is_root() { panic!("Must be root to run tests"); } let ctrl = LoopControl::open(); - let dev = ctrl - .and_then(|ref c| c.next_free()) - .map_err(LibcryptErr::IOError)?; + let dev = ctrl.and_then(|ref c| c.next_free()).unwrap(); - let path = setup_backing_file(file_size, with_zeros).map_err(LibcryptErr::IOError)?; - let attach_result = dev.attach_file(&path); - let test_result = attach_result - .map_err(LibcryptErr::IOError) - .and_then(|_| match dev.path() { - Some(ref d) => func(d, &path), - _ => Err(LibcryptErr::IOError(io::Error::from( - io::ErrorKind::NotFound, - ))), - }); - let detach_result = if cleanup { dev.detach() } else { Ok(()) }; - detach_result - .and_then(|_| if cleanup { remove_file(&path) } else { Ok(()) }) - .map_err(LibcryptErr::IOError) - .and(test_result) + let path = setup_backing_file(file_size, with_zeros).unwrap(); + dev.attach_file(&path).unwrap(); + let test_result = panic::catch_unwind(|| match dev.path() { + Some(ref d) => func(d, &path), + None => panic!("No path for loopback device"), + }); + if cleanup { + dev.detach().unwrap(); + remove_file(&path).unwrap(); + } + test_result.unwrap() } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index d1070551..99130607 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -7,6 +7,8 @@ use std::env::var; pub mod encrypt; pub mod keyfile; pub mod loopback; +#[cfg(cryptsetup24supported)] +pub mod reencrypt; fn format_with_zeros() -> bool { var("FORMAT_WITH_ZEROS") diff --git a/src/tests/reencrypt.rs b/src/tests/reencrypt.rs new file mode 100644 index 00000000..13875fde --- /dev/null +++ b/src/tests/reencrypt.rs @@ -0,0 +1,105 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use crate::{ + consts::{ + flags::{CryptActivate, CryptDeactivate, CryptReencrypt, CryptVolumeKey}, + vals::{CryptReencryptDirectionInfo, CryptReencryptModeInfo, EncryptionFormat}, + }, + device::CryptInit, + get_sector_size, + tests::loopback, + CryptParamsLuks2, CryptParamsReencrypt, Either, +}; + +pub fn test_reencrypt_by_password() { + loopback::use_loopback( + 50 * 1024 * 1024, + super::format_with_zeros(), + super::do_cleanup(), + |dev_path, _file_path| { + let mut dev = CryptInit::init(dev_path).unwrap(); + dev.context_handle() + .format::<()>( + EncryptionFormat::Luks2, + ("aes", "xts-plain"), + None, + Either::Right(512 / 8), + None, + ) + .unwrap(); + + dev.keyslot_handle() + .add_by_key( + None, + None, + "thisisatest".as_bytes(), + CryptVolumeKey::empty(), + ) + .unwrap(); + + let new_keyslot = dev + .keyslot_handle() + .add_by_key( + None, + Some(Either::Right(512 / 8)), + "thisisatest".as_bytes(), + CryptVolumeKey::NO_SEGMENT, + ) + .unwrap(); + + dev.activate_handle() + .activate_by_passphrase( + Some("test-device"), + None, + "thisisatest".as_bytes(), + CryptActivate::empty(), + ) + .unwrap(); + + let size = match get_sector_size(Some(&mut dev)) { + i if i < 0 => panic!("Received error: {i:?}"), + i => i as u32, + }; + let cipher = dev.status_handle().get_cipher().unwrap(); + let cipher_mode = dev.status_handle().get_cipher_mode().unwrap(); + + dev.reencrypt_handle() + .reencrypt_init_by_passphrase( + Some("test-device"), + "thisisatest".as_bytes(), + None, + Some(new_keyslot), + Some((&cipher, &cipher_mode)), + CryptParamsReencrypt { + mode: CryptReencryptModeInfo::Reencrypt, + direction: CryptReencryptDirectionInfo::Forward, + resilience: "checksum".to_string(), + hash: "sha256".to_string(), + data_shift: 0, + max_hotzone_size: 0, + device_size: 0, + luks2: CryptParamsLuks2 { + data_alignment: 0, + data_device: None, + integrity: None, + integrity_params: None, + pbkdf: None, + label: None, + sector_size: size, + subsystem: None, + }, + flags: CryptReencrypt::empty(), + }, + ) + .unwrap(); + + dev.reencrypt_handle().reencrypt2::<()>(None, None).unwrap(); + + dev.activate_handle() + .deactivate("test-device", CryptDeactivate::empty()) + .unwrap(); + }, + ) +}