├── .gitmodules ├── examples ├── .gitignore ├── callback_macro.rs ├── label_and_uuid.rs ├── add-key-to-persistent-keyring.rs ├── unlock-luks2-with-token.rs ├── format-luks2-with-token.rs └── cryptsetup-luks2.rs ├── src ├── consts │ ├── mod.rs │ ├── flags.rs │ └── vals.rs ├── luks2 │ ├── mod.rs │ ├── flags.rs │ ├── reencrypt.rs │ └── token.rs ├── debug.rs ├── log.rs ├── tests │ ├── mod.rs │ ├── keyfile.rs │ ├── loopback.rs │ ├── reencrypt.rs │ └── encrypt.rs ├── backup.rs ├── wipe.rs ├── key.rs ├── keyfile.rs ├── err.rs ├── runtime.rs ├── lib.rs ├── mem.rs ├── activate.rs ├── status.rs ├── device.rs ├── context.rs ├── settings.rs ├── keyslot.rs └── macros.rs ├── .gitignore ├── libcryptsetup-rs-sys ├── .gitignore ├── src │ └── lib.rs ├── README.md ├── Cargo.toml ├── safe_free.h ├── safe_free.c ├── build.rs └── header.h ├── .githooks └── pre-commit ├── README.md ├── Cargo.toml ├── .packit.yaml ├── Makefile └── .github └── workflows ├── cargo.yml ├── nightly.yml └── main.yml /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /src/consts/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod flags; 2 | pub mod vals; 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | *.swp 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /src/luks2/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod flags; 2 | pub mod reencrypt; 3 | pub mod token; 4 | -------------------------------------------------------------------------------- /libcryptsetup-rs-sys/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | src/bindings.rs 5 | -------------------------------------------------------------------------------- /.githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | make fmt-ci && \ 4 | make build && \ 5 | make test && \ 6 | make test-mutex && \ 7 | make test-mutex-guard && \ 8 | make clippy && \ 9 | make check-typos \ 10 | || exit 1 11 | -------------------------------------------------------------------------------- /libcryptsetup-rs-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | #![allow(clippy::all)] 5 | #![allow(deref_nullptr)] 6 | 7 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 8 | -------------------------------------------------------------------------------- /src/debug.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use crate::consts::vals::CryptDebugLevel; 6 | 7 | /// Set library debug level 8 | pub fn set_debug_level(level: CryptDebugLevel) { 9 | mutex!(libcryptsetup_rs_sys::crypt_set_debug_level(level.into())) 10 | } 11 | -------------------------------------------------------------------------------- /libcryptsetup-rs-sys/README.md: -------------------------------------------------------------------------------- 1 | # libcryptsetup-rs-sys 2 | 3 | This crate provides low-level Rust bindings generated by bindgen for libcryptsetup. 4 | 5 | ### Building 6 | 7 | The libcryptsetup bindings require some dependencies outside of cargo to build 8 | properly: 9 | 1. cryptsetup (provided by `cryptsetup` on Fedora) 10 | 2. cryptsetup development headers (provided by `cryptsetup-devel` on Fedora) 11 | 3. libclang (provided by `clang` on Fedora) 12 | 13 | ### Testing 14 | 15 | Tests are automatically generated by bindgen. To check that bindings have been 16 | generated correctly, run: 17 | ``` 18 | cargo test 19 | ``` 20 | -------------------------------------------------------------------------------- /examples/callback_macro.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use libcryptsetup_rs::c_confirm_callback; 6 | 7 | fn safe_confirm_callback(_msg: &str, usrdata: Option<&mut u32>) -> bool { 8 | *usrdata.unwrap() != 0 9 | } 10 | 11 | c_confirm_callback!(confirm_callback, u32, safe_confirm_callback); 12 | 13 | fn main() { 14 | assert!( 15 | confirm_callback( 16 | "Would you like to proceed?\0" 17 | .as_ptr() 18 | .cast::(), 19 | (&mut 0u32 as *mut u32).cast::(), 20 | ) == 0 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /examples/label_and_uuid.rs: -------------------------------------------------------------------------------- 1 | use std::{env::args, error::Error, path::Path}; 2 | 3 | use libcryptsetup_rs::{consts::vals::EncryptionFormat, CryptInit, LibcryptErr}; 4 | use uuid::Uuid; 5 | 6 | fn main() -> Result<(), Box> { 7 | let path = args().nth(1).ok_or_else(|| { 8 | LibcryptErr::Other("Path for device required as only argument".to_string()) 9 | })?; 10 | 11 | let mut device = CryptInit::init(Path::new(&path))?; 12 | device.context_handle().format::<()>( 13 | EncryptionFormat::Luks2, 14 | ("aes", "xts-plain"), 15 | None, 16 | libcryptsetup_rs::Either::Right(256 / 8), 17 | None, 18 | )?; 19 | device 20 | .context_handle() 21 | .set_label(Some("label"), Some("subsystem"))?; 22 | device 23 | .context_handle() 24 | .set_uuid(Some(Uuid::parse_str("01234567890123456890123456789012")?))?; 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /libcryptsetup-rs-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libcryptsetup-rs-sys" 3 | version = "0.6.1" 4 | authors = ["John Baublitz "] 5 | edition = "2021" 6 | rust-version = "1.82.0" # LOWEST SUPPORTED RUST TOOLCHAIN 7 | description = "Low level bindings for libcryptsetup" 8 | license = "MPL-2.0" 9 | documentation = "https://docs.rs/libcryptsetup-rs-sys" 10 | homepage = "https://stratis-storage.github.io/" 11 | repository = "https://github.com/stratis-storage/libcryptsetup-rs" 12 | categories = ["external-ffi-bindings", "os::linux-apis"] 13 | keywords = ["storage", "cryptsetup"] 14 | 15 | [build-dependencies] 16 | cc = "1.0.45" 17 | pkg-config = "0.3.17" 18 | semver = "1.0.0" 19 | 20 | [build-dependencies.bindgen] 21 | version = "0.72.0" 22 | 23 | [lints.rust] 24 | warnings = { level = "deny" } 25 | future_incompatible = { level = "deny", priority = 1 } 26 | unused = { level = "deny", priority = 2} 27 | rust_2018_idioms = { level = "deny", priority = 3 } 28 | nonstandard_style = { level = "deny", priority = 4 } 29 | 30 | [lints.clippy] 31 | all = { level = "deny" } 32 | cargo = { level = "deny", priority = 1 } 33 | multiple-crate-versions = { level = "allow", priority = 2 } 34 | 35 | [features] 36 | static = [] 37 | -------------------------------------------------------------------------------- /src/log.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{ 6 | os::raw::{c_char, c_int, c_void}, 7 | ptr, 8 | }; 9 | 10 | use crate::{consts::vals::CryptLogLevel, err::LibcryptErr}; 11 | 12 | type LoggingCallback = unsafe extern "C" fn(level: c_int, msg: *const c_char, usrptr: *mut c_void); 13 | 14 | /// Generate a log entry 15 | pub fn log(level: CryptLogLevel, msg: &str) -> Result<(), LibcryptErr> { 16 | let msg_cstring = to_cstring!(msg)?; 17 | mutex!(libcryptsetup_rs_sys::crypt_log( 18 | ptr::null_mut(), 19 | level as c_int, 20 | msg_cstring.as_ptr(), 21 | )); 22 | Ok(()) 23 | } 24 | 25 | /// Set the callback to be executed on logging events 26 | pub fn set_log_callback(callback: Option, usrdata: Option<&mut T>) { 27 | mutex!(libcryptsetup_rs_sys::crypt_set_log_callback( 28 | ptr::null_mut(), 29 | callback, 30 | match usrdata { 31 | Some(ud) => (ud as *mut T).cast::(), 32 | None => ptr::null_mut(), 33 | }, 34 | )) 35 | } 36 | -------------------------------------------------------------------------------- /libcryptsetup-rs-sys/safe_free.h: -------------------------------------------------------------------------------- 1 | /* 2 | * utils_crypt - cipher utilities for cryptsetup 3 | * 4 | * Copyright (C) 2004-2007 Clemens Fruhwirth 5 | * Copyright (C) 2009-2019 Red Hat, Inc. All rights reserved. 6 | * Copyright (C) 2009-2019 Milan Broz 7 | * 8 | * This program is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU General Public License 10 | * as published by the Free Software Foundation; either version 2 11 | * of the License, or (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #ifndef _UTILS_CRYPT_H 24 | #define _UTILS_CRYPT_H 25 | 26 | /* Not to be used directly */ 27 | struct safe_allocation { 28 | size_t size; 29 | char data[0]; 30 | }; 31 | 32 | void crypt_safe_free(void *data); 33 | 34 | #endif /* _UTILS_CRYPT_H */ 35 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::env::var; 6 | 7 | pub mod encrypt; 8 | pub mod keyfile; 9 | pub mod loopback; 10 | #[cfg(cryptsetup24supported)] 11 | pub mod reencrypt; 12 | 13 | fn format_with_zeros() -> bool { 14 | var("FORMAT_WITH_ZEROS") 15 | .ok() 16 | .and_then(|env| env.parse::().ok()) 17 | .unwrap_or(true) 18 | } 19 | 20 | fn do_cleanup() -> bool { 21 | var("DO_CLEANUP") 22 | .ok() 23 | .and_then(|env| env.parse::().ok()) 24 | .unwrap_or(true) 25 | } 26 | 27 | #[cfg(test)] 28 | mod test { 29 | use super::*; 30 | 31 | #[test] 32 | fn test_format_with_zeros() { 33 | std::env::remove_var("FORMAT_WITH_ZEROS"); 34 | assert!(format_with_zeros()); 35 | std::env::set_var("FORMAT_WITH_ZEROS", "false"); 36 | assert!(!format_with_zeros()); 37 | std::env::remove_var("FORMAT_WITH_ZEROS"); 38 | } 39 | 40 | #[test] 41 | fn test_do_cleanup() { 42 | std::env::remove_var("DO_CLEANUP"); 43 | assert!(do_cleanup()); 44 | std::env::set_var("DO_CLEANUP", "false"); 45 | assert!(!do_cleanup()); 46 | std::env::remove_var("DO_CLEANUP"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/tests/keyfile.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs::File, io::Write, path::PathBuf}; 2 | 3 | use super::loopback; 4 | 5 | use crate::{consts::flags::CryptKeyfile, CryptInit}; 6 | 7 | pub fn test_keyfile_cleanup() { 8 | loopback::use_loopback( 9 | 50 * 1024 * 1024, 10 | super::format_with_zeros(), 11 | super::do_cleanup(), 12 | |dev_path, _file_path| { 13 | let mut device = CryptInit::init(dev_path).unwrap(); 14 | let mut key_path = 15 | PathBuf::from(env::var("TEST_DIR").unwrap_or_else(|_| "/tmp".to_string())); 16 | key_path.push("safe-free-test-keyfile"); 17 | let mut f = File::create(&key_path).unwrap(); 18 | f.write(b"this is a test password").unwrap(); 19 | let keyfile_contents = 20 | device 21 | .keyfile_handle() 22 | .device_read(&key_path, 0, None, CryptKeyfile::empty()); 23 | std::fs::remove_file(&key_path).unwrap(); 24 | let (keyfile_ptr, keyfile_len) = { 25 | let keyfile_contents = keyfile_contents.unwrap(); 26 | 27 | let keyfile_ref = keyfile_contents.as_ref(); 28 | assert_eq!(keyfile_ref, b"this is a test password" as &[u8]); 29 | 30 | (keyfile_ref.as_ptr(), keyfile_ref.len()) 31 | }; 32 | 33 | let dangling_buffer = 34 | unsafe { std::slice::from_raw_parts(keyfile_ptr.cast::(), keyfile_len) }; 35 | if dangling_buffer == b"this is a test password" { 36 | panic!("Key was not cleaned up!"); 37 | } 38 | }, 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Latest Version](https://img.shields.io/crates/v/libcryptsetup-rs.svg)](https://crates.io/crates/libcryptsetup-rs) 2 | [![Documentation](https://docs.rs/libcryptsetup-rs/badge.svg)](https://docs.rs/libcryptsetup-rs/) 3 | 4 | # libcryptsetup-rs 5 | 6 | This crate provides Rust bindings for libcryptsetup. 7 | 8 | ### Note on thread-safety 9 | 10 | libcryptsetup is *not* thread-safe and also depends on libraries that are not 11 | thread-safe. Any use of libcryptsetup by default in a multithreaded environment will 12 | result in undefined behavior. 13 | 14 | As a workaround, this library provides a feature (`mutex`) to cause all calls to 15 | libcryptsetup to acquire a crate-level mutex. This will enforce single threaded 16 | access to all invocations of the libcryptsetup API. 17 | 18 | Rust's decision to make pointers `!Send` should be respected. Any data structure that 19 | contains a pointer is *not* safe to send across threads. Providing an `unsafe 20 | impl Send {}` for any data structure provided by libcryptsetup-rs that is not `Send` 21 | may result in undefined behavior. 22 | 23 | ### Building 24 | 25 | The libcryptsetup bindings require some dependencies outside of cargo to build 26 | properly: 27 | 1. cryptsetup (provided by `cryptsetup` on Fedora) 28 | 2. cryptsetup development headers (provided by `cryptsetup-devel` on Fedora) 29 | 3. libclang (provided by `clang` on Fedora) 30 | 31 | ### Sanity testing bindings 32 | 33 | There is one test that actually invokes libcryptsetup and can be used for basic sanity 34 | testing of the bindings as it will only succeed if low level bindings are correctly generated, 35 | the high level bindings build, and libcryptsetup successfully encrypts a loopback device. 36 | 37 | This can be invoked as follows: 38 | 39 | ``` 40 | make test-loopback 41 | ``` 42 | -------------------------------------------------------------------------------- /src/backup.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{path::Path, ptr}; 6 | 7 | use crate::{consts::vals::EncryptionFormat, device::CryptDevice, err::LibcryptErr}; 8 | 9 | /// Handle for backup operations on a device 10 | pub struct CryptBackupHandle<'a> { 11 | reference: &'a mut CryptDevice, 12 | } 13 | 14 | impl<'a> CryptBackupHandle<'a> { 15 | pub(crate) fn new(reference: &'a mut CryptDevice) -> Self { 16 | CryptBackupHandle { reference } 17 | } 18 | 19 | /// Back up header and keyslots to a file 20 | pub fn header_backup( 21 | &mut self, 22 | requested_type: Option, 23 | backup_file: &Path, 24 | ) -> Result<(), LibcryptErr> { 25 | let backup_file_cstring = path_to_cstring!(backup_file)?; 26 | errno!(mutex!(libcryptsetup_rs_sys::crypt_header_backup( 27 | self.reference.as_ptr(), 28 | requested_type 29 | .map(|ty| ty.as_ptr()) 30 | .unwrap_or_else(ptr::null), 31 | backup_file_cstring.as_ptr(), 32 | ))) 33 | } 34 | 35 | /// Restore header and keyslots from a file 36 | pub fn header_restore( 37 | &mut self, 38 | requested_type: Option, 39 | backup_file: &Path, 40 | ) -> Result<(), LibcryptErr> { 41 | let backup_file_cstring = path_to_cstring!(backup_file)?; 42 | errno!(mutex!(libcryptsetup_rs_sys::crypt_header_restore( 43 | self.reference.as_ptr(), 44 | requested_type 45 | .map(|ty| ty.as_ptr()) 46 | .unwrap_or_else(ptr::null), 47 | backup_file_cstring.as_ptr(), 48 | ))) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /libcryptsetup-rs-sys/safe_free.c: -------------------------------------------------------------------------------- 1 | /* 2 | * utils_crypt - cipher utilities for cryptsetup 3 | * 4 | * Copyright (C) 2004-2007 Clemens Fruhwirth 5 | * Copyright (C) 2009-2019 Red Hat, Inc. All rights reserved. 6 | * Copyright (C) 2009-2019 Milan Broz 7 | * 8 | * This program is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU General Public License 10 | * as published by the Free Software Foundation; either version 2 11 | * of the License, or (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "safe_free.h" 28 | 29 | /* 30 | * Replacement for memset(s, 0, n) on stack that can be optimized out 31 | * Also used in safe allocations for explicit memory wipe. 32 | */ 33 | void crypt_memzero(void *s, size_t n) 34 | { 35 | #ifdef HAVE_EXPLICIT_BZERO 36 | explicit_bzero(s, n); 37 | #else 38 | volatile uint8_t *p = (volatile uint8_t *)s; 39 | 40 | while(n--) 41 | *p++ = 0; 42 | #endif 43 | } 44 | 45 | void crypt_safe_free(void *data) 46 | { 47 | struct safe_allocation *alloc; 48 | 49 | if (!data) 50 | return; 51 | 52 | alloc = (struct safe_allocation *) 53 | ((char *)data - offsetof(struct safe_allocation, data)); 54 | 55 | crypt_memzero(data, alloc->size); 56 | 57 | alloc->size = 0x55aa55aa; 58 | free(alloc); 59 | } 60 | -------------------------------------------------------------------------------- /src/wipe.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{ 6 | os::raw::{c_int, c_void}, 7 | path::Path, 8 | }; 9 | 10 | use crate::{ 11 | consts::{flags::CryptWipe, vals::CryptWipePattern}, 12 | device::CryptDevice, 13 | err::LibcryptErr, 14 | }; 15 | 16 | type WipeProgressCallback = 17 | unsafe extern "C" fn(size: u64, offset: u64, usrptr: *mut c_void) -> c_int; 18 | 19 | /// Handle for volume key operations 20 | pub struct CryptWipeHandle<'a> { 21 | reference: &'a mut CryptDevice, 22 | } 23 | 24 | impl<'a> CryptWipeHandle<'a> { 25 | pub(crate) fn new(reference: &'a mut CryptDevice) -> Self { 26 | CryptWipeHandle { reference } 27 | } 28 | 29 | /// Wipe a device with the selected pattern 30 | #[allow(clippy::too_many_arguments)] 31 | pub fn wipe( 32 | &mut self, 33 | dev_path: &Path, 34 | pattern: CryptWipePattern, 35 | offset: u64, 36 | length: u64, 37 | wipe_block_size: crate::size_t, 38 | flags: CryptWipe, 39 | callback: Option, 40 | usrptr: Option<&mut T>, 41 | ) -> Result<(), LibcryptErr> { 42 | let dev_path_cstring = path_to_cstring!(dev_path)?; 43 | errno!(mutex!(libcryptsetup_rs_sys::crypt_wipe( 44 | self.reference.as_ptr(), 45 | dev_path_cstring.as_ptr(), 46 | pattern.into(), 47 | offset, 48 | length, 49 | wipe_block_size, 50 | flags.bits(), 51 | callback, 52 | match usrptr { 53 | Some(up) => (up as *mut T).cast::(), 54 | None => std::ptr::null_mut(), 55 | }, 56 | ))) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libcryptsetup-rs" 3 | version = "0.15.0" 4 | authors = ["John Baublitz "] 5 | edition = "2021" 6 | rust-version = "1.82.0" # LOWEST SUPPORTED RUST TOOLCHAIN 7 | description = "High level Rust bindings for libcryptsetup" 8 | license = "MPL-2.0" 9 | documentation = "https://docs.rs/libcryptsetup-rs" 10 | homepage = "https://stratis-storage.github.io/" 11 | repository = "https://github.com/stratis-storage/libcryptsetup-rs" 12 | build = "build.rs" 13 | keywords = ["Linux", "encryption", "storage"] 14 | categories = ["cryptography", "api-bindings", "os::linux-apis"] 15 | exclude = [".clippy.toml", ".githooks/*", ".gitignore", ".github/*", "Makefile"] 16 | 17 | [dependencies.libcryptsetup-rs-sys] 18 | version = "0.6.0" 19 | path = "./libcryptsetup-rs-sys" 20 | 21 | [dependencies] 22 | either = "1.6.1" 23 | libc = "0.2.155" 24 | bitflags = "2.3.1" 25 | log = "0.4.20" 26 | per-thread-mutex = "0.1.4" 27 | serde_json = "1.0.0" 28 | 29 | [dependencies.uuid] 30 | version = "1.0.0" 31 | features = ["v4"] 32 | 33 | [build-dependencies] 34 | pkg-config = "0.3.17" 35 | semver = "1.0.0" 36 | 37 | [dev-dependencies] 38 | base64 = "0.22.0" 39 | env_logger = "0.11.0" 40 | loopdev-3 = "0.5.0" 41 | nix = {version="0.30.1", features=["user"]} 42 | rand = "0.9.0" 43 | 44 | [features] 45 | mutex = [] 46 | static = ["libcryptsetup-rs-sys/static"] 47 | 48 | [lints.rust] 49 | warnings = { level = "deny" } 50 | future_incompatible = { level = "deny", priority = 1 } 51 | unused = { level = "deny", priority = 2} 52 | rust_2018_idioms = { level = "deny", priority = 3 } 53 | nonstandard_style = { level = "deny", priority = 4 } 54 | unexpected_cfgs = { level = "deny", check-cfg = [ 55 | 'cfg(cryptsetup23supported)', 'cfg(cryptsetup24supported)', 'cfg(cryptsetup27supported)' 56 | ] } 57 | 58 | [lints.clippy] 59 | all = { level = "deny" } 60 | cargo = { level = "deny", priority = 1 } 61 | multiple-crate-versions = { level = "allow", priority = 2 } 62 | ptr-as-ptr = { level = "deny", priority = 3 } 63 | -------------------------------------------------------------------------------- /src/key.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{ 6 | os::raw::{c_int, c_uint}, 7 | ptr, 8 | }; 9 | 10 | use crate::{device::CryptDevice, err::LibcryptErr}; 11 | 12 | /// Handle for volume key operations 13 | pub struct CryptVolumeKeyHandle<'a> { 14 | reference: &'a mut CryptDevice, 15 | } 16 | 17 | impl<'a> CryptVolumeKeyHandle<'a> { 18 | pub(crate) fn new(reference: &'a mut CryptDevice) -> Self { 19 | CryptVolumeKeyHandle { reference } 20 | } 21 | 22 | /// Get volume key from crypt device - first tuple element is key slot, second is volume key 23 | /// size 24 | pub fn get( 25 | &mut self, 26 | keyslot: Option, 27 | volume_key: &mut [u8], 28 | passphrase: Option<&[u8]>, 29 | ) -> Result<(c_int, crate::size_t), LibcryptErr> { 30 | let mut volume_key_size_t = volume_key.len(); 31 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_volume_key_get( 32 | self.reference.as_ptr(), 33 | keyslot 34 | .map(|i| i as c_int) 35 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_SLOT), 36 | to_mut_byte_ptr!(volume_key), 37 | &mut volume_key_size_t as *mut _, 38 | passphrase 39 | .as_ref() 40 | .map(|s| to_byte_ptr!(s)) 41 | .unwrap_or(ptr::null()), 42 | passphrase.map(|p| p.len()).unwrap_or(0), 43 | ))) 44 | .map(|i| (i, volume_key_size_t)) 45 | } 46 | 47 | /// Verify that volume key is valid for crypt device 48 | pub fn verify(&mut self, volume_key: &[u8]) -> Result<(), LibcryptErr> { 49 | errno!(mutex!(libcryptsetup_rs_sys::crypt_volume_key_verify( 50 | self.reference.as_ptr(), 51 | to_byte_ptr!(volume_key), 52 | volume_key.len(), 53 | ))) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/add-key-to-persistent-keyring.rs: -------------------------------------------------------------------------------- 1 | use std::{env::args, ffi::CString, io}; 2 | 3 | use libc::syscall; 4 | 5 | fn usage() -> &'static str { 6 | "Usage: add-to-persistent-keyring \n\ 7 | \tKEY_DESCRIPTION: Kernel keyring key description\n\ 8 | \tKEY_DATA: Secret data associated with the key description" 9 | } 10 | 11 | fn parse_args() -> Result<(String, String), &'static str> { 12 | let args: Vec<_> = args().collect(); 13 | if args.len() != 3 { 14 | println!("{}", usage()); 15 | return Err("Incorrect arguments provided"); 16 | } 17 | 18 | let key_desc = args.get(1).ok_or("No key description provided")?; 19 | 20 | let key_data = args.get(2).ok_or("No key data provided")?; 21 | 22 | Ok((key_desc.to_owned(), key_data.to_owned())) 23 | } 24 | 25 | fn add_to_persistent_keyring(key_desc: String, key_data: String) -> Result<(), io::Error> { 26 | let persistent_id = match unsafe { 27 | syscall( 28 | libc::SYS_keyctl, 29 | libc::KEYCTL_GET_PERSISTENT, 30 | 0, 31 | libc::KEY_SPEC_SESSION_KEYRING, 32 | ) 33 | } { 34 | i if i < 0 => return Err(io::Error::last_os_error()), 35 | i => i, 36 | }; 37 | if unsafe { syscall(libc::SYS_keyctl, libc::KEYCTL_CLEAR, persistent_id) } < 0 { 38 | return Err(io::Error::last_os_error()); 39 | } 40 | let key_desc_cstring = CString::new(key_desc)?; 41 | if unsafe { 42 | libc::syscall( 43 | libc::SYS_add_key, 44 | concat!("user", "\0").as_ptr(), 45 | key_desc_cstring.as_ptr(), 46 | key_data.as_ptr(), 47 | key_data.len(), 48 | persistent_id, 49 | ) 50 | } < 0 51 | { 52 | Err(io::Error::last_os_error()) 53 | } else { 54 | Ok(()) 55 | } 56 | } 57 | 58 | fn main() -> Result<(), String> { 59 | let (key_desc, key_data) = parse_args()?; 60 | add_to_persistent_keyring(key_desc, key_data).map_err(|e| e.to_string())?; 61 | Ok(()) 62 | } 63 | -------------------------------------------------------------------------------- /libcryptsetup-rs-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use pkg_config::{Config, Library}; 4 | use semver::Version; 5 | 6 | use std::path::PathBuf; 7 | 8 | fn probe() -> Library { 9 | let mut config = Config::new(); 10 | #[cfg(feature = "static")] 11 | config.statik(true); 12 | match config.atleast_version("2.2.0").probe("libcryptsetup") { 13 | Ok(l) => l, 14 | Err(e) => panic!("Bindings require at least cryptsetup-2.2.0: {e}"), 15 | } 16 | } 17 | 18 | fn build_safe_free() { 19 | cc::Build::new().file("safe_free.c").compile("safe_free"); 20 | 21 | println!("cargo:rustc-link-lib=cryptsetup"); 22 | } 23 | 24 | fn generate_bindings(library: &Library, safe_free_is_needed: bool) { 25 | let builder = bindgen::Builder::default() 26 | .rust_target(env!("CARGO_PKG_RUST_VERSION").parse().expect("valid rust version")) 27 | .clang_args( 28 | library 29 | .include_paths 30 | .iter() 31 | .map(|path| format!("-I{}", path.display())), 32 | ) 33 | .header("header.h") 34 | .size_t_is_usize(true); 35 | #[cfg(target_arch = "x86")] 36 | let builder = builder.blocklist_type("max_align_t"); 37 | let builder_with_safe_free = if safe_free_is_needed { 38 | builder.header("safe_free.h") 39 | } else { 40 | builder 41 | }; 42 | let bindings = builder_with_safe_free 43 | .generate() 44 | .expect("Unable to generate bindings"); 45 | 46 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 47 | bindings 48 | .write_to_file(out_path.join("bindings.rs")) 49 | .expect("Couldn't write bindings"); 50 | } 51 | 52 | fn main() { 53 | let library = probe(); 54 | let version = Version::parse(&library.version).expect("Could not parse version"); 55 | let safe_free_is_needed = version < Version::new(2, 3, 0); 56 | if safe_free_is_needed { 57 | build_safe_free(); 58 | } 59 | generate_bindings(&library, safe_free_is_needed); 60 | println!("cargo:rerun-if-changed=header.h"); 61 | } 62 | -------------------------------------------------------------------------------- /.packit.yaml: -------------------------------------------------------------------------------- 1 | upstream_project_url: https://github.com/stratis-storage/libcryptsetup-rs 2 | 3 | packages: 4 | libcryptsetup-rs-sys: 5 | specfile_path: ../distro/rust-libcryptsetup-rs-sys.spec 6 | downstream_package_name: rust-libcryptsetup-rs-sys 7 | actions: 8 | post-upstream-clone: 9 | - "mkdir ../distro" 10 | - "bash -c '(cd ../distro; wget https://src.fedoraproject.org/rpms/rust-libcryptsetup-rs-sys/raw/rawhide/f/rust-libcryptsetup-rs-sys.spec https://src.fedoraproject.org/rpms/rust-libcryptsetup-rs-sys/raw/rawhide/f/rust2rpm.toml)'" 11 | - "cargo install cargo-get" 12 | create-archive: 13 | - "bash -c '(cd libcryptsetup-rs-sys; cargo package --no-verify)'" 14 | - "bash -c '(cd ../distro; rust2rpm libcryptsetup-rs-sys)'" 15 | - "bash -c 'echo \"libcryptsetup-rs-sys/target/package/libcryptsetup-rs-sys-$(cd libcryptsetup-rs-sys; cargo get package.version).crate\"'" 16 | get-current-version: 17 | - "bash -c '(cd libcryptsetup-rs-sys; cargo get package.version)'" 18 | 19 | libcryptsetup-rs: 20 | specfile_path: ../distro/rust-libcryptsetup-rs.spec 21 | downstream_package_name: rust-libcryptsetup-rs 22 | actions: 23 | post-upstream-clone: 24 | - "mkdir ../distro" 25 | - "bash -c '(cd ../distro; wget https://src.fedoraproject.org/rpms/rust-libcryptsetup-rs/raw/rawhide/f/rust-libcryptsetup-rs.spec)'" 26 | - "cargo install cargo-get" 27 | create-archive: 28 | - "cargo package --no-verify" 29 | - "bash -c '(cd ../distro; rust2rpm libcryptsetup-rs)'" 30 | - "bash -c 'echo \"target/package/libcryptsetup-rs-$(cargo get package.version).crate\"'" 31 | get-current-version: 32 | - "cargo get package.version" 33 | 34 | srpm_build_deps: 35 | - cargo 36 | - rust2rpm 37 | - rust2rpm-helper 38 | - wget2 39 | - wget2-wget 40 | 41 | jobs: 42 | - job: copr_build 43 | trigger: pull_request 44 | identifier: copr_pull 45 | targets: 46 | - fedora-all 47 | 48 | - job: copr_build 49 | trigger: commit 50 | identifier: copr_commit 51 | preserve_project: true 52 | targets: 53 | - fedora-all 54 | 55 | notifications: 56 | pull_request: 57 | successful_build: true 58 | -------------------------------------------------------------------------------- /examples/unlock-luks2-with-token.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env::args, 3 | io, 4 | path::{Path, PathBuf}, 5 | }; 6 | 7 | use libc::syscall; 8 | use libcryptsetup_rs::{ 9 | consts::{flags::CryptActivate, vals::EncryptionFormat}, 10 | CryptInit, LibcryptErr, 11 | }; 12 | 13 | fn usage() -> &'static str { 14 | "Usage: format-luks2-with-token \n\ 15 | \tDEVICE_PATH: Path to devices to unlock\n\ 16 | \tDEVICE_NAME: Name of activated device" 17 | } 18 | 19 | fn parse_args() -> Result<(PathBuf, String), &'static str> { 20 | let args: Vec<_> = args().collect(); 21 | if args.len() != usage().split('\n').count() { 22 | println!("{}", usage()); 23 | return Err("Incorrect arguments provided"); 24 | } 25 | 26 | let device_string = args 27 | .get(1) 28 | .ok_or("Could not get the device path for the device node to be encrypted")?; 29 | let device_path = PathBuf::from(device_string); 30 | if !device_path.exists() { 31 | return Err("Device does not exist"); 32 | } 33 | 34 | let device_name = args 35 | .get(2) 36 | .ok_or("No device name was provided")? 37 | .to_string(); 38 | 39 | Ok((device_path, device_name)) 40 | } 41 | 42 | fn attach_persistent_keyring() -> Result<(), io::Error> { 43 | match unsafe { 44 | syscall( 45 | libc::SYS_keyctl, 46 | libc::KEYCTL_GET_PERSISTENT, 47 | 0, 48 | libc::KEY_SPEC_SESSION_KEYRING, 49 | ) 50 | } { 51 | i if i < 0 => Err(io::Error::last_os_error()), 52 | _ => Ok(()), 53 | } 54 | } 55 | 56 | fn activate(devpath: &Path, name: &str) -> Result<(), LibcryptErr> { 57 | let mut device = CryptInit::init(devpath)?; 58 | device 59 | .context_handle() 60 | .load::<()>(Some(EncryptionFormat::Luks2), None)?; 61 | device.token_handle().activate_by_token::<()>( 62 | Some(name), 63 | None, 64 | None, 65 | CryptActivate::empty(), 66 | )?; 67 | Ok(()) 68 | } 69 | 70 | fn main() -> Result<(), String> { 71 | let (device_path, device_name) = parse_args()?; 72 | attach_persistent_keyring().map_err(|e| e.to_string())?; 73 | activate(&device_path, &device_name).map_err(|e| e.to_string())?; 74 | Ok(()) 75 | } 76 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(origin FEDORA_RELEASE), undefined) 2 | else 3 | FEDORA_RELEASE_ARGS = --release=${FEDORA_RELEASE} 4 | endif 5 | 6 | ifeq ($(origin CLIPPY_FIX), undefined) 7 | CLIPPY_OPTS = --all-targets --no-deps 8 | else 9 | CLIPPY_OPTS = --fix 10 | endif 11 | 12 | ifeq ($(origin MINIMAL), undefined) 13 | BUILD = build 14 | else 15 | BUILD = minimal-versions build --direct 16 | endif 17 | 18 | IGNORE_ARGS ?= 19 | 20 | audit: 21 | cargo audit -D warnings 22 | 23 | check-typos: 24 | typos 25 | 26 | build: 27 | cargo ${BUILD} 28 | 29 | build-examples: 30 | cargo ${BUILD} --examples 31 | 32 | test-compare-fedora-versions: 33 | echo "Testing that COMPARE_FEDORA_VERSIONS environment variable is set to a valid path" 34 | test -e "${COMPARE_FEDORA_VERSIONS}" 35 | 36 | check-fedora-versions: test-compare-fedora-versions 37 | ${COMPARE_FEDORA_VERSIONS} ${FEDORA_RELEASE_ARGS} ${IGNORE_ARGS} 38 | 39 | clippy: 40 | (cd libcryptsetup-rs-sys && cargo clippy --all-features ${CARGO_OPTS}) 41 | cargo clippy --all-features ${CARGO_OPTS} 42 | 43 | docs-rust: 44 | cargo doc --no-deps --package libcryptsetup-rs --package libcryptsetup-rs-sys 45 | 46 | docs-ci: docs-rust 47 | 48 | fmt: 49 | cargo fmt 50 | 51 | fmt-ci: 52 | cargo fmt -- --check 53 | 54 | release: 55 | cargo build --release 56 | 57 | test: 58 | RUST_BACKTRACE=1 cargo test -- --skip test_mutex_poisoning_panic 59 | 60 | test-mutex: 61 | RUST_BACKTRACE=1 cargo test --features=mutex -- --skip test_mutex_poisoning_panic 62 | 63 | test-mutex-guard: 64 | RUST_BACKTRACE=1 RUST_TEST_THREADS=1 cargo test --features=mutex test_mutex_poisoning_panic 65 | 66 | # Loopback tests must have the mutex feature enabled because Rust runs the tests 67 | # on multiple threads which will cause a panic if the mutex feature is not enabled. 68 | test-loopback: 69 | RUST_BACKTRACE=1 RUST_TEST_THREADS=1 CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER='sudo -E' cargo test --features=mutex -- --ignored --skip test_mutex_poisoning_panic 70 | 71 | yamllint: 72 | yamllint --strict .github/workflows/*.yml 73 | 74 | .PHONY: 75 | audit 76 | build 77 | check-fedora-versions 78 | check-typos 79 | clippy 80 | docs-rust 81 | docs-ci 82 | fmt 83 | fmt-ci 84 | release 85 | test 86 | test-mutex 87 | test-mutex-guard 88 | test-compare-fedora-versions 89 | test-loopback 90 | test-loopback-mutex 91 | yamllint 92 | -------------------------------------------------------------------------------- /src/tests/loopback.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{ 6 | env, 7 | fs::{remove_file, File}, 8 | io::{self, Write}, 9 | panic::{self, RefUnwindSafe}, 10 | path::{Path, PathBuf}, 11 | }; 12 | 13 | use base64::Engine; 14 | use loopdev::LoopControl; 15 | use rand::random; 16 | 17 | fn setup_backing_file(size_in_bytes: usize, with_zeros: bool) -> Result { 18 | let mut i = 0; 19 | 20 | let b64_string = base64::engine::GeneralPurpose::new( 21 | &base64::alphabet::URL_SAFE, 22 | base64::engine::general_purpose::GeneralPurposeConfig::new().with_encode_padding(false), 23 | ) 24 | .encode(random::<[u8; 12]>()); 25 | let directory = PathBuf::from(env::var("TEST_DIR").unwrap_or_else(|_| "/tmp".to_string())); 26 | assert!(directory.exists() && directory.is_dir()); 27 | let mut file_path = PathBuf::new(); 28 | file_path.push(directory); 29 | file_path.push(b64_string); 30 | 31 | let mut f = File::create(&file_path)?; 32 | while i < size_in_bytes { 33 | let len = if with_zeros { 34 | f.write(&[0; 4096])? 35 | } else { 36 | let buf: Vec<_> = (0..4096).map(|_| random::()).collect(); 37 | f.write(&buf)? 38 | }; 39 | assert_eq!(len, 4096); 40 | i += len; 41 | } 42 | Ok(file_path) 43 | } 44 | 45 | pub fn use_loopback(file_size: usize, with_zeros: bool, cleanup: bool, func: F) 46 | where 47 | F: Fn(&Path, &Path) + RefUnwindSafe, 48 | { 49 | if !nix::unistd::Uid::effective().is_root() { 50 | panic!("Must be root to run tests"); 51 | } 52 | let ctrl = LoopControl::open(); 53 | let dev = ctrl.and_then(|ref c| c.next_free()).unwrap(); 54 | 55 | let path = setup_backing_file(file_size, with_zeros).unwrap(); 56 | dev.attach_file(&path).unwrap(); 57 | let test_result = panic::catch_unwind(|| match dev.path() { 58 | Some(ref d) => func(d, &path), 59 | None => panic!("No path for loopback device"), 60 | }); 61 | if cleanup { 62 | dev.detach().unwrap(); 63 | remove_file(&path).unwrap(); 64 | } 65 | test_result.unwrap() 66 | } 67 | -------------------------------------------------------------------------------- /src/keyfile.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{path::Path, ptr}; 6 | 7 | use libc::{c_char, c_void}; 8 | 9 | use crate::{ 10 | consts::flags::CryptKeyfile, device::CryptDevice, err::LibcryptErr, mem::SafeMemHandle, 11 | }; 12 | 13 | /// Contents of a keyfile that have been read 14 | pub struct CryptKeyfileContents { 15 | key_mem: SafeMemHandle, 16 | } 17 | 18 | impl AsRef<[u8]> for CryptKeyfileContents { 19 | fn as_ref(&self) -> &[u8] { 20 | self.key_mem.as_ref() 21 | } 22 | } 23 | 24 | /// Handle for keyfile operations 25 | pub struct CryptKeyfileHandle<'a> { 26 | reference: &'a mut CryptDevice, 27 | } 28 | 29 | impl<'a> CryptKeyfileHandle<'a> { 30 | /// Create a new keyfile operation handle 31 | pub(crate) fn new(reference: &'a mut CryptDevice) -> Self { 32 | CryptKeyfileHandle { reference } 33 | } 34 | 35 | /// Read keyfile into memory - these bindings will automatically 36 | /// safely clean it up after `CryptKeyfileContents` is dropped 37 | pub fn device_read( 38 | &mut self, 39 | keyfile: &Path, 40 | keyfile_offset: u64, 41 | key_size: Option, 42 | flags: CryptKeyfile, 43 | ) -> Result { 44 | let keyfile_cstring = path_to_cstring!(keyfile)?; 45 | let keyfile_size = match key_size { 46 | Some(i) => i, 47 | None => std::fs::metadata(keyfile) 48 | .map_err(LibcryptErr::IOError)? 49 | .len() as crate::size_t, 50 | }; 51 | 52 | let mut key: *mut c_char = ptr::null_mut(); 53 | let mut size: crate::size_t = 0; 54 | errno!(mutex!(libcryptsetup_rs_sys::crypt_keyfile_device_read( 55 | self.reference.as_ptr(), 56 | keyfile_cstring.as_ptr(), 57 | &mut key as *mut *mut c_char, 58 | &mut size as *mut crate::size_t, 59 | keyfile_offset, 60 | keyfile_size, 61 | flags.bits(), 62 | )))?; 63 | Ok(CryptKeyfileContents { 64 | key_mem: unsafe { SafeMemHandle::from_ptr(key.cast::(), size) }, 65 | }) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/err.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{ 6 | error::Error, 7 | ffi::NulError, 8 | fmt::{self, Display}, 9 | io, 10 | str::Utf8Error, 11 | }; 12 | 13 | #[derive(Debug)] 14 | /// Error returned from any libcryptsetup-rs function 15 | pub enum LibcryptErr { 16 | /// Wrapper for `io::Error` 17 | IOError(io::Error), 18 | /// Wrapper for `uuid::parser::ParseError` 19 | UuidError(uuid::Error), 20 | /// Wrapper for `ffi::NulError` 21 | NullError(NulError), 22 | /// Wrapper for `str::Utf8Error` 23 | Utf8Error(Utf8Error), 24 | /// Wrapper for `serde_json::Error` 25 | JsonError(serde_json::Error), 26 | /// Indicates that a Rust/C conversion was unsuccessful 27 | InvalidConversion, 28 | /// Indicates that a pointer returned was null signifying an error 29 | NullPtr, 30 | /// Indicates that a `&'static str` was not created with `c_str!()` macro 31 | NoNull(&'static str), 32 | /// Custom message 33 | Other(String), 34 | } 35 | 36 | impl Display for LibcryptErr { 37 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 38 | match *self { 39 | LibcryptErr::IOError(ref e) => write!(f, "IO error occurred: {e}"), 40 | LibcryptErr::UuidError(ref e) => write!(f, "Failed to parse UUID from C string: {e}"), 41 | LibcryptErr::NullError(ref e) => { 42 | write!(f, "Null error occurred when handling &str conversion: {e}") 43 | } 44 | LibcryptErr::Utf8Error(ref e) => { 45 | write!(f, "UTF8 error occurred when handling &str conversion: {e}") 46 | } 47 | LibcryptErr::JsonError(ref e) => { 48 | write!(f, "Failed to parse the provided string into JSON: {e}") 49 | } 50 | LibcryptErr::InvalidConversion => { 51 | write!(f, "Failed to perform the specified conversion") 52 | } 53 | LibcryptErr::NullPtr => write!(f, "Cryptsetup returned a null pointer"), 54 | LibcryptErr::NoNull(s) => { 55 | write!(f, "Static string {s} was not created with c_str!() macro") 56 | } 57 | LibcryptErr::Other(ref s) => write!(f, "Failed with error: {s}"), 58 | } 59 | } 60 | } 61 | 62 | impl Error for LibcryptErr {} 63 | -------------------------------------------------------------------------------- /src/runtime.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use crate::{consts::flags::CryptActivate, device::CryptDevice, err::LibcryptErr}; 6 | 7 | /// Record containing data on the given active device 8 | pub struct ActiveDevice { 9 | /// Device offset 10 | pub offset: u64, 11 | /// Initialization vector offset 12 | pub iv_offset: u64, 13 | /// Size of the device 14 | pub size: u64, 15 | /// Flags with activation options 16 | pub flags: CryptActivate, 17 | } 18 | 19 | impl<'a> TryFrom<&'a libcryptsetup_rs_sys::crypt_active_device> for ActiveDevice { 20 | type Error = LibcryptErr; 21 | 22 | fn try_from(v: &'a libcryptsetup_rs_sys::crypt_active_device) -> Result { 23 | Ok(ActiveDevice { 24 | offset: v.offset, 25 | iv_offset: v.iv_offset, 26 | size: v.size, 27 | flags: CryptActivate::from_bits(v.flags).ok_or(LibcryptErr::InvalidConversion)?, 28 | }) 29 | } 30 | } 31 | 32 | /// Handle for runtime attribute options 33 | pub struct CryptRuntimeHandle<'a> { 34 | reference: &'a mut CryptDevice, 35 | name: &'a str, 36 | } 37 | 38 | impl<'a> CryptRuntimeHandle<'a> { 39 | pub(crate) fn new(reference: &'a mut CryptDevice, name: &'a str) -> Self { 40 | CryptRuntimeHandle { reference, name } 41 | } 42 | 43 | /// Get active crypt device attributes 44 | pub fn get_active_device(&mut self) -> Result { 45 | let mut cad = libcryptsetup_rs_sys::crypt_active_device { 46 | offset: 0, 47 | iv_offset: 0, 48 | size: 0, 49 | flags: 0, 50 | }; 51 | let name_cstring = to_cstring!(self.name)?; 52 | errno!(mutex!(libcryptsetup_rs_sys::crypt_get_active_device( 53 | self.reference.as_ptr(), 54 | name_cstring.as_ptr(), 55 | &mut cad as *mut _, 56 | ))) 57 | .and_then(|_| ActiveDevice::try_from(&cad)) 58 | } 59 | 60 | /// Get detected number of integrity failures 61 | pub fn get_active_integrity_failures(&mut self) -> Result { 62 | let name_cstring = to_cstring!(self.name)?; 63 | Ok(mutex!( 64 | libcryptsetup_rs_sys::crypt_get_active_integrity_failures( 65 | self.reference.as_ptr(), 66 | name_cstring.as_ptr(), 67 | ) 68 | )) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/luks2/flags.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::marker::PhantomData; 6 | 7 | use crate::{ 8 | consts::{ 9 | flags::{CryptActivate, CryptRequirement}, 10 | vals::CryptFlagsType, 11 | }, 12 | device::CryptDevice, 13 | err::LibcryptErr, 14 | }; 15 | 16 | /// Handle for LUKS2 persistent flag operations 17 | pub struct CryptLuks2FlagsHandle<'a, T> { 18 | reference: &'a mut CryptDevice, 19 | data: PhantomData, 20 | } 21 | 22 | impl<'a, T> CryptLuks2FlagsHandle<'a, T> { 23 | pub(crate) fn new(reference: &'a mut CryptDevice) -> Self { 24 | CryptLuks2FlagsHandle { 25 | reference, 26 | data: PhantomData, 27 | } 28 | } 29 | } 30 | 31 | impl CryptLuks2FlagsHandle<'_, CryptActivate> { 32 | /// Implementation for setting persistent flags for activation 33 | pub fn persistent_flags_set(&mut self, flags: CryptActivate) -> Result<(), LibcryptErr> { 34 | errno!(mutex!(libcryptsetup_rs_sys::crypt_persistent_flags_set( 35 | self.reference.as_ptr(), 36 | CryptFlagsType::Activation as u32, 37 | flags.bits(), 38 | ))) 39 | } 40 | 41 | /// Implementation for getting persistent flags for activation 42 | pub fn persistent_flags_get(&mut self) -> Result { 43 | let mut flags_u32 = 0u32; 44 | errno!(unsafe { 45 | libcryptsetup_rs_sys::crypt_persistent_flags_get( 46 | self.reference.as_ptr(), 47 | CryptFlagsType::Activation as u32, 48 | &mut flags_u32 as *mut _, 49 | ) 50 | }) 51 | .and_then(|_| CryptActivate::from_bits(flags_u32).ok_or(LibcryptErr::InvalidConversion)) 52 | } 53 | } 54 | 55 | impl CryptLuks2FlagsHandle<'_, CryptRequirement> { 56 | /// Implementation for setting persistent flags for requirements 57 | pub fn persistent_flags_set(&mut self, flags: CryptRequirement) -> Result<(), LibcryptErr> { 58 | errno!(unsafe { 59 | libcryptsetup_rs_sys::crypt_persistent_flags_set( 60 | self.reference.as_ptr(), 61 | CryptFlagsType::Requirements as u32, 62 | flags.bits(), 63 | ) 64 | }) 65 | } 66 | 67 | /// Implementation for getting persistent flags for requirements 68 | pub fn persistent_flags_get(&mut self) -> Result { 69 | let mut flags_u32 = 0u32; 70 | errno!(unsafe { 71 | libcryptsetup_rs_sys::crypt_persistent_flags_get( 72 | self.reference.as_ptr(), 73 | CryptFlagsType::Requirements as u32, 74 | &mut flags_u32 as *mut _, 75 | ) 76 | }) 77 | .and_then(|_| CryptRequirement::from_bits(flags_u32).ok_or(LibcryptErr::InvalidConversion)) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /.github/workflows/cargo.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: libcryptsetup cargo CI 3 | 4 | # yamllint disable-line rule:truthy 5 | on: 6 | push: 7 | branches: [master] 8 | paths: 9 | - '**/Cargo.toml' 10 | - '.github/workflows/cargo.yml' 11 | pull_request: 12 | branches: [master] 13 | paths: 14 | - '**/Cargo.toml' 15 | - '.github/workflows/cargo.yml' 16 | 17 | # Allows you to run this workflow manually from the Actions tab 18 | workflow_dispatch: 19 | 20 | jobs: 21 | checks-with-ci-repo: 22 | runs-on: ubuntu-22.04 23 | container: 24 | image: fedora:42 # CURRENT DEVELOPMENT ENVIRONMENT 25 | steps: 26 | - name: Install git 27 | run: dnf install -y git 28 | - uses: actions/checkout@v6 29 | with: 30 | path: libcryptsetup-rs 31 | persist-credentials: false 32 | - name: Install dependencies for Fedora 33 | run: > 34 | dnf install -y 35 | clang 36 | cryptsetup-devel 37 | curl 38 | make 39 | openssl-devel 40 | python-requests 41 | python-semantic_version 42 | - uses: dtolnay/rust-toolchain@master 43 | with: 44 | components: cargo 45 | toolchain: 1.92.0 # CURRENT DEVELOPMENT RUST TOOLCHAIN 46 | - name: Check out ci repo 47 | uses: actions/checkout@v6 48 | with: 49 | path: ci 50 | repository: stratis-storage/ci 51 | persist-credentials: false 52 | - name: Run comparisons of -sys version specs with Fedora 53 | # yamllint disable rule:line-length 54 | run: | 55 | COMPARE_FEDORA_VERSIONS=$GITHUB_WORKSPACE/ci/dependency_management/compare_fedora_versions FEDORA_RELEASE=rawhide IGNORE_ARGS="--ignore-category low" make -f ../Makefile check-fedora-versions 56 | COMPARE_FEDORA_VERSIONS=$GITHUB_WORKSPACE/ci/dependency_management/compare_fedora_versions FEDORA_RELEASE=f43 IGNORE_ARGS="--ignore-category low" make -f ../Makefile check-fedora-versions 57 | COMPARE_FEDORA_VERSIONS=$GITHUB_WORKSPACE/ci/dependency_management/compare_fedora_versions FEDORA_RELEASE=f42 IGNORE_ARGS="--ignore-category low" make -f ../Makefile check-fedora-versions 58 | working-directory: libcryptsetup-rs/libcryptsetup-rs-sys 59 | - name: Run comparisons of version specs with Fedora 60 | # yamllint disable rule:line-length 61 | run: | 62 | COMPARE_FEDORA_VERSIONS=$GITHUB_WORKSPACE/ci/dependency_management/compare_fedora_versions FEDORA_RELEASE=rawhide IGNORE_ARGS="--ignore-category low --ignore-high=libcryptsetup-rs-sys" make -f Makefile check-fedora-versions 63 | COMPARE_FEDORA_VERSIONS=$GITHUB_WORKSPACE/ci/dependency_management/compare_fedora_versions FEDORA_RELEASE=f43 IGNORE_ARGS="--ignore-category low --ignore-high=libcryptsetup-rs-sys" make -f Makefile check-fedora-versions 64 | COMPARE_FEDORA_VERSIONS=$GITHUB_WORKSPACE/ci/dependency_management/compare_fedora_versions FEDORA_RELEASE=f42 IGNORE_ARGS="--ignore-category low --ignore-high=libcryptsetup-rs-sys" make -f Makefile check-fedora-versions 65 | working-directory: libcryptsetup-rs 66 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: libcryptsetup nightly 3 | 4 | # yamllint disable-line rule:truthy 5 | on: 6 | schedule: 7 | - cron: 10 3 * * * 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | jobs: 13 | audit: 14 | runs-on: ubuntu-22.04 15 | steps: 16 | - name: Install git 17 | run: sudo apt-get install git 18 | - uses: actions/checkout@v6 19 | with: 20 | persist-credentials: false 21 | - uses: dtolnay/rust-toolchain@master 22 | with: 23 | components: cargo 24 | toolchain: 1.92.0 # CURRENT DEVELOPMENT RUST TOOLCHAIN 25 | - name: Install dependencies 26 | run: sudo apt-get install libcryptsetup-dev 27 | - uses: baptiste0928/cargo-install@v3 28 | with: 29 | crate: cargo-audit 30 | - name: Run audit check 31 | run: make audit 32 | 33 | checks-with-ci-repo: 34 | runs-on: ubuntu-22.04 35 | container: 36 | image: fedora:42 # CURRENT DEVELOPMENT ENVIRONMENT 37 | steps: 38 | - name: Install git 39 | run: dnf install -y git 40 | - uses: actions/checkout@v6 41 | with: 42 | path: libcryptsetup-rs 43 | persist-credentials: false 44 | - name: Install dependencies for Fedora 45 | run: > 46 | dnf install -y 47 | clang 48 | cryptsetup-devel 49 | curl 50 | make 51 | openssl-devel 52 | python-requests 53 | python-semantic_version 54 | - uses: dtolnay/rust-toolchain@master 55 | with: 56 | components: cargo 57 | toolchain: 1.92.0 # CURRENT DEVELOPMENT RUST TOOLCHAIN 58 | - name: Check out ci repo 59 | uses: actions/checkout@v6 60 | with: 61 | path: ci 62 | repository: stratis-storage/ci 63 | persist-credentials: false 64 | - name: Run comparisons of version specs with -sys package 65 | # yamllint disable rule:line-length 66 | run: | 67 | COMPARE_FEDORA_VERSIONS=$GITHUB_WORKSPACE/ci/dependency_management/compare_fedora_versions FEDORA_RELEASE=rawhide make -f ../Makefile check-fedora-versions 68 | COMPARE_FEDORA_VERSIONS=$GITHUB_WORKSPACE/ci/dependency_management/compare_fedora_versions FEDORA_RELEASE=f43 make -f ../Makefile check-fedora-versions 69 | COMPARE_FEDORA_VERSIONS=$GITHUB_WORKSPACE/ci/dependency_management/compare_fedora_versions FEDORA_RELEASE=f42 make -f ../Makefile check-fedora-versions 70 | working-directory: libcryptsetup-rs/libcryptsetup-rs-sys 71 | - name: Run comparisons of version specs with package 72 | # yamllint disable rule:line-length 73 | run: | 74 | COMPARE_FEDORA_VERSIONS=$GITHUB_WORKSPACE/ci/dependency_management/compare_fedora_versions FEDORA_RELEASE=rawhide make -f Makefile check-fedora-versions 75 | COMPARE_FEDORA_VERSIONS=$GITHUB_WORKSPACE/ci/dependency_management/compare_fedora_versions FEDORA_RELEASE=f43 make -f Makefile check-fedora-versions 76 | COMPARE_FEDORA_VERSIONS=$GITHUB_WORKSPACE/ci/dependency_management/compare_fedora_versions FEDORA_RELEASE=f42 make -f Makefile check-fedora-versions 77 | working-directory: libcryptsetup-rs 78 | 79 | semver-checks: 80 | runs-on: ubuntu-latest 81 | steps: 82 | - uses: actions/checkout@v6 83 | with: 84 | persist-credentials: false 85 | - name: Install dependencies 86 | run: | 87 | sudo apt-get -q update 88 | sudo apt-get -y install libcryptsetup-dev 89 | - name: Do semantic version checks on libcryptsetup-rs-sys 90 | uses: obi1kenobi/cargo-semver-checks-action@v2 91 | with: 92 | verbose: true 93 | rust-toolchain: 1.92.0 # CURRENT DEVELOPMENT RUST TOOLCHAIN 94 | manifest-path: libcryptsetup-rs-sys 95 | - name: Do semantic version checks 96 | uses: obi1kenobi/cargo-semver-checks-action@v2 97 | with: 98 | verbose: true 99 | rust-toolchain: 1.92.0 # CURRENT DEVELOPMENT RUST TOOLCHAIN 100 | -------------------------------------------------------------------------------- /src/tests/reencrypt.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use crate::{ 6 | consts::{ 7 | flags::{CryptActivate, CryptDeactivate, CryptReencrypt, CryptVolumeKey}, 8 | vals::{CryptReencryptDirectionInfo, CryptReencryptModeInfo, EncryptionFormat}, 9 | }, 10 | device::CryptInit, 11 | get_sector_size, 12 | tests::loopback, 13 | CryptParamsLuks2, CryptParamsReencrypt, Either, 14 | }; 15 | 16 | pub fn test_reencrypt_by_password() { 17 | loopback::use_loopback( 18 | 50 * 1024 * 1024, 19 | super::format_with_zeros(), 20 | super::do_cleanup(), 21 | |dev_path, _file_path| { 22 | let mut dev = CryptInit::init(dev_path).unwrap(); 23 | dev.context_handle() 24 | .format::<()>( 25 | EncryptionFormat::Luks2, 26 | ("aes", "xts-plain"), 27 | None, 28 | Either::Right(512 / 8), 29 | None, 30 | ) 31 | .unwrap(); 32 | 33 | dev.keyslot_handle() 34 | .add_by_key( 35 | None, 36 | None, 37 | "thisisatest".as_bytes(), 38 | CryptVolumeKey::empty(), 39 | ) 40 | .unwrap(); 41 | 42 | let new_keyslot = dev 43 | .keyslot_handle() 44 | .add_by_key( 45 | None, 46 | Some(Either::Right(512 / 8)), 47 | "thisisatest".as_bytes(), 48 | CryptVolumeKey::NO_SEGMENT, 49 | ) 50 | .unwrap(); 51 | 52 | dev.activate_handle() 53 | .activate_by_passphrase( 54 | Some("test-device"), 55 | None, 56 | "thisisatest".as_bytes(), 57 | CryptActivate::empty(), 58 | ) 59 | .unwrap(); 60 | 61 | let size = match get_sector_size(Some(&mut dev)) { 62 | i if i < 0 => panic!("Received error: {i:?}"), 63 | i => i as u32, 64 | }; 65 | let cipher = dev.status_handle().get_cipher().unwrap(); 66 | let cipher_mode = dev.status_handle().get_cipher_mode().unwrap(); 67 | 68 | dev.reencrypt_handle() 69 | .reencrypt_init_by_passphrase( 70 | Some("test-device"), 71 | "thisisatest".as_bytes(), 72 | None, 73 | Some(new_keyslot), 74 | Some((&cipher, &cipher_mode)), 75 | CryptParamsReencrypt { 76 | mode: CryptReencryptModeInfo::Reencrypt, 77 | direction: CryptReencryptDirectionInfo::Forward, 78 | resilience: "checksum".to_string(), 79 | hash: "sha256".to_string(), 80 | data_shift: 0, 81 | max_hotzone_size: 0, 82 | device_size: 0, 83 | luks2: Some(CryptParamsLuks2 { 84 | data_alignment: 0, 85 | data_device: None, 86 | integrity: None, 87 | integrity_params: None, 88 | pbkdf: None, 89 | label: None, 90 | sector_size: size, 91 | subsystem: None, 92 | }), 93 | flags: CryptReencrypt::empty(), 94 | }, 95 | ) 96 | .unwrap(); 97 | 98 | dev.reencrypt_handle().reencrypt2::<()>(None, None).unwrap(); 99 | 100 | dev.activate_handle() 101 | .deactivate("test-device", CryptDeactivate::empty()) 102 | .unwrap(); 103 | }, 104 | ) 105 | } 106 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | //! This is a wrapper library for libcryptsetup. The intention is to provide as much safety as 6 | //! possible when crossing FFI boundaries to the cryptsetup C library. 7 | 8 | // Keyfile reading functions are supported through a workaround in these bindings due 9 | // to how memory is handled in these functions - memory for keys is allocated 10 | // and the corresponding free functions are not part of the public API. 11 | // The function is copied and pasted from libcryptsetup and compiled into the bindings 12 | // for now to work around this. This will be supported by libcryptsetup at a later 13 | // time. 14 | 15 | pub use either::Either; 16 | 17 | #[macro_use] 18 | mod macros; 19 | 20 | mod activate; 21 | mod backup; 22 | pub mod consts; 23 | mod context; 24 | mod debug; 25 | mod device; 26 | mod err; 27 | mod format; 28 | mod key; 29 | mod keyfile; 30 | mod keyslot; 31 | mod log; 32 | mod luks2; 33 | mod mem; 34 | mod runtime; 35 | mod settings; 36 | mod status; 37 | #[cfg(test)] 38 | mod tests; 39 | mod wipe; 40 | 41 | #[cfg(cryptsetup23supported)] 42 | pub use crate::mem::{SafeBorrowedMemZero, SafeMemzero, SafeOwnedMemZero}; 43 | pub use crate::{ 44 | activate::CryptActivationHandle, 45 | backup::CryptBackupHandle, 46 | context::CryptContextHandle, 47 | debug::set_debug_level, 48 | device::{CryptDevice, CryptInit}, 49 | err::LibcryptErr, 50 | format::{ 51 | CryptFormatHandle, CryptParamsIntegrity, CryptParamsIntegrityRef, CryptParamsLoopaes, 52 | CryptParamsLoopaesRef, CryptParamsLuks1, CryptParamsLuks1Ref, CryptParamsLuks2, 53 | CryptParamsLuks2Ref, CryptParamsPlain, CryptParamsPlainRef, CryptParamsTcrypt, 54 | CryptParamsTcryptRef, CryptParamsVerity, CryptParamsVerityRef, 55 | }, 56 | key::CryptVolumeKeyHandle, 57 | keyfile::{CryptKeyfileContents, CryptKeyfileHandle}, 58 | keyslot::CryptKeyslotHandle, 59 | log::{log, set_log_callback}, 60 | luks2::{ 61 | flags::CryptLuks2FlagsHandle, 62 | reencrypt::{CryptLuks2ReencryptHandle, CryptParamsReencrypt, CryptParamsReencryptRef}, 63 | token::{register, CryptLuks2TokenHandle, CryptTokenInfo, TokenInput}, 64 | }, 65 | mem::SafeMemHandle, 66 | runtime::{ActiveDevice, CryptRuntimeHandle}, 67 | settings::{CryptPbkdfType, CryptPbkdfTypeRef, CryptSettingsHandle}, 68 | status::{get_sector_size, status, CryptDeviceStatusHandle}, 69 | wipe::CryptWipeHandle, 70 | }; 71 | 72 | /// Re-exports `libc` types in API 73 | pub use libc::{c_int, c_uint, size_t}; 74 | 75 | /// Result type to be used with `libcryptsetup-rs` 76 | pub type Result = std::result::Result; 77 | 78 | #[cfg(feature = "mutex")] 79 | static MUTEX: std::sync::LazyLock = 80 | std::sync::LazyLock::new(per_thread_mutex::PerThreadMutex::default); 81 | 82 | #[cfg(not(feature = "mutex"))] 83 | static THREAD_ID: std::sync::LazyLock = 84 | std::sync::LazyLock::new(|| std::thread::current().id()); 85 | 86 | #[cfg(test)] 87 | mod test { 88 | use crate::tests; 89 | 90 | #[ignore] 91 | #[test] 92 | fn test_encrypt_by_password() { 93 | tests::encrypt::test_encrypt_by_password(); 94 | } 95 | 96 | #[ignore] 97 | #[test] 98 | #[cfg(cryptsetup24supported)] 99 | fn test_reencrypt_by_password() { 100 | tests::reencrypt::test_reencrypt_by_password(); 101 | } 102 | 103 | #[ignore] 104 | #[test] 105 | fn test_encrypt_by_keyfile() { 106 | tests::encrypt::test_encrypt_by_keyfile(); 107 | } 108 | 109 | #[ignore] 110 | #[test] 111 | fn test_encrypt_by_password_without_explicit_format() { 112 | tests::encrypt::test_encrypt_by_password_without_explicit_format(); 113 | } 114 | 115 | #[ignore] 116 | #[test] 117 | fn test_unencrypted() { 118 | tests::encrypt::test_unencrypted(); 119 | } 120 | 121 | #[ignore] 122 | #[test] 123 | fn test_crypt_setup_free_exists() { 124 | tests::keyfile::test_keyfile_cleanup(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /libcryptsetup-rs-sys/header.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const uint32_t crypt_activate_readonly = CRYPT_ACTIVATE_READONLY; 4 | const uint32_t crypt_activate_no_uuid = CRYPT_ACTIVATE_NO_UUID; 5 | const uint32_t crypt_activate_shared = CRYPT_ACTIVATE_SHARED; 6 | const uint32_t crypt_activate_allow_discards = CRYPT_ACTIVATE_ALLOW_DISCARDS; 7 | const uint32_t crypt_activate_private = CRYPT_ACTIVATE_PRIVATE; 8 | const uint32_t crypt_activate_corrupted = CRYPT_ACTIVATE_CORRUPTED; 9 | const uint32_t crypt_activate_same_cpu_crypt = CRYPT_ACTIVATE_SAME_CPU_CRYPT; 10 | const uint32_t crypt_activate_submit_from_crypt_cpus= CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS; 11 | const uint32_t crypt_activate_ignore_corruption = CRYPT_ACTIVATE_IGNORE_CORRUPTION; 12 | const uint32_t crypt_activate_restart_on_corruption = CRYPT_ACTIVATE_RESTART_ON_CORRUPTION; 13 | const uint32_t crypt_activate_ignore_zero_blocks = CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS; 14 | const uint32_t crypt_activate_keyring_key= CRYPT_ACTIVATE_KEYRING_KEY; 15 | const uint32_t crypt_activate_no_journal = CRYPT_ACTIVATE_NO_JOURNAL; 16 | const uint32_t crypt_activate_recovery = CRYPT_ACTIVATE_RECOVERY; 17 | const uint32_t crypt_activate_ignore_persistent = CRYPT_ACTIVATE_IGNORE_PERSISTENT; 18 | const uint32_t crypt_activate_check_at_most_once = CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE; 19 | const uint32_t crypt_activate_allow_unbound_key = CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY; 20 | const uint32_t crypt_activate_recalculate = CRYPT_ACTIVATE_RECALCULATE; 21 | const uint32_t crypt_activate_refresh = CRYPT_ACTIVATE_REFRESH; 22 | const uint32_t crypt_activate_serialize_memory_hard_pbkdf = CRYPT_ACTIVATE_SERIALIZE_MEMORY_HARD_PBKDF; 23 | const uint32_t crypt_activate_no_journal_bitmap = CRYPT_ACTIVATE_NO_JOURNAL_BITMAP; 24 | #ifdef CRYPT_ACTIVATE_SUSPENDED 25 | const uint32_t crypt_activate_suspended = CRYPT_ACTIVATE_SUSPENDED; 26 | #endif 27 | #ifdef CRYPT_ACTIVATE_IV_LARGE_SECTORS 28 | const uint32_t crypt_activate_iv_large_sectors = CRYPT_ACTIVATE_IV_LARGE_SECTORS; 29 | #endif 30 | #ifdef CRYPT_ACTIVATE_PANIC_ON_CORRUPTION 31 | const uint32_t crypt_activate_panic_on_corruption = CRYPT_ACTIVATE_PANIC_ON_CORRUPTION; 32 | #endif 33 | #ifdef CRYPT_ACTIVATE_NO_READ_WORKQUEUE 34 | const uint32_t crypt_activate_no_read_workqueue = CRYPT_ACTIVATE_NO_READ_WORKQUEUE; 35 | #endif 36 | #ifdef CRYPT_ACTIVATE_NO_WRITE_WORKQUEUE 37 | const uint32_t crypt_activate_no_write_workqueue = CRYPT_ACTIVATE_NO_WRITE_WORKQUEUE; 38 | #endif 39 | #ifdef CRYPT_ACTIVATE_RECALCULATE_RESET 40 | const uint32_t crypt_activate_recalculate_reset = CRYPT_ACTIVATE_RECALCULATE_RESET; 41 | #endif 42 | 43 | const uint32_t crypt_deactivate_deferred = CRYPT_DEACTIVATE_DEFERRED; 44 | const uint32_t crypt_deactivate_force = CRYPT_DEACTIVATE_FORCE; 45 | 46 | const uint32_t crypt_verity_no_header = CRYPT_VERITY_NO_HEADER; 47 | const uint32_t crypt_verity_check_hash = CRYPT_VERITY_CHECK_HASH; 48 | const uint32_t crypt_verity_create_hash = CRYPT_VERITY_CREATE_HASH; 49 | 50 | const uint32_t crypt_tcrypt_legacy_modes = CRYPT_TCRYPT_LEGACY_MODES; 51 | const uint32_t crypt_tcrypt_hidden_header = CRYPT_TCRYPT_HIDDEN_HEADER; 52 | const uint32_t crypt_tcrypt_backup_header = CRYPT_TCRYPT_BACKUP_HEADER; 53 | const uint32_t crypt_tcrypt_system_header = CRYPT_TCRYPT_SYSTEM_HEADER; 54 | const uint32_t crypt_tcrypt_vera_modes = CRYPT_TCRYPT_VERA_MODES; 55 | 56 | const uint32_t crypt_keyfile_stop_eol = CRYPT_KEYFILE_STOP_EOL; 57 | 58 | const uint32_t crypt_volume_key_no_segment = CRYPT_VOLUME_KEY_NO_SEGMENT; 59 | const uint32_t crypt_volume_key_set = CRYPT_VOLUME_KEY_SET; 60 | const uint32_t crypt_volume_key_digest_reuse = CRYPT_VOLUME_KEY_DIGEST_REUSE; 61 | 62 | const uint32_t crypt_requirement_offline_reencrypt = CRYPT_REQUIREMENT_OFFLINE_REENCRYPT; 63 | const uint32_t crypt_requirement_online_reencrypt = CRYPT_REQUIREMENT_ONLINE_REENCRYPT; 64 | const uint32_t crypt_requirement_unknown = CRYPT_REQUIREMENT_UNKNOWN; 65 | 66 | const uint32_t crypt_reencrypt_recovery = CRYPT_REENCRYPT_RECOVERY; 67 | const uint32_t crypt_reencrypt_resume_only = CRYPT_REENCRYPT_RESUME_ONLY; 68 | const uint32_t crypt_reencrypt_initialize_only = CRYPT_REENCRYPT_INITIALIZE_ONLY; 69 | const uint32_t crypt_reencrypt_move_first_segment = CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT; 70 | 71 | const uint32_t crypt_pbkdf_iter_time_set = CRYPT_PBKDF_ITER_TIME_SET; 72 | const uint32_t crypt_pbkdf_no_benchmark = CRYPT_PBKDF_NO_BENCHMARK; 73 | 74 | const uint32_t crypt_wipe_no_direct_io = CRYPT_WIPE_NO_DIRECT_IO; 75 | -------------------------------------------------------------------------------- /examples/format-luks2-with-token.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env::args, 3 | path::{Path, PathBuf}, 4 | }; 5 | 6 | use libcryptsetup_rs::{ 7 | c_uint, 8 | consts::{flags::CryptVolumeKey, vals::EncryptionFormat}, 9 | CryptInit, LibcryptErr, TokenInput, 10 | }; 11 | 12 | #[macro_use] 13 | extern crate serde_json; 14 | use uuid::Uuid; 15 | 16 | fn usage() -> &'static str { 17 | "Usage: format-luks2-with-token \n\ 18 | \tDEVICE_PATH: Path to device to format\n\ 19 | \tKEY_DESCRIPTION: Kernel keyring key description\n\ 20 | \tKEY_DATA: Kernel keyring key data\n\ 21 | \topenable|unopenable: openable to write the openable LUKS2 token to the keyslot" 22 | } 23 | 24 | enum Openable { 25 | Yes, 26 | No, 27 | } 28 | 29 | impl TryFrom<&String> for Openable { 30 | type Error = &'static str; 31 | 32 | fn try_from(v: &String) -> Result { 33 | match v.as_str() { 34 | "openable" => Ok(Openable::Yes), 35 | "unopenable" => Ok(Openable::No), 36 | _ => Err("Unrecognized option for whether device should be openable"), 37 | } 38 | } 39 | } 40 | 41 | fn parse_args() -> Result<(PathBuf, String, String, Openable), &'static str> { 42 | let args: Vec<_> = args().collect(); 43 | if args.len() != usage().split('\n').count() { 44 | println!("{}", usage()); 45 | return Err("Incorrect arguments provided"); 46 | } 47 | 48 | let device_string = args 49 | .get(1) 50 | .ok_or("Could not get the device path for the device node to be encrypted")?; 51 | let device_path = PathBuf::from(device_string); 52 | if !device_path.exists() { 53 | return Err("Device does not exist"); 54 | } 55 | 56 | let key_description = args 57 | .get(2) 58 | .ok_or("No kernel keyring key description was provided")?; 59 | 60 | let key_data = args 61 | .get(3) 62 | .ok_or("No kernel keyring key data was provided")?; 63 | 64 | let openable_string = args 65 | .get(4) 66 | .ok_or("Could not determine whether device should be openable or not")?; 67 | let openable = Openable::try_from(openable_string)?; 68 | 69 | Ok(( 70 | device_path, 71 | key_description.to_string(), 72 | key_data.to_string(), 73 | openable, 74 | )) 75 | } 76 | 77 | fn format(dev: &Path, key_data: &str) -> Result { 78 | let mut device = CryptInit::init(dev)?; 79 | device.context_handle().format::<()>( 80 | EncryptionFormat::Luks2, 81 | ("aes", "xts-plain"), 82 | None, 83 | libcryptsetup_rs::Either::Right(256 / 8), 84 | None, 85 | )?; 86 | let keyslot = device.keyslot_handle().add_by_key( 87 | None, 88 | None, 89 | key_data.as_bytes(), 90 | CryptVolumeKey::empty(), 91 | )?; 92 | 93 | Ok(keyslot) 94 | } 95 | 96 | fn luks2_token_handler( 97 | dev: &Path, 98 | key_description: &str, 99 | keyslot: c_uint, 100 | ) -> Result<(), LibcryptErr> { 101 | let mut device = CryptInit::init(dev)?; 102 | device 103 | .context_handle() 104 | .load::<()>(Some(EncryptionFormat::Luks2), None)?; 105 | let mut token = device.token_handle(); 106 | let token_num = token.luks2_keyring_set(None, key_description)?; 107 | token.assign_keyslot(token_num, Some(keyslot))?; 108 | Ok(()) 109 | } 110 | 111 | fn proto_token_handler(dev: &Path, key_description: &str) -> Result<(), LibcryptErr> { 112 | let mut device = CryptInit::init(dev)?; 113 | device 114 | .context_handle() 115 | .load::<()>(Some(EncryptionFormat::Luks2), None)?; 116 | let mut token = device.token_handle(); 117 | let _ = token.json_set(TokenInput::AddToken(&json!({ 118 | "type": "proto", 119 | "keyslots": [], 120 | "a_uuid": Uuid::new_v4().as_simple().to_string(), 121 | "key_description": key_description 122 | }))); 123 | Ok(()) 124 | } 125 | 126 | fn main() -> Result<(), String> { 127 | let (path, key_description, key_data, openable) = parse_args()?; 128 | let keyslot = format(&path, &key_data).map_err(|e| e.to_string())?; 129 | luks2_token_handler(&path, &key_description, keyslot).map_err(|e| e.to_string())?; 130 | if let Openable::Yes = openable { 131 | proto_token_handler(&path, &key_description).map_err(|e| e.to_string())?; 132 | }; 133 | Ok(()) 134 | } 135 | -------------------------------------------------------------------------------- /examples/cryptsetup-luks2.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{ 6 | env::args, 7 | io, 8 | path::{Path, PathBuf}, 9 | }; 10 | 11 | use libcryptsetup_rs::{ 12 | consts::{ 13 | flags::{CryptActivate, CryptDeactivate, CryptVolumeKey}, 14 | vals::EncryptionFormat, 15 | }, 16 | CryptInit, LibcryptErr, 17 | }; 18 | 19 | enum CryptCommand { 20 | Encrypt(PathBuf), 21 | Open(PathBuf, String), 22 | Deactivate(PathBuf, String), 23 | } 24 | 25 | fn parse_args() -> Result { 26 | let mut args = args(); 27 | let _ = args.next(); 28 | let command = args.next(); 29 | match command.as_deref() { 30 | Some("encrypt") => { 31 | let dev = PathBuf::from(match args.next() { 32 | Some(p) => p, 33 | None => { 34 | return Err(LibcryptErr::Other( 35 | "Device path for device to be encrypted is required".to_string(), 36 | )) 37 | } 38 | }); 39 | if dev.exists() { 40 | Ok(CryptCommand::Encrypt(dev)) 41 | } else { 42 | Err(LibcryptErr::IOError(io::Error::from( 43 | io::ErrorKind::NotFound, 44 | ))) 45 | } 46 | } 47 | Some("open") => { 48 | let dev = PathBuf::from(match args.next() { 49 | Some(p) => p, 50 | None => { 51 | return Err(LibcryptErr::Other( 52 | "Device path for device to be opened is required".to_string(), 53 | )) 54 | } 55 | }); 56 | if !dev.exists() { 57 | return Err(LibcryptErr::IOError(io::Error::from( 58 | io::ErrorKind::NotFound, 59 | ))); 60 | } 61 | let name = args.next().ok_or_else(|| { 62 | LibcryptErr::Other("Name for mapped device is required".to_string()) 63 | })?; 64 | Ok(CryptCommand::Open(dev, name)) 65 | } 66 | Some("close") => { 67 | let dev = PathBuf::from(match args.next() { 68 | Some(p) => p, 69 | None => { 70 | return Err(LibcryptErr::Other( 71 | "Device path for device to be closed is required".to_string(), 72 | )) 73 | } 74 | }); 75 | if !dev.exists() { 76 | return Err(LibcryptErr::IOError(io::Error::from( 77 | io::ErrorKind::NotFound, 78 | ))); 79 | } 80 | let name = args.next().ok_or_else(|| { 81 | LibcryptErr::Other("Name for mapped device is required".to_string()) 82 | })?; 83 | Ok(CryptCommand::Deactivate(dev, name)) 84 | } 85 | Some(s) => Err(LibcryptErr::Other(format!("Unrecognized command {s}"))), 86 | None => Err(LibcryptErr::Other("Missing command".to_string())), 87 | } 88 | } 89 | 90 | fn encrypt(path: &Path) -> Result<(), LibcryptErr> { 91 | let mut device = CryptInit::init(path)?; 92 | device.context_handle().format::<()>( 93 | EncryptionFormat::Luks2, 94 | ("aes", "xts-plain"), 95 | None, 96 | libcryptsetup_rs::Either::Right(256 / 8), 97 | None, 98 | )?; 99 | device 100 | .keyslot_handle() 101 | .add_by_key(None, None, b"changeme", CryptVolumeKey::empty())?; 102 | Ok(()) 103 | } 104 | 105 | fn activate(path: &Path, name: &str) -> Result<(), LibcryptErr> { 106 | let mut device = CryptInit::init(path)?; 107 | device 108 | .context_handle() 109 | .load::<()>(Some(EncryptionFormat::Luks2), None)?; 110 | device.activate_handle().activate_by_passphrase( 111 | Some(name), 112 | None, 113 | b"changeme", 114 | CryptActivate::empty(), 115 | )?; 116 | Ok(()) 117 | } 118 | 119 | fn deactivate(path: &Path, name: &str) -> Result<(), LibcryptErr> { 120 | let mut device = CryptInit::init(path)?; 121 | device 122 | .context_handle() 123 | .load::<()>(Some(EncryptionFormat::Luks2), None)?; 124 | device 125 | .activate_handle() 126 | .deactivate(name, CryptDeactivate::empty())?; 127 | Ok(()) 128 | } 129 | 130 | fn main() -> Result<(), LibcryptErr> { 131 | let args = parse_args()?; 132 | if let CryptCommand::Encrypt(ref path) = args { 133 | encrypt(path)?; 134 | } else if let CryptCommand::Open(ref path, ref name) = args { 135 | activate(path, name)?; 136 | } else if let CryptCommand::Deactivate(ref path, ref name) = args { 137 | deactivate(path, name)?; 138 | } 139 | Ok(()) 140 | } 141 | -------------------------------------------------------------------------------- /src/consts/flags.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use bitflags::bitflags; 6 | 7 | bitflags! { 8 | /// Crypt device activation flags. 9 | pub struct CryptActivate: u32 { 10 | const READONLY = libcryptsetup_rs_sys::crypt_activate_readonly; 11 | const NO_UUID = libcryptsetup_rs_sys::crypt_activate_no_uuid; 12 | const SHARED = libcryptsetup_rs_sys::crypt_activate_shared; 13 | const ALLOW_DISCARDS = libcryptsetup_rs_sys::crypt_activate_allow_discards; 14 | const PRIVATE = libcryptsetup_rs_sys::crypt_activate_private; 15 | const CORRUPTED = libcryptsetup_rs_sys::crypt_activate_corrupted; 16 | const SAME_CPU_CRYPT = libcryptsetup_rs_sys::crypt_activate_same_cpu_crypt; 17 | const SUBMIT_FROM_CRYPT_CPUS = libcryptsetup_rs_sys::crypt_activate_submit_from_crypt_cpus; 18 | const IGNORE_CORRUPTION = libcryptsetup_rs_sys::crypt_activate_ignore_corruption; 19 | const RESTART_ON_CORRUPTION = libcryptsetup_rs_sys::crypt_activate_restart_on_corruption; 20 | const IGNORE_ZERO_BLOCKS = libcryptsetup_rs_sys::crypt_activate_ignore_zero_blocks; 21 | const KEYRING_KEY = libcryptsetup_rs_sys::crypt_activate_keyring_key; 22 | const NO_JOURNAL = libcryptsetup_rs_sys::crypt_activate_no_journal; 23 | const RECOVERY = libcryptsetup_rs_sys::crypt_activate_recovery; 24 | const IGNORE_PERSISTENT = libcryptsetup_rs_sys::crypt_activate_ignore_persistent; 25 | const CHECK_AT_MOST_ONCE = libcryptsetup_rs_sys::crypt_activate_check_at_most_once; 26 | const ALLOW_UNBOUND_KEY = libcryptsetup_rs_sys::crypt_activate_allow_unbound_key; 27 | const RECALCULATE = libcryptsetup_rs_sys::crypt_activate_recalculate; 28 | const REFRESH = libcryptsetup_rs_sys::crypt_activate_refresh; 29 | const SERIALIZE_MEMORY_HARD_PBKDF = libcryptsetup_rs_sys::crypt_activate_serialize_memory_hard_pbkdf; 30 | const NO_JOURNAL_BITMAP = libcryptsetup_rs_sys::crypt_activate_no_journal_bitmap; 31 | #[cfg(cryptsetup23supported)] 32 | const SUSPENDED = libcryptsetup_rs_sys::crypt_activate_suspended; 33 | #[cfg(cryptsetup24supported)] 34 | const IV_LARGE_SECTORS = libcryptsetup_rs_sys::crypt_activate_iv_large_sectors; 35 | #[cfg(cryptsetup24supported)] 36 | const PANIC_ON_CORRUPTION = libcryptsetup_rs_sys::crypt_activate_panic_on_corruption; 37 | #[cfg(cryptsetup24supported)] 38 | const NO_READ_WORKQUEUE = libcryptsetup_rs_sys::crypt_activate_no_read_workqueue; 39 | #[cfg(cryptsetup24supported)] 40 | const NO_WRITE_WORKQUEUE = libcryptsetup_rs_sys::crypt_activate_no_write_workqueue; 41 | #[cfg(cryptsetup24supported)] 42 | const RECALCULATE_RESET = libcryptsetup_rs_sys::crypt_activate_recalculate_reset; 43 | } 44 | } 45 | 46 | bitflags! { 47 | /// Flags for crypt deactivate operations 48 | pub struct CryptDeactivate: u32 { 49 | const DEFERRED = libcryptsetup_rs_sys::crypt_deactivate_deferred; 50 | const FORCE = libcryptsetup_rs_sys::crypt_deactivate_force; 51 | } 52 | } 53 | 54 | bitflags! { 55 | /// Verity format flags 56 | pub struct CryptVerity: u32 { 57 | const NO_HEADER = libcryptsetup_rs_sys::crypt_verity_no_header; 58 | const CHECK_HASH = libcryptsetup_rs_sys::crypt_verity_check_hash; 59 | const CREATE_HASH = libcryptsetup_rs_sys::crypt_verity_create_hash; 60 | } 61 | } 62 | 63 | bitflags! { 64 | /// tcrypt format flags 65 | pub struct CryptTcrypt: u32 { 66 | const LEGACY_MODES = libcryptsetup_rs_sys::crypt_tcrypt_legacy_modes; 67 | const HIDDEN_HEADER = libcryptsetup_rs_sys::crypt_tcrypt_hidden_header; 68 | const BACKUP_HEADER = libcryptsetup_rs_sys::crypt_tcrypt_backup_header; 69 | const SYSTEM_HEADER = libcryptsetup_rs_sys::crypt_tcrypt_system_header; 70 | const VERA_MODES = libcryptsetup_rs_sys::crypt_tcrypt_vera_modes; 71 | } 72 | } 73 | 74 | bitflags! { 75 | /// Flags for reading keyfiles 76 | pub struct CryptKeyfile: u32 { 77 | const STOP_EOL = libcryptsetup_rs_sys::crypt_keyfile_stop_eol; 78 | } 79 | } 80 | 81 | bitflags! { 82 | /// Flags for tunable options when operating with volume keys 83 | pub struct CryptVolumeKey: u32 { 84 | const NO_SEGMENT = libcryptsetup_rs_sys::crypt_volume_key_no_segment; 85 | const SET = libcryptsetup_rs_sys::crypt_volume_key_set; 86 | const DIGEST_REUSE = libcryptsetup_rs_sys::crypt_volume_key_digest_reuse; 87 | } 88 | } 89 | 90 | bitflags! { 91 | /// Requirement flags 92 | pub struct CryptRequirement: u32 { 93 | const OFFLINE_REENCRYPT = libcryptsetup_rs_sys::crypt_requirement_offline_reencrypt; 94 | const ONLINE_REENCRYPT = libcryptsetup_rs_sys::crypt_requirement_online_reencrypt; 95 | const UNKNOWN = libcryptsetup_rs_sys::crypt_requirement_unknown; 96 | } 97 | } 98 | 99 | bitflags! { 100 | /// Reencryption flags 101 | pub struct CryptReencrypt: u32 { 102 | const INITIALIZE_ONLY = libcryptsetup_rs_sys::crypt_reencrypt_initialize_only; 103 | const MOVE_FIRST_SEGMENT = libcryptsetup_rs_sys::crypt_reencrypt_move_first_segment; 104 | const RESUME_ONLY = libcryptsetup_rs_sys::crypt_reencrypt_resume_only; 105 | const RECOVERY = libcryptsetup_rs_sys::crypt_reencrypt_recovery; 106 | } 107 | } 108 | 109 | bitflags! { 110 | /// PBKDF flags 111 | pub struct CryptPbkdf: u32 { 112 | const ITER_TIME_SET = libcryptsetup_rs_sys::crypt_pbkdf_iter_time_set; 113 | const NO_BENCHMARK = libcryptsetup_rs_sys::crypt_pbkdf_no_benchmark; 114 | } 115 | } 116 | 117 | bitflags! { 118 | /// Flags for crypt wipe operations 119 | pub struct CryptWipe: u32 { 120 | const NO_DIRECT_IO = libcryptsetup_rs_sys::crypt_wipe_no_direct_io; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/mem.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::slice; 6 | 7 | use libc::c_void; 8 | 9 | #[cfg(cryptsetup23supported)] 10 | use crate::Result; 11 | 12 | macro_rules! define_handle { 13 | ($(#[$docs:meta])* $name:ident, $(#[$from_ptr_docs:meta])* from_ptr $(, $drop:expr)?) => { 14 | $(#[$docs])* 15 | #[cfg(cryptsetup23supported)] 16 | pub struct $name(*mut c_void, usize); 17 | 18 | #[cfg(cryptsetup23supported)] 19 | impl $name { 20 | $(#[$from_ptr_docs])* 21 | pub unsafe fn from_ptr(ptr: *mut c_void, size: usize) -> Self { 22 | $name(ptr, size) 23 | } 24 | } 25 | 26 | #[cfg(cryptsetup23supported)] 27 | impl Drop for $name { 28 | fn drop(&mut self) { 29 | self.safe_memzero(); 30 | $( 31 | #[allow(clippy::redundant_closure_call)] 32 | unsafe { $drop(self) }; 33 | )? 34 | } 35 | } 36 | }; 37 | } 38 | 39 | macro_rules! memzero { 40 | ($name:ident) => { 41 | #[cfg(cryptsetup23supported)] 42 | impl SafeMemzero for $name { 43 | fn safe_memzero(&mut self) { 44 | mutex!(libcryptsetup_rs_sys::crypt_safe_memzero(self.0, self.1)) 45 | } 46 | } 47 | }; 48 | } 49 | 50 | macro_rules! as_ref { 51 | ($name:ident) => { 52 | impl AsRef<[u8]> for $name { 53 | fn as_ref(&self) -> &[u8] { 54 | unsafe { slice::from_raw_parts(self.0.cast::(), self.1) } 55 | } 56 | } 57 | 58 | impl AsMut<[u8]> for $name { 59 | fn as_mut(&mut self) -> &mut [u8] { 60 | unsafe { slice::from_raw_parts_mut(self.0.cast::(), self.1) } 61 | } 62 | } 63 | }; 64 | } 65 | 66 | /// A trait to be implemented for a segment of memory that can be explicitly 67 | /// zeroed in a way that will not be optimized away by the compiler. 68 | #[cfg(cryptsetup23supported)] 69 | pub trait SafeMemzero { 70 | /// Zero the data in the buffer. To enable managed zeroing of a buffer, 71 | /// call this in a `Drop` implementation. 72 | fn safe_memzero(&mut self); 73 | } 74 | 75 | define_handle! { 76 | /// Handle for zeroing owned memory. "Owned" in this context refers to memory 77 | /// that has been allocated and stored in some kind of `char **` argument 78 | /// in the context of C FFI. This means that the memory has been allocated 79 | /// by standard C allocators and needs to be cleaned up by the caller. 80 | /// In the context of Rust, we would consider this owned by the current scope. 81 | /// 82 | /// # SECURITY WARNING 83 | /// 84 | /// Any pointer used with this *must point to memory allocated by* `libc::malloc` 85 | /// or any other function compatible with `libc::free`. If it has not been, 86 | /// you could cause memory corruption and security problems. 87 | SafeOwnedMemZero, 88 | /// Construct a safe memory handle from a pointer and a size. 89 | /// 90 | /// # Safety 91 | /// 92 | /// The pointer must point to memory allocated by `libc::malloc` or something 93 | /// compatible with `libc::free`. See the struct-level security warning for more 94 | /// information. The `size` argument also must match the length of the 95 | /// allocated block or memory corruption could occur. 96 | from_ptr, 97 | |self_: &mut SafeOwnedMemZero| { 98 | libc::free(self_.0); 99 | } 100 | } 101 | memzero!(SafeOwnedMemZero); 102 | #[cfg(cryptsetup23supported)] 103 | as_ref!(SafeOwnedMemZero); 104 | 105 | define_handle! { 106 | /// Handle for zeroing borrowed memory. "Borrowed" in this context refers to memory 107 | /// that will be cleaned up by some other scope and is not required to be freed 108 | /// by the caller. An example of this would be a `char *` pointer to kernel memory 109 | /// where the caller can access the memory but is not responsible for its 110 | /// allocation or deallocation. 111 | SafeBorrowedMemZero, 112 | /// Construct a safe memory handle from a pointer and a size. 113 | /// 114 | /// # Safety 115 | /// 116 | /// The length must match the length of the exposed memory block 117 | /// or memory corruption could occur. 118 | from_ptr 119 | } 120 | memzero!(SafeBorrowedMemZero); 121 | #[cfg(cryptsetup23supported)] 122 | as_ref!(SafeBorrowedMemZero); 123 | 124 | /// Handle to allocated memory from libcryptsetup 125 | pub struct SafeMemHandle(*mut c_void, usize); 126 | 127 | impl SafeMemHandle { 128 | pub(crate) unsafe fn from_ptr(ptr: *mut c_void, size: usize) -> Self { 129 | SafeMemHandle(ptr, size) 130 | } 131 | 132 | /// Allocate a block of memory that will be safely zeroed when deallocated 133 | /// by the `Drop` trait. 134 | #[cfg(cryptsetup23supported)] 135 | pub fn alloc(size: usize) -> Result { 136 | let ptr = ptr_to_result!(mutex!(libcryptsetup_rs_sys::crypt_safe_alloc(size)))?; 137 | Ok(SafeMemHandle(ptr, size)) 138 | } 139 | } 140 | 141 | // libcryptsetup uses standard C heap allocation to allocate the safe memory. As a 142 | // result, it is safe to send and access across threads. 143 | unsafe impl Send for SafeMemHandle {} 144 | 145 | impl Drop for SafeMemHandle { 146 | fn drop(&mut self) { 147 | mutex!(libcryptsetup_rs_sys::crypt_safe_free(self.0)) 148 | } 149 | } 150 | memzero!(SafeMemHandle); 151 | as_ref!(SafeMemHandle); 152 | 153 | #[cfg(all(test, cryptsetup23supported, feature = "mutex"))] 154 | mod test { 155 | use super::*; 156 | 157 | use std::io::Write; 158 | 159 | #[test] 160 | fn test_memzero() { 161 | let mut handle = SafeMemHandle::alloc(32).unwrap(); 162 | handle.as_mut().write_all(&[20; 32]).unwrap(); 163 | assert_eq!(&[20; 32], handle.as_ref()); 164 | handle.safe_memzero(); 165 | assert_eq!(&[0; 32], handle.as_ref()); 166 | } 167 | 168 | #[test] 169 | fn test_memzero_borrowed() { 170 | let mut slice = [0u8; 32]; 171 | let mut borrowed_handle = 172 | unsafe { SafeBorrowedMemZero::from_ptr(slice.as_mut_ptr().cast(), slice.len()) }; 173 | borrowed_handle.as_mut().write_all(&[33; 32]).unwrap(); 174 | assert_eq!(&[33; 32], borrowed_handle.as_ref()); 175 | std::mem::drop(borrowed_handle); 176 | assert_eq!(&[0u8; 32], &slice); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/activate.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{path::Path, ptr}; 6 | 7 | use libc::{c_int, c_uint}; 8 | 9 | use crate::{ 10 | consts::flags::{CryptActivate, CryptDeactivate}, 11 | device::CryptDevice, 12 | err::LibcryptErr, 13 | }; 14 | 15 | /// Handle for activation options 16 | pub struct CryptActivationHandle<'a> { 17 | reference: &'a mut CryptDevice, 18 | } 19 | 20 | impl<'a> CryptActivationHandle<'a> { 21 | pub(crate) fn new(reference: &'a mut CryptDevice) -> Self { 22 | CryptActivationHandle { reference } 23 | } 24 | 25 | /// Activate device by passphrase. 26 | /// 27 | /// A value of `None` for the name will only check the passphrase and will 28 | /// not activate the keyslot. 29 | pub fn activate_by_passphrase( 30 | &mut self, 31 | name: Option<&str>, 32 | keyslot: Option, 33 | passphrase: &[u8], 34 | flags: CryptActivate, 35 | ) -> Result { 36 | let name_cstring_option = match name { 37 | Some(n) => Some(to_cstring!(n)?), 38 | None => None, 39 | }; 40 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_activate_by_passphrase( 41 | self.reference.as_ptr(), 42 | match name_cstring_option { 43 | Some(ref cs) => cs.as_ptr(), 44 | None => ptr::null_mut(), 45 | }, 46 | keyslot 47 | .map(|k| k as c_int) 48 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_SLOT), 49 | to_byte_ptr!(passphrase), 50 | passphrase.len(), 51 | flags.bits(), 52 | ))) 53 | .map(|k| k as c_uint) 54 | } 55 | 56 | /// Activate device by key file 57 | pub fn activate_by_keyfile_device_offset( 58 | &mut self, 59 | name: Option<&str>, 60 | keyslot: Option, 61 | keyfile: &Path, 62 | keyfile_size: Option, 63 | keyfile_offset: u64, 64 | flags: CryptActivate, 65 | ) -> Result { 66 | let name_cstring_option = match name { 67 | Some(n) => Some(to_cstring!(n)?), 68 | None => None, 69 | }; 70 | let keyfile_cstring = path_to_cstring!(keyfile)?; 71 | errno_int_success!(mutex!( 72 | libcryptsetup_rs_sys::crypt_activate_by_keyfile_device_offset( 73 | self.reference.as_ptr(), 74 | match name_cstring_option { 75 | Some(ref cs) => cs.as_ptr(), 76 | None => ptr::null_mut(), 77 | }, 78 | keyslot 79 | .map(|k| k as c_int) 80 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_SLOT), 81 | keyfile_cstring.as_ptr(), 82 | match keyfile_size { 83 | Some(i) => i, 84 | None => std::fs::metadata(keyfile) 85 | .map_err(LibcryptErr::IOError)? 86 | .len() as crate::size_t, 87 | }, 88 | keyfile_offset, 89 | flags.bits(), 90 | ) 91 | )) 92 | .map(|k| k as c_uint) 93 | } 94 | 95 | /// Activate device by volume key 96 | pub fn activate_by_volume_key( 97 | &mut self, 98 | name: Option<&str>, 99 | volume_key: Option<&[u8]>, 100 | flags: CryptActivate, 101 | ) -> Result<(), LibcryptErr> { 102 | let name_cstring_option = match name { 103 | Some(n) => Some(to_cstring!(n)?), 104 | None => None, 105 | }; 106 | let (volume_key_ptr, volume_key_len) = match volume_key { 107 | Some(vk) => (to_byte_ptr!(vk), vk.len()), 108 | None => (ptr::null(), 0), 109 | }; 110 | errno!(mutex!(libcryptsetup_rs_sys::crypt_activate_by_volume_key( 111 | self.reference.as_ptr(), 112 | match name_cstring_option { 113 | Some(ref cs) => cs.as_ptr(), 114 | None => ptr::null_mut(), 115 | }, 116 | volume_key_ptr, 117 | volume_key_len, 118 | flags.bits(), 119 | ))) 120 | } 121 | 122 | /// Activeate device using passphrase in kernel keyring 123 | pub fn activate_by_keyring( 124 | &mut self, 125 | name: Option<&str>, 126 | key_description: &str, 127 | keyslot: Option, 128 | flags: CryptActivate, 129 | ) -> Result { 130 | let name_cstring_option = match name { 131 | Some(n) => Some(to_cstring!(n)?), 132 | None => None, 133 | }; 134 | let description_cstring = to_cstring!(key_description)?; 135 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_activate_by_keyring( 136 | self.reference.as_ptr(), 137 | match name_cstring_option { 138 | Some(ref cs) => cs.as_ptr(), 139 | None => ptr::null_mut(), 140 | }, 141 | description_cstring.as_ptr(), 142 | keyslot 143 | .map(|k| k as c_int) 144 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_SLOT), 145 | flags.bits(), 146 | ))) 147 | .map(|k| k as c_uint) 148 | } 149 | 150 | /// Deactivate crypt device 151 | pub fn deactivate(&mut self, name: &str, flags: CryptDeactivate) -> Result<(), LibcryptErr> { 152 | let name_cstring = to_cstring!(name)?; 153 | errno!(mutex!(libcryptsetup_rs_sys::crypt_deactivate_by_name( 154 | self.reference.as_ptr(), 155 | name_cstring.as_ptr(), 156 | flags.bits(), 157 | ))) 158 | } 159 | 160 | /// Set the keyring to link the volume key to on activation. 161 | /// 162 | /// If `keyring` is None, linking will be disabled. 163 | #[cfg(cryptsetup27supported)] 164 | pub fn set_keyring_to_link( 165 | &mut self, 166 | key_description: &str, 167 | old_key_description: Option<&str>, 168 | key_desc_type: Option<&str>, 169 | keyring: Option<&str>, 170 | ) -> Result<(), LibcryptErr> { 171 | let kd_cstring = to_cstring!(key_description)?; 172 | let old_kd_cstring = match old_key_description.as_ref() { 173 | Some(s) => Some(to_cstring!(s)?), 174 | None => None, 175 | }; 176 | let kd_type_cstring = match key_desc_type.as_ref() { 177 | Some(s) => Some(to_cstring!(s)?), 178 | None => None, 179 | }; 180 | let kr_cstring = match keyring.as_ref() { 181 | Some(s) => Some(to_cstring!(s)?), 182 | None => None, 183 | }; 184 | errno!(mutex!(libcryptsetup_rs_sys::crypt_set_keyring_to_link( 185 | self.reference.as_ptr(), 186 | kd_cstring.as_ptr(), 187 | old_kd_cstring 188 | .as_ref() 189 | .map(|s| s.as_ptr()) 190 | .unwrap_or(ptr::null()), 191 | kd_type_cstring 192 | .as_ref() 193 | .map(|s| s.as_ptr()) 194 | .unwrap_or(ptr::null()), 195 | kr_cstring 196 | .as_ref() 197 | .map(|s| s.as_ptr()) 198 | .unwrap_or(ptr::null()), 199 | ))) 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/status.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | #[cfg(cryptsetup24supported)] 6 | use std::ffi::CStr; 7 | use std::{os::raw::c_int, path::Path, ptr, str::FromStr}; 8 | 9 | use crate::{ 10 | consts::vals::CryptStatusInfo, 11 | device::CryptDevice, 12 | err::LibcryptErr, 13 | format::{CryptParamsIntegrity, CryptParamsVerity}, 14 | }; 15 | 16 | #[cfg(cryptsetup24supported)] 17 | use serde_json::Value; 18 | use uuid::Uuid; 19 | 20 | /// Handle for crypt device status operations 21 | pub struct CryptDeviceStatusHandle<'a> { 22 | reference: &'a mut CryptDevice, 23 | } 24 | 25 | impl<'a> CryptDeviceStatusHandle<'a> { 26 | pub(crate) fn new(reference: &'a mut CryptDevice) -> Self { 27 | CryptDeviceStatusHandle { reference } 28 | } 29 | 30 | /// Dump text info about device to log output 31 | pub fn dump(&mut self) -> Result<(), LibcryptErr> { 32 | errno!(mutex!(libcryptsetup_rs_sys::crypt_dump( 33 | self.reference.as_ptr() 34 | ))) 35 | } 36 | 37 | /// Dump text info about device to JSON output 38 | #[cfg(cryptsetup24supported)] 39 | pub fn dump_json(&mut self) -> Result { 40 | let mut buffer = ptr::null(); 41 | errno!(mutex!(libcryptsetup_rs_sys::crypt_dump_json( 42 | self.reference.as_ptr(), 43 | &mut buffer as *mut _, 44 | 0, 45 | )))?; 46 | let json = serde_json::from_str( 47 | unsafe { CStr::from_ptr(buffer) } 48 | .to_str() 49 | .map_err(LibcryptErr::Utf8Error)?, 50 | ) 51 | .map_err(LibcryptErr::JsonError)?; 52 | // Here one would probably expect a free call because we are receiving an allocated buffer 53 | // into a pointer. Adding a free call here results in a double free error. Furthermore 54 | // json-c references reference counting in its documentation. Just to ensure that we are 55 | // not causing a memory leak, I ran valgrind on a test program without the free and no 56 | // memory appears to be lost. This leads me to believe that we are doing the right thing 57 | // here. 58 | Ok(json) 59 | } 60 | 61 | /// Get cipher used by device 62 | pub fn get_cipher(&mut self) -> Result { 63 | from_str_ptr_to_owned!(libcryptsetup_rs_sys::crypt_get_cipher( 64 | self.reference.as_ptr() 65 | )) 66 | } 67 | 68 | /// Get cipher mode used by device 69 | pub fn get_cipher_mode(&mut self) -> Result { 70 | from_str_ptr_to_owned!(libcryptsetup_rs_sys::crypt_get_cipher_mode( 71 | self.reference.as_ptr() 72 | )) 73 | } 74 | 75 | /// Get device UUID 76 | pub fn get_uuid(&mut self) -> Result { 77 | from_str_ptr!(unsafe { libcryptsetup_rs_sys::crypt_get_uuid(self.reference.as_ptr()) }) 78 | .and_then(|e| Uuid::from_str(e).map_err(LibcryptErr::UuidError)) 79 | } 80 | 81 | /// Get path to underlying device 82 | pub fn get_device_path(&mut self) -> Result<&Path, LibcryptErr> { 83 | from_str_ptr!(unsafe { 84 | libcryptsetup_rs_sys::crypt_get_device_name(self.reference.as_ptr()) 85 | }) 86 | .map(Path::new) 87 | } 88 | 89 | /// Get path to detached metadata device or `None` if it is attached 90 | pub fn get_metadata_device_path(&mut self) -> Result, LibcryptErr> { 91 | let ptr = mutex!(libcryptsetup_rs_sys::crypt_get_metadata_device_name( 92 | self.reference.as_ptr() 93 | )); 94 | if ptr.is_null() { 95 | return Ok(None); 96 | } 97 | from_str_ptr!(ptr).map(|s| Some(Path::new(s))) 98 | } 99 | 100 | /// Get offset in 512-byte sectors where real data starts 101 | pub fn get_data_offset(&mut self) -> u64 { 102 | mutex!(libcryptsetup_rs_sys::crypt_get_data_offset( 103 | self.reference.as_ptr() 104 | )) 105 | } 106 | 107 | /// Get IV location offset in 512-byte sectors 108 | pub fn get_iv_offset(&mut self) -> u64 { 109 | mutex!(libcryptsetup_rs_sys::crypt_get_iv_offset( 110 | self.reference.as_ptr() 111 | )) 112 | } 113 | 114 | /// Get size in bytes of volume key 115 | pub fn get_volume_key_size(&mut self) -> c_int { 116 | mutex!(libcryptsetup_rs_sys::crypt_get_volume_key_size( 117 | self.reference.as_ptr() 118 | )) 119 | } 120 | 121 | /// Get Verity device parameters 122 | pub fn get_verity_info(&mut self) -> Result { 123 | let mut verity = libcryptsetup_rs_sys::crypt_params_verity { 124 | hash_name: std::ptr::null(), 125 | data_device: std::ptr::null(), 126 | hash_device: std::ptr::null(), 127 | fec_device: std::ptr::null(), 128 | salt: std::ptr::null(), 129 | salt_size: 0, 130 | hash_type: 0, 131 | data_block_size: 0, 132 | hash_block_size: 0, 133 | data_size: 0, 134 | hash_area_offset: 0, 135 | fec_area_offset: 0, 136 | fec_roots: 0, 137 | flags: 0, 138 | }; 139 | errno!(mutex!(libcryptsetup_rs_sys::crypt_get_verity_info( 140 | self.reference.as_ptr(), 141 | &mut verity as *mut _, 142 | ))) 143 | .and_then(|_| CryptParamsVerity::try_from(&verity)) 144 | } 145 | 146 | /// Get Integrity device parameters 147 | pub fn get_integrity_info(&mut self) -> Result { 148 | let mut integrity = libcryptsetup_rs_sys::crypt_params_integrity { 149 | journal_size: 0, 150 | journal_watermark: 0, 151 | journal_commit_time: 0, 152 | interleave_sectors: 0, 153 | tag_size: 0, 154 | sector_size: 0, 155 | buffer_sectors: 0, 156 | integrity: std::ptr::null(), 157 | integrity_key_size: 0, 158 | journal_integrity: std::ptr::null(), 159 | journal_integrity_key: std::ptr::null(), 160 | journal_integrity_key_size: 0, 161 | journal_crypt: std::ptr::null(), 162 | journal_crypt_key: std::ptr::null(), 163 | journal_crypt_key_size: 0, 164 | }; 165 | errno!(mutex!(libcryptsetup_rs_sys::crypt_get_integrity_info( 166 | self.reference.as_ptr(), 167 | &mut integrity as *mut _, 168 | ))) 169 | .and_then(|_| CryptParamsIntegrity::try_from(&integrity)) 170 | } 171 | } 172 | 173 | /// Get status info from device name 174 | pub fn status( 175 | device: Option<&mut CryptDevice>, 176 | name: &str, 177 | ) -> Result { 178 | let name_cstring = to_cstring!(name)?; 179 | try_int_to_return!( 180 | mutex!(libcryptsetup_rs_sys::crypt_status( 181 | match device { 182 | Some(d) => d.as_ptr(), 183 | None => std::ptr::null_mut(), 184 | }, 185 | name_cstring.as_ptr(), 186 | )), 187 | CryptStatusInfo 188 | ) 189 | } 190 | 191 | /// Get size of encryption sectors in bytes 192 | pub fn get_sector_size(device: Option<&mut CryptDevice>) -> c_int { 193 | mutex!(libcryptsetup_rs_sys::crypt_get_sector_size( 194 | device.map(|d| d.as_ptr()).unwrap_or(ptr::null_mut()), 195 | )) 196 | } 197 | -------------------------------------------------------------------------------- /src/device.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{ffi::CString, path::Path, ptr}; 6 | 7 | use either::Either; 8 | use libc::{c_char, c_int, c_void}; 9 | 10 | use libcryptsetup_rs_sys::crypt_device; 11 | 12 | use crate::{ 13 | activate::CryptActivationHandle, 14 | backup::CryptBackupHandle, 15 | context::CryptContextHandle, 16 | err::LibcryptErr, 17 | format::CryptFormatHandle, 18 | key::CryptVolumeKeyHandle, 19 | keyfile::CryptKeyfileHandle, 20 | keyslot::CryptKeyslotHandle, 21 | luks2::{ 22 | flags::CryptLuks2FlagsHandle, reencrypt::CryptLuks2ReencryptHandle, 23 | token::CryptLuks2TokenHandle, 24 | }, 25 | runtime::CryptRuntimeHandle, 26 | settings::CryptSettingsHandle, 27 | status::CryptDeviceStatusHandle, 28 | wipe::CryptWipeHandle, 29 | }; 30 | 31 | type ConfirmCallback = unsafe extern "C" fn(msg: *const c_char, usrptr: *mut c_void) -> c_int; 32 | 33 | /// Initialization handle for devices 34 | pub struct CryptInit; 35 | 36 | impl CryptInit { 37 | /// Initialize by device path 38 | pub fn init(device_path: &Path) -> Result { 39 | let mut cdevice: *mut crypt_device = ptr::null_mut(); 40 | let device_path_cstring = path_to_cstring!(device_path)?; 41 | errno!(mutex!(libcryptsetup_rs_sys::crypt_init( 42 | &mut cdevice as *mut *mut crypt_device, 43 | device_path_cstring.as_ptr(), 44 | )))?; 45 | Ok(CryptDevice { ptr: cdevice }) 46 | } 47 | 48 | /// Initialize by device path or a header path and a data device path 49 | pub fn init_with_data_device( 50 | device_paths: Either<&Path, (&Path, &Path)>, 51 | ) -> Result { 52 | let mut cdevice: *mut crypt_device = ptr::null_mut(); 53 | let (device_path_cstring, data_device_option) = match device_paths { 54 | Either::Left(device) => (path_to_cstring!(device)?, None), 55 | Either::Right((header_device, data_device)) => ( 56 | path_to_cstring!(header_device)?, 57 | Some(path_to_cstring!(data_device)?), 58 | ), 59 | }; 60 | 61 | errno!(mutex!(libcryptsetup_rs_sys::crypt_init_data_device( 62 | &mut cdevice as *mut *mut crypt_device, 63 | device_path_cstring.as_ptr(), 64 | match data_device_option { 65 | Some(ref d) => d.as_ptr(), 66 | None => ptr::null(), 67 | }, 68 | )))?; 69 | Ok(CryptDevice { ptr: cdevice }) 70 | } 71 | 72 | /// Initialize by name and header device path 73 | pub fn init_by_name_and_header( 74 | name: &str, 75 | header_device_path: Option<&Path>, 76 | ) -> Result { 77 | let mut cdevice: *mut crypt_device = ptr::null_mut(); 78 | let name_cstring = to_cstring!(name)?; 79 | 80 | let mut header_device_path_cstring = CString::default(); 81 | if let Some(path) = header_device_path { 82 | header_device_path_cstring = path_to_cstring!(path)?; 83 | } 84 | 85 | errno!(mutex!(libcryptsetup_rs_sys::crypt_init_by_name_and_header( 86 | &mut cdevice as *mut *mut crypt_device, 87 | name_cstring.as_ptr(), 88 | if header_device_path.is_some() { 89 | header_device_path_cstring.as_ptr() 90 | } else { 91 | ptr::null() 92 | }, 93 | )))?; 94 | Ok(CryptDevice { ptr: cdevice }) 95 | } 96 | } 97 | 98 | /// Data type that is a handle for a crypt device 99 | pub struct CryptDevice { 100 | ptr: *mut crypt_device, 101 | } 102 | 103 | impl CryptDevice { 104 | /// Reconstruct a `CryptDevice` object from a pointer 105 | pub fn from_ptr(ptr: *mut crypt_device) -> Self { 106 | CryptDevice { ptr } 107 | } 108 | 109 | /// Get a settings option handle 110 | pub fn settings_handle(&mut self) -> CryptSettingsHandle<'_> { 111 | CryptSettingsHandle::new(self) 112 | } 113 | 114 | /// Get a format option handle 115 | pub fn format_handle(&mut self) -> CryptFormatHandle<'_> { 116 | CryptFormatHandle::new(self) 117 | } 118 | 119 | /// Get a context option handle 120 | pub fn context_handle(&mut self) -> CryptContextHandle<'_> { 121 | CryptContextHandle::new(self) 122 | } 123 | 124 | /// Get a keyslot option handle 125 | pub fn keyslot_handle(&mut self) -> CryptKeyslotHandle<'_> { 126 | CryptKeyslotHandle::new(self) 127 | } 128 | 129 | /// Get a runtime attribute option handle 130 | pub fn runtime_handle<'a>(&'a mut self, name: &'a str) -> CryptRuntimeHandle<'a> { 131 | CryptRuntimeHandle::new(self, name) 132 | } 133 | 134 | /// Get LUKS2 flags option handle 135 | pub fn luks2_flag_handle(&mut self) -> CryptLuks2FlagsHandle<'_, T> { 136 | CryptLuks2FlagsHandle::new(self) 137 | } 138 | 139 | /// Get activation option handle 140 | pub fn activate_handle(&mut self) -> CryptActivationHandle<'_> { 141 | CryptActivationHandle::new(self) 142 | } 143 | 144 | /// Get volume key option handle 145 | pub fn volume_key_handle(&mut self) -> CryptVolumeKeyHandle<'_> { 146 | CryptVolumeKeyHandle::new(self) 147 | } 148 | 149 | /// Get crypt device status option handle 150 | pub fn status_handle(&mut self) -> CryptDeviceStatusHandle<'_> { 151 | CryptDeviceStatusHandle::new(self) 152 | } 153 | 154 | /// Get crypt device backup option handle 155 | pub fn backup_handle(&mut self) -> CryptBackupHandle<'_> { 156 | CryptBackupHandle::new(self) 157 | } 158 | 159 | /// Get crypt device keyfile option handle 160 | pub fn keyfile_handle(&mut self) -> CryptKeyfileHandle<'_> { 161 | CryptKeyfileHandle::new(self) 162 | } 163 | 164 | /// Get crypt device wipe option handle 165 | pub fn wipe_handle(&mut self) -> CryptWipeHandle<'_> { 166 | CryptWipeHandle::new(self) 167 | } 168 | 169 | /// Get crypt device LUKS2 token option handle 170 | pub fn token_handle(&mut self) -> CryptLuks2TokenHandle<'_> { 171 | CryptLuks2TokenHandle::new(self) 172 | } 173 | 174 | /// Get crypt device reencryption option handle 175 | pub fn reencrypt_handle(&mut self) -> CryptLuks2ReencryptHandle<'_> { 176 | CryptLuks2ReencryptHandle::new(self) 177 | } 178 | 179 | /// Set the callback that prompts the user to confirm an action 180 | pub fn set_confirm_callback( 181 | &mut self, 182 | confirm: Option, 183 | usrdata: Option<&mut T>, 184 | ) { 185 | mutex!(libcryptsetup_rs_sys::crypt_set_confirm_callback( 186 | self.ptr, 187 | confirm, 188 | match usrdata { 189 | Some(ud) => (ud as *mut T).cast::(), 190 | None => ptr::null_mut(), 191 | }, 192 | )) 193 | } 194 | 195 | /// Set the device path for a data device 196 | pub fn set_data_device(&mut self, device_path: &Path) -> Result<(), LibcryptErr> { 197 | let device_path_cstring = path_to_cstring!(device_path)?; 198 | errno!(mutex!(libcryptsetup_rs_sys::crypt_set_data_device( 199 | self.ptr, 200 | device_path_cstring.as_ptr() 201 | ))) 202 | } 203 | 204 | /// Set the offset in 512-byte sectors for the data section on a device 205 | pub fn set_data_offset(&mut self, offset: u64) -> Result<(), LibcryptErr> { 206 | errno!(mutex!(libcryptsetup_rs_sys::crypt_set_data_offset( 207 | self.ptr, offset 208 | ))) 209 | } 210 | 211 | pub(crate) fn as_ptr(&mut self) -> *mut crypt_device { 212 | self.ptr 213 | } 214 | } 215 | 216 | impl Drop for CryptDevice { 217 | fn drop(&mut self) { 218 | mutex!(libcryptsetup_rs_sys::crypt_free(self.ptr)) 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/context.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{os::raw::c_int, path::Path, ptr}; 6 | 7 | use crate::{ 8 | consts::vals::EncryptionFormat, device::CryptDevice, err::LibcryptErr, format::CryptParams, 9 | }; 10 | 11 | use either::Either; 12 | use uuid::Uuid; 13 | 14 | /// Cryptographic context for device 15 | pub struct CryptContextHandle<'a> { 16 | reference: &'a mut CryptDevice, 17 | } 18 | 19 | impl<'a> CryptContextHandle<'a> { 20 | pub(crate) fn new(reference: &'a mut CryptDevice) -> Self { 21 | CryptContextHandle { reference } 22 | } 23 | 24 | /// Format and encrypt the given device with the requested encryption 25 | /// algorithm and key or key length. 26 | /// 27 | /// For `volume_key` parameter, either the volume key or the desired length of 28 | /// the generated volume key can be specified. 29 | /// 30 | /// For the `volume_key` parameter, the value in `Either::Right` must be in 31 | /// units of bytes. For a common key length such as 512 bits, the value passed 32 | /// to the `Either::Right` variant would be `512 / 8`. 33 | pub fn format( 34 | &mut self, 35 | type_: EncryptionFormat, 36 | cipher_and_mode: (&str, &str), 37 | uuid: Option, 38 | volume_key: Either<&[u8], usize>, 39 | params: Option<&mut T>, 40 | ) -> Result<(), LibcryptErr> { 41 | let uuid_c_string = match uuid { 42 | Some(u) => Some(to_cstring!(u.to_string())?), 43 | None => None, 44 | }; 45 | let (volume_key_ptr, volume_key_len) = match volume_key { 46 | Either::Left(vk) => (to_byte_ptr!(vk), vk.len()), 47 | Either::Right(len) => (ptr::null(), len), 48 | }; 49 | let (cipher, cipher_mode) = cipher_and_mode; 50 | let cipher_cstring = to_cstring!(cipher)?; 51 | let cipher_mode_cstring = to_cstring!(cipher_mode)?; 52 | errno!(mutex!(libcryptsetup_rs_sys::crypt_format( 53 | self.reference.as_ptr(), 54 | type_.as_ptr(), 55 | cipher_cstring.as_ptr(), 56 | cipher_mode_cstring.as_ptr(), 57 | uuid_c_string 58 | .as_ref() 59 | .map(|cs| cs.as_ptr()) 60 | .unwrap_or_else(ptr::null), 61 | volume_key_ptr, 62 | volume_key_len, 63 | params.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut()), 64 | )))?; 65 | Ok(()) 66 | } 67 | 68 | /// Convert to new format type 69 | pub fn convert( 70 | &mut self, 71 | type_: EncryptionFormat, 72 | params: Option<&mut T>, 73 | ) -> Result<(), LibcryptErr> { 74 | errno!(mutex!(libcryptsetup_rs_sys::crypt_convert( 75 | self.reference.as_ptr(), 76 | type_.as_ptr(), 77 | params.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut()), 78 | ))) 79 | } 80 | 81 | /// Set UUID of crypt device 82 | pub fn set_uuid(&mut self, uuid: Option) -> Result<(), LibcryptErr> { 83 | let c_string = match uuid { 84 | Some(u) => Some(to_cstring!(u.to_string())?), 85 | None => None, 86 | }; 87 | errno!(mutex!(libcryptsetup_rs_sys::crypt_set_uuid( 88 | self.reference.as_ptr(), 89 | c_string 90 | .as_ref() 91 | .map(|cs| cs.as_ptr()) 92 | .unwrap_or_else(ptr::null) 93 | ))) 94 | } 95 | 96 | /// Set LUKS2 device label 97 | pub fn set_label( 98 | &mut self, 99 | label: Option<&str>, 100 | subsystem_label: Option<&str>, 101 | ) -> Result<(), LibcryptErr> { 102 | let (lcstring, slcstring) = match (label, subsystem_label) { 103 | (Some(l), Some(sl)) => (Some(to_cstring!(l)?), Some(to_cstring!(sl)?)), 104 | (Some(l), _) => (Some(to_cstring!(l)?), None), 105 | (_, Some(sl)) => (None, Some(to_cstring!(sl)?)), 106 | (_, _) => (None, None), 107 | }; 108 | errno!(mutex!(libcryptsetup_rs_sys::crypt_set_label( 109 | self.reference.as_ptr(), 110 | lcstring 111 | .as_ref() 112 | .map(|cs| cs.as_ptr()) 113 | .unwrap_or(ptr::null()), 114 | slcstring 115 | .as_ref() 116 | .map(|cs| cs.as_ptr()) 117 | .unwrap_or(ptr::null()), 118 | ))) 119 | } 120 | 121 | /// Set policty on loading volume keys via kernel keyring 122 | pub fn volume_key_keyring(&mut self, enable: bool) -> Result<(), LibcryptErr> { 123 | errno!(mutex!(libcryptsetup_rs_sys::crypt_volume_key_keyring( 124 | self.reference.as_ptr(), 125 | enable as c_int 126 | ))) 127 | } 128 | 129 | /// Load on-disk header parameters based on provided type 130 | pub fn load( 131 | &mut self, 132 | type_: Option, 133 | params: Option<&mut T>, 134 | ) -> Result<(), LibcryptErr> { 135 | errno!(mutex!(libcryptsetup_rs_sys::crypt_load( 136 | self.reference.as_ptr(), 137 | type_.map(|t| t.as_ptr()).unwrap_or(ptr::null()), 138 | params.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut()), 139 | )))?; 140 | Ok(()) 141 | } 142 | 143 | /// Repair crypt device header if invalid 144 | pub fn repair( 145 | &mut self, 146 | type_: EncryptionFormat, 147 | params: Option<&mut T>, 148 | ) -> Result<(), LibcryptErr> { 149 | errno!(mutex!(libcryptsetup_rs_sys::crypt_repair( 150 | self.reference.as_ptr(), 151 | type_.as_ptr(), 152 | params.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut()), 153 | ))) 154 | } 155 | 156 | /// Resize crypt device 157 | pub fn resize(&mut self, name: &str, new_size: u64) -> Result<(), LibcryptErr> { 158 | let name_cstring = to_cstring!(name)?; 159 | errno!(mutex!(libcryptsetup_rs_sys::crypt_resize( 160 | self.reference.as_ptr(), 161 | name_cstring.as_ptr(), 162 | new_size, 163 | ))) 164 | } 165 | 166 | /// Suspend crypt device 167 | pub fn suspend(&mut self, name: &str) -> Result<(), LibcryptErr> { 168 | let name_cstring = to_cstring!(name)?; 169 | errno!(mutex!(libcryptsetup_rs_sys::crypt_suspend( 170 | self.reference.as_ptr(), 171 | name_cstring.as_ptr() 172 | ))) 173 | } 174 | 175 | /// Resume crypt device using a passphrase 176 | pub fn resume_by_passphrase( 177 | &mut self, 178 | name: &str, 179 | keyslot: c_int, 180 | passphrase: &str, 181 | ) -> Result { 182 | let name_cstring = to_cstring!(name)?; 183 | let passphrase_cstring = to_cstring!(passphrase)?; 184 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_resume_by_passphrase( 185 | self.reference.as_ptr(), 186 | name_cstring.as_ptr(), 187 | keyslot, 188 | passphrase_cstring.as_ptr(), 189 | passphrase.len() as crate::size_t, 190 | ))) 191 | } 192 | 193 | /// Resume crypt device using a key file at an offset on disk 194 | pub fn resume_by_keyfile_device_offset( 195 | &mut self, 196 | name: &str, 197 | keyslot: c_int, 198 | keyfile: &Path, 199 | keyfile_size: crate::size_t, 200 | keyfile_offset: u64, 201 | ) -> Result { 202 | let name_cstring = to_cstring!(name)?; 203 | let keyfile_cstring = path_to_cstring!(keyfile)?; 204 | errno_int_success!(mutex!( 205 | libcryptsetup_rs_sys::crypt_resume_by_keyfile_device_offset( 206 | self.reference.as_ptr(), 207 | name_cstring.as_ptr(), 208 | keyslot, 209 | keyfile_cstring.as_ptr(), 210 | keyfile_size, 211 | keyfile_offset, 212 | ) 213 | )) 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/settings.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{ffi::CString, marker::PhantomData, os::raw::c_int}; 6 | 7 | use libcryptsetup_rs_sys::crypt_pbkdf_type; 8 | 9 | use crate::{ 10 | consts::{ 11 | flags::CryptPbkdf, 12 | vals::{CryptKdf, CryptRng, KeyslotsSize, LockState, LuksType, MetadataSize}, 13 | }, 14 | device::CryptDevice, 15 | err::LibcryptErr, 16 | }; 17 | 18 | /// Rust representation of `crypt_pbkdf_type` 19 | pub struct CryptPbkdfType { 20 | #[allow(missing_docs)] 21 | pub type_: CryptKdf, 22 | #[allow(missing_docs)] 23 | pub hash: String, 24 | #[allow(missing_docs)] 25 | pub time_ms: u32, 26 | #[allow(missing_docs)] 27 | pub iterations: u32, 28 | #[allow(missing_docs)] 29 | pub max_memory_kb: u32, 30 | #[allow(missing_docs)] 31 | pub parallel_threads: u32, 32 | #[allow(missing_docs)] 33 | pub flags: CryptPbkdf, 34 | } 35 | 36 | impl TryFrom for CryptPbkdfType { 37 | type Error = LibcryptErr; 38 | 39 | fn try_from( 40 | type_: libcryptsetup_rs_sys::crypt_pbkdf_type, 41 | ) -> Result { 42 | Ok(CryptPbkdfType { 43 | type_: CryptKdf::from_ptr(type_.type_)?, 44 | hash: String::from(from_str_ptr!(type_.hash)?), 45 | time_ms: type_.time_ms, 46 | iterations: type_.iterations, 47 | max_memory_kb: type_.max_memory_kb, 48 | parallel_threads: type_.parallel_threads, 49 | flags: CryptPbkdf::from_bits(type_.flags).ok_or(LibcryptErr::InvalidConversion)?, 50 | }) 51 | } 52 | } 53 | 54 | impl<'a> TryFrom<&'a libcryptsetup_rs_sys::crypt_pbkdf_type> for CryptPbkdfType { 55 | type Error = LibcryptErr; 56 | 57 | fn try_from(v: &'a libcryptsetup_rs_sys::crypt_pbkdf_type) -> Result { 58 | Ok(CryptPbkdfType { 59 | type_: CryptKdf::from_ptr(v.type_)?, 60 | hash: from_str_ptr!(v.hash)?.to_string(), 61 | time_ms: v.time_ms, 62 | iterations: v.iterations, 63 | max_memory_kb: v.max_memory_kb, 64 | parallel_threads: v.parallel_threads, 65 | flags: CryptPbkdf::from_bits(v.flags).ok_or(LibcryptErr::InvalidConversion)?, 66 | }) 67 | } 68 | } 69 | 70 | /// A type wrapping a PBKDF type with pointers derived from Rust data types and lifetimes to ensure 71 | /// pointer validity 72 | pub struct CryptPbkdfTypeRef<'a> { 73 | /// Field containing a `crypt_pbkdf_type` that contains pointers valid for the supplied struct lifetime 74 | pub inner: crypt_pbkdf_type, 75 | #[allow(dead_code)] 76 | hash_cstring: CString, 77 | phantomdata: PhantomData<&'a ()>, 78 | } 79 | 80 | impl<'a> TryInto> for &'a CryptPbkdfType { 81 | type Error = LibcryptErr; 82 | 83 | fn try_into(self) -> Result, Self::Error> { 84 | let hash_cstring = CString::new(self.hash.as_bytes()).map_err(LibcryptErr::NullError)?; 85 | let inner = libcryptsetup_rs_sys::crypt_pbkdf_type { 86 | type_: self.type_.as_ptr(), 87 | hash: hash_cstring.as_ptr(), 88 | time_ms: self.time_ms, 89 | iterations: self.iterations, 90 | max_memory_kb: self.max_memory_kb, 91 | parallel_threads: self.parallel_threads, 92 | flags: self.flags.bits(), 93 | }; 94 | Ok(CryptPbkdfTypeRef { 95 | inner, 96 | hash_cstring, 97 | phantomdata: PhantomData, 98 | }) 99 | } 100 | } 101 | 102 | /// Handle to operate on cryptsetup device settings 103 | pub struct CryptSettingsHandle<'a> { 104 | reference: &'a mut CryptDevice, 105 | } 106 | 107 | impl<'a> CryptSettingsHandle<'a> { 108 | pub(crate) fn new(reference: &'a mut CryptDevice) -> Self { 109 | CryptSettingsHandle { reference } 110 | } 111 | 112 | /// Set random number generator type 113 | pub fn set_rng_type(&mut self, rng_type: CryptRng) { 114 | let rng_u32: u32 = rng_type.into(); 115 | mutex!(libcryptsetup_rs_sys::crypt_set_rng_type( 116 | self.reference.as_ptr(), 117 | rng_u32 as c_int 118 | )) 119 | } 120 | 121 | /// Get random number generator type 122 | pub fn get_rng_type(&mut self) -> Result { 123 | CryptRng::try_from(mutex!(libcryptsetup_rs_sys::crypt_get_rng_type( 124 | self.reference.as_ptr() 125 | )) as u32) 126 | } 127 | 128 | /// Set PBKDF type 129 | pub fn set_pbkdf_type<'b>( 130 | &mut self, 131 | pbkdf_type: &'b CryptPbkdfType, 132 | ) -> Result<(), LibcryptErr> { 133 | let type_: CryptPbkdfTypeRef<'b> = pbkdf_type.try_into()?; 134 | errno!(mutex!(libcryptsetup_rs_sys::crypt_set_pbkdf_type( 135 | self.reference.as_ptr(), 136 | &type_.inner as *const crypt_pbkdf_type, 137 | ))) 138 | } 139 | 140 | /// Get PBKDF parameters 141 | pub fn get_pbkdf_type_params(pbkdf_type: &CryptKdf) -> Result { 142 | let type_ = ptr_to_result_with_reference!(mutex!( 143 | libcryptsetup_rs_sys::crypt_get_pbkdf_type_params(pbkdf_type.as_ptr()) 144 | ))?; 145 | CryptPbkdfType::try_from(type_) 146 | } 147 | 148 | /// Get PBKDF default type 149 | pub fn get_pbkdf_default(luks_type: &LuksType) -> Result { 150 | let default = ptr_to_result_with_reference!(mutex!( 151 | libcryptsetup_rs_sys::crypt_get_pbkdf_default(luks_type.as_ptr()) 152 | ))?; 153 | CryptPbkdfType::try_from(default) 154 | } 155 | 156 | /// Get PBKDF type 157 | pub fn get_pbkdf_type(&mut self) -> Result { 158 | let type_ = ptr_to_result_with_reference!(mutex!( 159 | libcryptsetup_rs_sys::crypt_get_pbkdf_type(self.reference.as_ptr()) 160 | ))?; 161 | CryptPbkdfType::try_from(type_) 162 | } 163 | 164 | /// Set the iteration time in milliseconds 165 | pub fn set_iteration_time(&mut self, iteration_time_ms: u64) { 166 | mutex!(libcryptsetup_rs_sys::crypt_set_iteration_time( 167 | self.reference.as_ptr(), 168 | iteration_time_ms, 169 | )) 170 | } 171 | 172 | /// Lock or unlock memory 173 | pub fn memory_lock(&mut self, lock: LockState) -> LockState { 174 | int_to_return!( 175 | mutex!(libcryptsetup_rs_sys::crypt_memory_lock( 176 | self.reference.as_ptr(), 177 | lock as c_int 178 | )), 179 | LockState 180 | ) 181 | } 182 | 183 | /// Lock or unlock the metadata 184 | pub fn metadata_locking(&mut self, enable: bool) -> Result<(), LibcryptErr> { 185 | errno!(mutex!(libcryptsetup_rs_sys::crypt_metadata_locking( 186 | self.reference.as_ptr(), 187 | enable as c_int 188 | ))) 189 | } 190 | 191 | /// Set the metadata size and keyslot size 192 | pub fn set_metadata_size( 193 | &mut self, 194 | metadata_size: MetadataSize, 195 | keyslots_size: KeyslotsSize, 196 | ) -> Result<(), LibcryptErr> { 197 | errno!(mutex!(libcryptsetup_rs_sys::crypt_set_metadata_size( 198 | self.reference.as_ptr(), 199 | *metadata_size, 200 | *keyslots_size, 201 | ))) 202 | } 203 | 204 | /// Get the metadata size and keyslot size 205 | pub fn get_metadata_size(&mut self) -> Result<(MetadataSize, KeyslotsSize), LibcryptErr> { 206 | let mut metadata_size = 0u64; 207 | let mut keyslots_size = 0u64; 208 | errno!(mutex!(libcryptsetup_rs_sys::crypt_get_metadata_size( 209 | self.reference.as_ptr(), 210 | &mut metadata_size as *mut u64, 211 | &mut keyslots_size as *mut u64, 212 | )))?; 213 | let msize = MetadataSize::try_from(metadata_size)?; 214 | let ksize = KeyslotsSize::try_from(keyslots_size)?; 215 | Ok((msize, ksize)) 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: libcryptsetup CI 3 | 4 | # yamllint disable-line rule:truthy 5 | on: 6 | push: 7 | branches: [master] 8 | paths-ignore: 9 | - 'CHANGES.txt' 10 | - '**/README.md' 11 | pull_request: 12 | branches: [master] 13 | paths-ignore: 14 | - 'CHANGES.txt' 15 | - '**/README.md' 16 | 17 | # Allows you to run this workflow manually from the Actions tab 18 | workflow_dispatch: 19 | 20 | jobs: 21 | # MANDATORY CHECKS USING CURRENT DEVELOPMENT TOOLCHAIN: 22 | format: 23 | env: 24 | TASK: fmt-ci 25 | TOOLCHAIN: 1.92.0 # CURRENT DEVELOPMENT RUST TOOLCHAIN 26 | runs-on: ubuntu-22.04 27 | steps: 28 | - name: Install git 29 | run: sudo apt-get install git 30 | - uses: actions/checkout@v6 31 | with: 32 | persist-credentials: false 33 | - uses: dtolnay/rust-toolchain@master 34 | with: 35 | components: rustfmt 36 | toolchain: ${{ env.TOOLCHAIN }} 37 | - name: Test format on ${{ env.TOOLCHAIN }} toolchain 38 | run: make -f Makefile $TASK 39 | lint: 40 | env: 41 | TASK: clippy 42 | TOOLCHAIN: 1.92.0 # CURRENT DEVELOPMENT RUST TOOLCHAIN 43 | runs-on: ubuntu-22.04 44 | steps: 45 | - name: Install git 46 | run: sudo apt-get install git 47 | - uses: actions/checkout@v6 48 | with: 49 | persist-credentials: false 50 | - uses: dtolnay/rust-toolchain@master 51 | with: 52 | components: clippy 53 | toolchain: ${{ env.TOOLCHAIN }} 54 | - name: Install dependencies 55 | run: > 56 | sudo apt-get install libcryptsetup-dev 57 | - name: Test linting on ${{ env.TOOLCHAIN }} toolchain 58 | run: make -f Makefile $TASK 59 | typos: 60 | env: 61 | TASK: check-typos 62 | TOOLCHAIN: 1.92.0 # CURRENT DEVELOPMENT RUST TOOLCHAIN 63 | runs-on: ubuntu-22.04 64 | steps: 65 | - name: Install git 66 | run: sudo apt-get install git 67 | - uses: actions/checkout@v6 68 | with: 69 | persist-credentials: false 70 | - uses: dtolnay/rust-toolchain@master 71 | with: 72 | toolchain: ${{ env.TOOLCHAIN }} 73 | - uses: baptiste0928/cargo-install@v3 74 | with: 75 | crate: typos-cli 76 | - name: Test typos on ${{ env.TOOLCHAIN }} toolchain 77 | run: make -f Makefile $TASK 78 | 79 | 80 | # MANDATORY TESTING ON STABLE 81 | stable: 82 | env: 83 | TOOLCHAIN: stable 84 | strategy: 85 | matrix: 86 | include: 87 | - task: make -f Makefile build 88 | dependencies: libcryptsetup-dev 89 | - task: make -f Makefile build-examples 90 | dependencies: libcryptsetup-dev 91 | - task: make -f Makefile docs-ci 92 | dependencies: libcryptsetup-dev 93 | - task: make -f Makefile test 94 | dependencies: libcryptsetup-dev libkeyutils-dev 95 | - task: make -f Makefile test-mutex 96 | dependencies: libcryptsetup-dev libkeyutils-dev 97 | - task: make -f Makefile test-mutex-guard 98 | dependencies: libcryptsetup-dev libkeyutils-dev 99 | - task: make -f Makefile release 100 | dependencies: libcryptsetup-dev 101 | runs-on: ubuntu-22.04 102 | steps: 103 | - name: Install git 104 | run: sudo apt-get install git 105 | - uses: actions/checkout@v6 106 | with: 107 | persist-credentials: false 108 | - uses: dtolnay/rust-toolchain@master 109 | with: 110 | toolchain: ${{ env.TOOLCHAIN }} 111 | - name: Install dependencies 112 | run: > 113 | sudo apt-get install ${{ matrix.dependencies }} 114 | - name: Test ${{ matrix.task }} on ${{ env.TOOLCHAIN }} toolchain 115 | run: ${{ matrix.task }} 116 | 117 | # MANDATORY TESTING USING LOWEST SUPPORTED TOOLCHAIN 118 | lowest_supported: 119 | env: 120 | TOOLCHAIN: 1.82.0 # LOWEST SUPPORTED RUST TOOLCHAIN 121 | strategy: 122 | matrix: 123 | include: 124 | - task: make -f Makefile test 125 | dependencies: libcryptsetup-dev libkeyutils-dev 126 | - task: make -f Makefile test-mutex 127 | dependencies: libcryptsetup-dev libkeyutils-dev 128 | - task: make -f Makefile test-mutex-guard 129 | dependencies: libcryptsetup-dev libkeyutils-dev 130 | runs-on: ubuntu-22.04 131 | steps: 132 | - name: Install git 133 | run: sudo apt-get install git 134 | - uses: actions/checkout@v6 135 | with: 136 | persist-credentials: false 137 | - uses: dtolnay/rust-toolchain@master 138 | with: 139 | toolchain: ${{ env.TOOLCHAIN }} 140 | - name: Install dependencies 141 | run: > 142 | sudo apt-get install ${{ matrix.dependencies }} 143 | - name: Test ${{ matrix.task }} on ${{ env.TOOLCHAIN }} toolchain 144 | run: ${{ matrix.task }} 145 | 146 | # MANDATORY TESTING ON FEDORA 147 | fedora: 148 | env: 149 | # Lowest supported 150 | TOOLCHAIN: 1.82.0 # LOWEST SUPPORTED RUST TOOLCHAIN 151 | strategy: 152 | matrix: 153 | include: 154 | # Fedora 33 155 | - task: make -f Makefile build 156 | dependencies: cryptsetup-devel 157 | container: 33 158 | - task: make -f Makefile build-examples 159 | dependencies: cryptsetup-devel 160 | container: 33 161 | - task: make -f Makefile docs-ci 162 | dependencies: cryptsetup-devel 163 | container: 33 164 | - task: make -f Makefile test 165 | dependencies: cryptsetup-devel keyutils-libs-devel 166 | container: 33 167 | - task: make -f Makefile test-mutex 168 | dependencies: cryptsetup-devel keyutils-libs-devel 169 | container: 33 170 | - task: make -f Makefile test-mutex-guard 171 | dependencies: cryptsetup-devel keyutils-libs-devel 172 | container: 33 173 | - task: make -f Makefile test-loopback 174 | dependencies: cryptsetup-devel keyutils-libs-devel 175 | container: 33 176 | - task: make -f Makefile release 177 | dependencies: cryptsetup-devel 178 | container: 33 179 | 180 | # Fedora 34 181 | - task: make -f Makefile build 182 | dependencies: cryptsetup-devel 183 | container: 34 184 | - task: make -f Makefile build-examples 185 | dependencies: cryptsetup-devel 186 | container: 34 187 | - task: make -f Makefile docs-ci 188 | dependencies: cryptsetup-devel 189 | container: 34 190 | - task: make -f Makefile test 191 | dependencies: cryptsetup-devel keyutils-libs-devel 192 | container: 34 193 | - task: make -f Makefile test-mutex 194 | dependencies: cryptsetup-devel keyutils-libs-devel 195 | container: 34 196 | - task: make -f Makefile test-mutex-guard 197 | dependencies: cryptsetup-devel keyutils-libs-devel 198 | container: 34 199 | - task: make -f Makefile test-loopback 200 | dependencies: cryptsetup-devel keyutils-libs-devel 201 | container: 34 202 | - task: make -f Makefile release 203 | dependencies: cryptsetup-devel 204 | container: 34 205 | 206 | # Fedora 38 207 | - task: make -f Makefile build 208 | dependencies: cryptsetup-devel 209 | container: 38 210 | - task: make -f Makefile build-examples 211 | dependencies: cryptsetup-devel 212 | container: 38 213 | - task: make -f Makefile docs-ci 214 | dependencies: cryptsetup-devel 215 | container: 38 216 | - task: make -f Makefile test 217 | dependencies: cryptsetup-devel keyutils-libs-devel 218 | container: 38 219 | - task: make -f Makefile test-mutex 220 | dependencies: cryptsetup-devel keyutils-libs-devel 221 | container: 38 222 | - task: make -f Makefile test-mutex-guard 223 | dependencies: cryptsetup-devel keyutils-libs-devel 224 | container: 38 225 | - task: make -f Makefile test-loopback 226 | dependencies: cryptsetup-devel keyutils-libs-devel 227 | container: 38 228 | - task: make -f Makefile release 229 | dependencies: cryptsetup-devel 230 | container: 38 231 | 232 | runs-on: ubuntu-22.04 233 | container: 234 | image: fedora:${{ matrix.container }} 235 | options: --privileged -v /dev:/dev 236 | steps: 237 | - name: Install git 238 | run: dnf install -y git 239 | - uses: actions/checkout@v6 240 | with: 241 | persist-credentials: false 242 | - uses: dtolnay/rust-toolchain@master 243 | with: 244 | toolchain: ${{ env.TOOLCHAIN }} 245 | - name: Install dependencies 246 | run: dnf install -y make gcc clang-devel ${{ matrix.dependencies }} 247 | - name: Test ${{ matrix.task }} on ${{ env.TOOLCHAIN }} toolchain 248 | run: ${{ matrix.task }} 249 | 250 | # VERIFICATION OF TEST INFRASTRUCTURE 251 | yamllint: 252 | runs-on: ubuntu-22.04 253 | container: fedora:42 # CURRENT DEVELOPMENT ENVIRONMENT 254 | steps: 255 | - name: Install git 256 | run: dnf install -y git 257 | - uses: actions/checkout@v6 258 | with: 259 | persist-credentials: false 260 | - name: Install dependencies 261 | run: dnf install -y make yamllint 262 | - name: Run yamllint 263 | run: make -f Makefile yamllint 264 | -------------------------------------------------------------------------------- /src/keyslot.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{ 6 | path::{Path, PathBuf}, 7 | ptr, 8 | }; 9 | 10 | use either::Either; 11 | use libc::{c_int, c_uint}; 12 | 13 | use crate::{ 14 | consts::{ 15 | flags::CryptVolumeKey, 16 | vals::{EncryptionFormat, KeyslotInfo, KeyslotPriority}, 17 | }, 18 | device::CryptDevice, 19 | err::LibcryptErr, 20 | settings::CryptPbkdfType, 21 | }; 22 | 23 | /// Handle for keyslot operations 24 | pub struct CryptKeyslotHandle<'a> { 25 | reference: &'a mut CryptDevice, 26 | } 27 | 28 | impl<'a> CryptKeyslotHandle<'a> { 29 | pub(crate) fn new(reference: &'a mut CryptDevice) -> Self { 30 | CryptKeyslotHandle { reference } 31 | } 32 | 33 | /// Add key slot using a passphrase 34 | pub fn add_by_passphrase( 35 | &mut self, 36 | keyslot: Option, 37 | passphrase: &[u8], 38 | new_passphrase: &[u8], 39 | ) -> Result { 40 | errno_int_success!(mutex!( 41 | libcryptsetup_rs_sys::crypt_keyslot_add_by_passphrase( 42 | self.reference.as_ptr(), 43 | keyslot 44 | .map(|k| k as c_int) 45 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_SLOT), 46 | to_byte_ptr!(passphrase), 47 | passphrase.len(), 48 | to_byte_ptr!(new_passphrase), 49 | new_passphrase.len(), 50 | ) 51 | )) 52 | .map(|k| k as c_uint) 53 | } 54 | 55 | /// Change allocated key slot using a passphrase 56 | pub fn change_by_passphrase( 57 | &mut self, 58 | keyslot_old: Option, 59 | keyslot_new: Option, 60 | passphrase: &[u8], 61 | new_passphrase: &[u8], 62 | ) -> Result { 63 | errno_int_success!(mutex!( 64 | libcryptsetup_rs_sys::crypt_keyslot_change_by_passphrase( 65 | self.reference.as_ptr(), 66 | keyslot_old 67 | .map(|k| k as c_int) 68 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_SLOT), 69 | keyslot_new 70 | .map(|k| k as c_int) 71 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_SLOT), 72 | to_byte_ptr!(passphrase), 73 | passphrase.len(), 74 | to_byte_ptr!(new_passphrase), 75 | new_passphrase.len(), 76 | ) 77 | )) 78 | .map(|k| k as c_uint) 79 | } 80 | 81 | /// Add key slot using key file 82 | pub fn add_by_keyfile_device_offset( 83 | &mut self, 84 | keyslot: Option, 85 | keyfile_and_size: (&Path, crate::size_t), 86 | keyfile_offset: u64, 87 | new_keyfile_and_size: (&Path, crate::size_t), 88 | new_keyfile_offset: u64, 89 | ) -> Result { 90 | let (keyfile, keyfile_size) = keyfile_and_size; 91 | let (new_keyfile, new_keyfile_size) = new_keyfile_and_size; 92 | let keyfile_cstring = path_to_cstring!(keyfile)?; 93 | let new_keyfile_cstring = path_to_cstring!(new_keyfile)?; 94 | errno_int_success!(mutex!( 95 | libcryptsetup_rs_sys::crypt_keyslot_add_by_keyfile_device_offset( 96 | self.reference.as_ptr(), 97 | keyslot 98 | .map(|k| k as c_int) 99 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_SLOT), 100 | keyfile_cstring.as_ptr(), 101 | keyfile_size, 102 | keyfile_offset, 103 | new_keyfile_cstring.as_ptr(), 104 | new_keyfile_size, 105 | new_keyfile_offset, 106 | ) 107 | )) 108 | .map(|k| k as c_uint) 109 | } 110 | 111 | /// Add key slot with a key 112 | pub fn add_by_key( 113 | &mut self, 114 | keyslot: Option, 115 | volume_key: Option>, 116 | passphrase: &[u8], 117 | flags: CryptVolumeKey, 118 | ) -> Result { 119 | let (vk_ptr, vk_len) = match volume_key { 120 | Some(Either::Left(vk)) => (to_byte_ptr!(vk), vk.len()), 121 | Some(Either::Right(s)) => (std::ptr::null(), s), 122 | None => (std::ptr::null(), 0), 123 | }; 124 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_keyslot_add_by_key( 125 | self.reference.as_ptr(), 126 | keyslot 127 | .map(|k| k as c_int) 128 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_SLOT), 129 | vk_ptr, 130 | vk_len, 131 | to_byte_ptr!(passphrase), 132 | passphrase.len(), 133 | flags.bits(), 134 | ))) 135 | .map(|k| k as c_uint) 136 | } 137 | 138 | /// Destroy key slot 139 | pub fn destroy(&mut self, keyslot: c_uint) -> Result<(), LibcryptErr> { 140 | errno!(mutex!(libcryptsetup_rs_sys::crypt_keyslot_destroy( 141 | self.reference.as_ptr(), 142 | keyslot as c_int 143 | ))) 144 | } 145 | 146 | /// Get keyslot status 147 | pub fn status(&mut self, keyslot: c_uint) -> Result { 148 | try_int_to_return!( 149 | mutex!(libcryptsetup_rs_sys::crypt_keyslot_status( 150 | self.reference.as_ptr(), 151 | keyslot as c_int, 152 | )), 153 | KeyslotInfo 154 | ) 155 | } 156 | 157 | /// Get keyslot priority (LUKS2 specific) 158 | pub fn get_priority(&mut self, keyslot: c_uint) -> Result { 159 | try_int_to_return!( 160 | mutex!(libcryptsetup_rs_sys::crypt_keyslot_get_priority( 161 | self.reference.as_ptr(), 162 | keyslot as c_int, 163 | )), 164 | KeyslotPriority 165 | ) 166 | } 167 | 168 | /// Get keyslot priority (LUKS2 specific) 169 | pub fn set_priority( 170 | &mut self, 171 | keyslot: c_uint, 172 | priority: KeyslotPriority, 173 | ) -> Result<(), LibcryptErr> { 174 | errno!(mutex!(libcryptsetup_rs_sys::crypt_keyslot_set_priority( 175 | self.reference.as_ptr(), 176 | keyslot as c_int, 177 | priority.into(), 178 | ))) 179 | } 180 | 181 | /// Get maximum keyslots supported for device type 182 | pub fn max_keyslots(fmt: EncryptionFormat) -> Result { 183 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_keyslot_max( 184 | fmt.as_ptr() 185 | ))) 186 | .map(|k| k as c_uint) 187 | } 188 | 189 | /// Get keyslot area pointers 190 | pub fn area(&mut self, keyslot: c_uint) -> Result<(u64, u64), LibcryptErr> { 191 | let mut offset = 0u64; 192 | let mut length = 0u64; 193 | errno!(mutex!(libcryptsetup_rs_sys::crypt_keyslot_area( 194 | self.reference.as_ptr(), 195 | keyslot as c_int, 196 | &mut offset as *mut u64, 197 | &mut length as *mut u64, 198 | ))) 199 | .map(|_| (offset, length)) 200 | } 201 | 202 | /// Get size of key in keyslot - only different from `crypt_get_volume_key_size()` binding 203 | /// in the case of LUKS2 using unbound keyslots 204 | pub fn get_key_size(&mut self, keyslot: c_uint) -> Result { 205 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_keyslot_get_key_size( 206 | self.reference.as_ptr(), 207 | keyslot as c_int, 208 | ))) 209 | .map(|k| k as c_uint) 210 | } 211 | 212 | /// Get encryption cipher and key size of keyslot (not data) 213 | pub fn get_encryption( 214 | &mut self, 215 | keyslot: Option, 216 | ) -> Result<(&str, crate::size_t), LibcryptErr> { 217 | let mut key_size: crate::size_t = 0; 218 | ptr_to_result!(mutex!(libcryptsetup_rs_sys::crypt_keyslot_get_encryption( 219 | self.reference.as_ptr(), 220 | keyslot 221 | .map(|k| k as c_int) 222 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_SLOT), 223 | &mut key_size as *mut crate::size_t, 224 | ))) 225 | .and_then(|ptr| from_str_ptr!(ptr)) 226 | .map(|st| (st, key_size)) 227 | } 228 | 229 | /// Get PBDKF parameters for a keyslot 230 | pub fn get_pbkdf(&mut self, keyslot: c_uint) -> Result { 231 | let mut type_ = libcryptsetup_rs_sys::crypt_pbkdf_type { 232 | type_: ptr::null(), 233 | hash: ptr::null(), 234 | time_ms: 0, 235 | iterations: 0, 236 | max_memory_kb: 0, 237 | parallel_threads: 0, 238 | flags: 0, 239 | }; 240 | errno!(mutex!(libcryptsetup_rs_sys::crypt_keyslot_get_pbkdf( 241 | self.reference.as_ptr(), 242 | keyslot as c_int, 243 | &mut type_ as *mut _, 244 | ))) 245 | .and_then(|_| CryptPbkdfType::try_from(type_)) 246 | } 247 | 248 | /// Set encryption used for keyslot 249 | pub fn set_encryption( 250 | &mut self, 251 | cipher: &str, 252 | key_size: crate::size_t, 253 | ) -> Result<(), LibcryptErr> { 254 | let cipher_cstring = to_cstring!(cipher)?; 255 | errno!(mutex!(libcryptsetup_rs_sys::crypt_keyslot_set_encryption( 256 | self.reference.as_ptr(), 257 | cipher_cstring.as_ptr(), 258 | key_size, 259 | ))) 260 | } 261 | 262 | /// Get directory where crypt devices are mapped 263 | pub fn get_dir() -> Result, LibcryptErr> { 264 | ptr_to_result!(mutex!(libcryptsetup_rs_sys::crypt_get_dir())) 265 | .and_then(|s| from_str_ptr_to_owned!(s)) 266 | .map(PathBuf::from) 267 | .map(|b| b.into_boxed_path()) 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/luks2/reencrypt.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{ 6 | ffi::CString, 7 | os::raw::{c_int, c_uint, c_void}, 8 | ptr, 9 | }; 10 | 11 | use libcryptsetup_rs_sys::{crypt_params_reencrypt, CRYPT_ANY_SLOT}; 12 | 13 | use crate::{ 14 | consts::{ 15 | flags::CryptReencrypt, 16 | vals::{CryptReencryptDirectionInfo, CryptReencryptInfo, CryptReencryptModeInfo}, 17 | }, 18 | device::CryptDevice, 19 | err::LibcryptErr, 20 | format::{CryptParams, CryptParamsLuks2, CryptParamsLuks2Ref}, 21 | }; 22 | 23 | type ReencryptProgress = unsafe extern "C" fn(size: u64, offset: u64, *mut c_void) -> c_int; 24 | 25 | /// A struct representing a reference with a lifetime to a `CryptParamsReencrypt` 26 | /// struct 27 | pub struct CryptParamsReencryptRef<'a> { 28 | #[allow(missing_docs)] 29 | inner: libcryptsetup_rs_sys::crypt_params_reencrypt, 30 | #[allow(dead_code)] 31 | reference: &'a CryptParamsReencrypt, 32 | #[allow(dead_code)] 33 | luks2_params: Option>>, 34 | #[allow(dead_code)] 35 | resilience_cstring: CString, 36 | #[allow(dead_code)] 37 | hash_cstring: CString, 38 | } 39 | 40 | impl CryptParamsReencryptRef<'_> { 41 | fn as_ptr(&self) -> *const crypt_params_reencrypt { 42 | (&self.inner as *const crypt_params_reencrypt).cast::() 43 | } 44 | } 45 | 46 | /// Parameters for reencryption operations 47 | pub struct CryptParamsReencrypt { 48 | /// Type of reencryption operation 49 | pub mode: CryptReencryptModeInfo, 50 | /// Start at beginning or end of disk 51 | pub direction: CryptReencryptDirectionInfo, 52 | #[allow(missing_docs)] 53 | pub resilience: String, 54 | #[allow(missing_docs)] 55 | pub hash: String, 56 | #[allow(missing_docs)] 57 | pub data_shift: u64, 58 | #[allow(missing_docs)] 59 | pub max_hotzone_size: u64, 60 | /// Size of the device 61 | pub device_size: u64, 62 | /// LUKS2-specific parameters 63 | pub luks2: Option, 64 | /// Reencryption flags 65 | pub flags: CryptReencrypt, 66 | } 67 | 68 | impl<'a> TryInto> for &'a CryptParamsReencrypt { 69 | type Error = LibcryptErr; 70 | 71 | fn try_into(self) -> Result, Self::Error> { 72 | let mut luks2_params: Option>> = match self.luks2.as_ref() { 73 | Some(l) => Some(Box::new(l.try_into()?)), 74 | None => None, 75 | }; 76 | 77 | let resilience_cstring = to_cstring!(self.resilience)?; 78 | let hash_cstring = to_cstring!(self.hash)?; 79 | 80 | let inner = libcryptsetup_rs_sys::crypt_params_reencrypt { 81 | mode: self.mode.into(), 82 | direction: self.direction.into(), 83 | resilience: resilience_cstring.as_ptr(), 84 | hash: hash_cstring.as_ptr(), 85 | data_shift: self.data_shift, 86 | max_hotzone_size: self.max_hotzone_size, 87 | device_size: self.device_size, 88 | luks2: luks2_params 89 | .as_mut() 90 | .map(|l| l.as_ptr().cast()) 91 | .unwrap_or(ptr::null_mut()), 92 | flags: self.flags.bits(), 93 | }; 94 | Ok(CryptParamsReencryptRef { 95 | inner, 96 | reference: self, 97 | luks2_params, 98 | resilience_cstring, 99 | hash_cstring, 100 | }) 101 | } 102 | } 103 | 104 | /// Handle for reencryption operations 105 | pub struct CryptLuks2ReencryptHandle<'a> { 106 | reference: &'a mut CryptDevice, 107 | } 108 | 109 | impl<'a> CryptLuks2ReencryptHandle<'a> { 110 | pub(crate) fn new(reference: &'a mut CryptDevice) -> Self { 111 | CryptLuks2ReencryptHandle { reference } 112 | } 113 | 114 | /// Initialize reencryption metadata on a device by passphrase 115 | pub fn reencrypt_init_by_passphrase( 116 | &mut self, 117 | name: Option<&str>, 118 | passphrase: &[u8], 119 | keyslot_old: Option, 120 | keyslot_new: Option, 121 | cipher_and_mode: Option<(&str, &str)>, 122 | params: CryptParamsReencrypt, 123 | ) -> Result { 124 | let name_cstring = match name { 125 | Some(n) => Some(to_cstring!(n)?), 126 | None => None, 127 | }; 128 | let (cipher_cstring_err, cipher_mode_cstring_err) = cipher_and_mode 129 | .map(|(c, cm)| { 130 | ( 131 | Some(to_cstring!(c)).transpose(), 132 | Some(to_cstring!(cm)).transpose(), 133 | ) 134 | }) 135 | .unwrap_or_else(|| (Ok(None), Ok(None))); 136 | let cipher_cstring = cipher_cstring_err?; 137 | let cipher_mode_cstring = cipher_mode_cstring_err?; 138 | let params_reencrypt: CryptParamsReencryptRef<'_> = (¶ms).try_into()?; 139 | 140 | errno_int_success!(mutex!( 141 | libcryptsetup_rs_sys::crypt_reencrypt_init_by_passphrase( 142 | self.reference.as_ptr(), 143 | name_cstring 144 | .as_ref() 145 | .map(|cs| cs.as_ptr()) 146 | .unwrap_or_else(ptr::null), 147 | to_byte_ptr!(passphrase), 148 | passphrase.len(), 149 | keyslot_old.map(|k| k as c_int).unwrap_or(CRYPT_ANY_SLOT), 150 | keyslot_new.map(|k| k as c_int).unwrap_or(CRYPT_ANY_SLOT), 151 | // NOTE: Must keep as_ref to avoid use after free error. 152 | cipher_cstring 153 | .as_ref() 154 | .map(|s| s.as_ptr()) 155 | .unwrap_or_else(ptr::null), 156 | // NOTE: Must keep as_ref to avoid use after free error. 157 | cipher_mode_cstring 158 | .as_ref() 159 | .map(|s| s.as_ptr()) 160 | .unwrap_or_else(ptr::null), 161 | params_reencrypt.as_ptr() 162 | ) 163 | )) 164 | } 165 | 166 | /// Initialize reencryption metadata on a device by passphrase in a keyring 167 | pub fn reencrypt_init_by_keyring( 168 | &mut self, 169 | name: Option<&str>, 170 | key_description: &str, 171 | keyslot_old: Option, 172 | keyslot_new: Option, 173 | cipher_and_mode: Option<(&str, &str)>, 174 | params: CryptParamsReencrypt, 175 | ) -> Result { 176 | let name_cstring = match name { 177 | Some(n) => Some(to_cstring!(n)?), 178 | None => None, 179 | }; 180 | let (cipher_cstring_err, cipher_mode_cstring_err) = cipher_and_mode 181 | .map(|(c, cm)| { 182 | ( 183 | Some(to_cstring!(c)).transpose(), 184 | Some(to_cstring!(cm)).transpose(), 185 | ) 186 | }) 187 | .unwrap_or_else(|| (Ok(None), Ok(None))); 188 | let cipher_cstring = cipher_cstring_err?; 189 | let cipher_mode_cstring = cipher_mode_cstring_err?; 190 | let params_reencrypt: CryptParamsReencryptRef<'_> = (¶ms).try_into()?; 191 | 192 | let description_cstring = to_cstring!(key_description)?; 193 | errno_int_success!(mutex!( 194 | libcryptsetup_rs_sys::crypt_reencrypt_init_by_keyring( 195 | self.reference.as_ptr(), 196 | name_cstring 197 | .as_ref() 198 | .map(|cs| cs.as_ptr()) 199 | .unwrap_or(ptr::null()), 200 | description_cstring.as_ptr(), 201 | keyslot_old.map(|k| k as c_int).unwrap_or(CRYPT_ANY_SLOT), 202 | keyslot_new.map(|k| k as c_int).unwrap_or(CRYPT_ANY_SLOT), 203 | // NOTE: Must keep as_ref to avoid use after free error. 204 | cipher_cstring 205 | .as_ref() 206 | .map(|s| s.as_ptr()) 207 | .unwrap_or_else(ptr::null), 208 | // NOTE: Must keep as_ref to avoid use after free error. 209 | cipher_mode_cstring 210 | .as_ref() 211 | .map(|s| s.as_ptr()) 212 | .unwrap_or_else(ptr::null), 213 | params_reencrypt.as_ptr(), 214 | ) 215 | )) 216 | } 217 | 218 | /// Run data reencryption 219 | pub fn reencrypt(&mut self, progress: Option) -> Result<(), LibcryptErr> { 220 | errno!(mutex!(libcryptsetup_rs_sys::crypt_reencrypt( 221 | self.reference.as_ptr(), 222 | progress 223 | ))) 224 | } 225 | 226 | /// Run data reencryption 227 | /// 228 | /// This method provides a bug fix for the API added in libcryptsetup 2.4.0 229 | #[cfg(cryptsetup24supported)] 230 | pub fn reencrypt2( 231 | &mut self, 232 | progress: Option, 233 | usrdata: Option<&mut T>, 234 | ) -> Result<(), LibcryptErr> { 235 | let usrptr = usrdata 236 | .map(|data| (data as *mut T).cast::()) 237 | .unwrap_or_else(ptr::null_mut); 238 | errno!(mutex!(libcryptsetup_rs_sys::crypt_reencrypt_run( 239 | self.reference.as_ptr(), 240 | progress, 241 | usrptr, 242 | ))) 243 | } 244 | 245 | /// LUKS2 reencryption status 246 | pub fn status( 247 | &mut self, 248 | params: CryptParamsReencrypt, 249 | ) -> Result { 250 | let mut params_reencrypt: CryptParamsReencryptRef<'_> = (¶ms).try_into()?; 251 | try_int_to_return!( 252 | mutex!(libcryptsetup_rs_sys::crypt_reencrypt_status( 253 | self.reference.as_ptr(), 254 | &mut params_reencrypt.inner as *mut _, 255 | )), 256 | CryptReencryptInfo 257 | ) 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/tests/encrypt.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{ 6 | ffi::CString, 7 | fs::{File, OpenOptions}, 8 | io::{self, Read, Write}, 9 | mem::MaybeUninit, 10 | path::{Path, PathBuf}, 11 | ptr, slice, 12 | }; 13 | 14 | use crate::{ 15 | consts::{ 16 | flags::{CryptActivate, CryptDeactivate, CryptKeyfile, CryptVolumeKey}, 17 | vals::EncryptionFormat, 18 | }, 19 | device::CryptInit, 20 | err::LibcryptErr, 21 | tests::loopback, 22 | Either, 23 | }; 24 | 25 | use libc::c_uint; 26 | use rand::random; 27 | 28 | /// Size of the sliding window used to search for random bytes on encrypted 29 | /// and unencrypted devices. 30 | const WINDOW_SIZE: usize = 1024 * 1024; 31 | 32 | fn init(dev_path: &Path, passphrase: &str) -> c_uint { 33 | let mut dev = CryptInit::init(dev_path).unwrap(); 34 | dev.context_handle() 35 | .format::<()>( 36 | EncryptionFormat::Luks2, 37 | ("aes", "xts-plain"), 38 | None, 39 | Either::Right(512 / 8), 40 | None, 41 | ) 42 | .unwrap(); 43 | dev.keyslot_handle() 44 | .add_by_key(None, None, passphrase.as_bytes(), CryptVolumeKey::empty()) 45 | .unwrap() 46 | } 47 | 48 | /// This method initializes the device with no encryption as a way to test 49 | /// that the plaintext can be read vs. the plaintext not being found due to 50 | /// proper encryption in the other tests. 51 | fn init_null_cipher(dev_path: &Path) -> c_uint { 52 | let mut dev = CryptInit::init(dev_path).unwrap(); 53 | dev.context_handle() 54 | .format::<()>( 55 | EncryptionFormat::Luks1, 56 | ("cipher_null", "ecb"), 57 | None, 58 | Either::Right(32), 59 | None, 60 | ) 61 | .unwrap(); 62 | dev.keyslot_handle() 63 | .add_by_passphrase(None, b"", b"") 64 | .unwrap() 65 | } 66 | 67 | fn init_by_keyfile(dev_path: &Path, keyfile_path: &Path) -> c_uint { 68 | let mut dev = CryptInit::init(dev_path).unwrap(); 69 | dev.context_handle() 70 | .format::<()>( 71 | EncryptionFormat::Luks2, 72 | ("aes", "xts-plain"), 73 | None, 74 | Either::Right(512 / 8), 75 | None, 76 | ) 77 | .unwrap(); 78 | let keyfile_contents = { 79 | let mut kf_handle = dev.keyfile_handle(); 80 | kf_handle 81 | .device_read(keyfile_path, 0, None, CryptKeyfile::empty()) 82 | .unwrap() 83 | }; 84 | dev.keyslot_handle() 85 | .add_by_key( 86 | None, 87 | None, 88 | keyfile_contents.as_ref(), 89 | CryptVolumeKey::empty(), 90 | ) 91 | .unwrap() 92 | } 93 | 94 | fn activate_without_explicit_format( 95 | dev_path: &Path, 96 | device_name: &'static str, 97 | keyslot: c_uint, 98 | passphrase: &'static str, 99 | ) { 100 | let mut dev = CryptInit::init(dev_path).unwrap(); 101 | dev.context_handle().load::<()>(None, None).unwrap(); 102 | dev.activate_handle() 103 | .activate_by_passphrase( 104 | Some(device_name), 105 | Some(keyslot), 106 | passphrase.as_bytes(), 107 | CryptActivate::empty(), 108 | ) 109 | .unwrap(); 110 | } 111 | 112 | fn activate_by_passphrase( 113 | dev_path: &Path, 114 | device_name: &'static str, 115 | keyslot: c_uint, 116 | passphrase: &'static str, 117 | ) { 118 | let mut dev = CryptInit::init(dev_path).unwrap(); 119 | dev.context_handle() 120 | .load::<()>(Some(EncryptionFormat::Luks2), None) 121 | .unwrap(); 122 | dev.activate_handle() 123 | .activate_by_passphrase( 124 | Some(device_name), 125 | Some(keyslot), 126 | passphrase.as_bytes(), 127 | CryptActivate::empty(), 128 | ) 129 | .unwrap(); 130 | } 131 | 132 | fn create_keyfile(loopback_file_path: &Path) -> PathBuf { 133 | let path = PathBuf::from(format!("{}-key", loopback_file_path.display())); 134 | let mut f = File::create(&path).unwrap(); 135 | let random: Vec<_> = (0..4096).map(|_| random::()).collect(); 136 | f.write(&random).unwrap(); 137 | path 138 | } 139 | 140 | fn activate_by_keyfile( 141 | dev_path: &Path, 142 | device_name: &'static str, 143 | keyslot: c_uint, 144 | keyfile_path: &Path, 145 | keyfile_size: Option, 146 | ) { 147 | let mut dev = CryptInit::init(dev_path).unwrap(); 148 | dev.context_handle() 149 | .load::<()>(Some(EncryptionFormat::Luks2), None) 150 | .unwrap(); 151 | dev.activate_handle() 152 | .activate_by_keyfile_device_offset( 153 | Some(device_name), 154 | Some(keyslot), 155 | keyfile_path, 156 | keyfile_size, 157 | 0, 158 | CryptActivate::empty(), 159 | ) 160 | .unwrap(); 161 | } 162 | 163 | fn activate_null_cipher(dev_path: &Path, device_name: &'static str) { 164 | let mut dev = CryptInit::init(dev_path).unwrap(); 165 | dev.context_handle().load::<()>(None, None).unwrap(); 166 | dev.activate_handle() 167 | .activate_by_passphrase(Some(device_name), None, b"", CryptActivate::empty()) 168 | .unwrap(); 169 | } 170 | 171 | fn write_random(device_name: &str) -> Result, io::Error> { 172 | let mapped_device_path = PathBuf::from(format!("/dev/mapper/{device_name}")); 173 | let mut random_buffer = Box::new([0; WINDOW_SIZE]); 174 | File::open("/dev/urandom")?.read_exact(&mut (*random_buffer))?; 175 | let mut device = OpenOptions::new().write(true).open(mapped_device_path)?; 176 | device.write_all(random_buffer.as_ref())?; 177 | Ok(random_buffer) 178 | } 179 | 180 | fn test_existence(file_path: &Path, buffer: &[u8]) -> Result { 181 | let file_path_cstring = 182 | CString::new(file_path.to_str().ok_or_else(|| { 183 | io::Error::new(io::ErrorKind::Other, "Failed to convert path to string") 184 | })?) 185 | .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; 186 | let fd = unsafe { libc::open(file_path_cstring.as_ptr(), libc::O_RDONLY) }; 187 | if fd < 0 { 188 | return Err(io::Error::last_os_error()); 189 | } 190 | let mut stat: MaybeUninit = MaybeUninit::zeroed(); 191 | let fstat_result = unsafe { libc::fstat(fd, stat.as_mut_ptr()) }; 192 | if fstat_result < 0 { 193 | return Err(io::Error::last_os_error()); 194 | } 195 | let device_size = unsafe { stat.assume_init() }.st_size as usize; 196 | let mapped_ptr = unsafe { 197 | libc::mmap( 198 | ptr::null_mut(), 199 | device_size, 200 | libc::PROT_READ, 201 | libc::MAP_SHARED, 202 | fd, 203 | 0, 204 | ) 205 | }; 206 | if mapped_ptr.is_null() { 207 | return Err(io::Error::new(io::ErrorKind::Other, "mmap failed")); 208 | } 209 | 210 | { 211 | let disk_bytes = unsafe { slice::from_raw_parts(mapped_ptr as *const u8, device_size) }; 212 | for chunk in disk_bytes.windows(WINDOW_SIZE) { 213 | if chunk == buffer { 214 | unsafe { 215 | libc::munmap(mapped_ptr, device_size); 216 | libc::close(fd); 217 | } 218 | return Ok(true); 219 | } 220 | } 221 | } 222 | 223 | unsafe { 224 | libc::munmap(mapped_ptr, device_size); 225 | libc::close(fd); 226 | } 227 | Ok(false) 228 | } 229 | 230 | /// Run a test on whether the plaintext could be found or not. Return a boolean 231 | /// as we actually want to see the plaintext in some cases and not in others. 232 | fn run_plaintext_test(dev_path: &Path, device_name: &str) -> Result { 233 | let write_result = write_random(device_name); 234 | 235 | if super::do_cleanup() { 236 | let mut dev = CryptInit::init_by_name_and_header(device_name, None)?; 237 | dev.activate_handle() 238 | .deactivate(device_name, CryptDeactivate::empty())?; 239 | } 240 | 241 | let buffer = write_result.map_err(|e| LibcryptErr::Other(e.to_string()))?; 242 | 243 | test_existence(dev_path, &buffer).map_err(|e| LibcryptErr::Other(e.to_string())) 244 | } 245 | 246 | pub fn test_encrypt_by_password() { 247 | loopback::use_loopback( 248 | 1024 * 1024 * 1024, 249 | super::format_with_zeros(), 250 | super::do_cleanup(), 251 | |dev_path, file_path| { 252 | let device_name = "test-device"; 253 | let passphrase = "abadpassphrase"; 254 | 255 | let keyslot = init(dev_path, passphrase); 256 | activate_by_passphrase(dev_path, device_name, keyslot, passphrase); 257 | if run_plaintext_test(file_path, device_name).unwrap() { 258 | panic!("Should not find plaintext"); 259 | } 260 | }, 261 | ) 262 | } 263 | 264 | pub fn test_encrypt_by_keyfile() { 265 | loopback::use_loopback( 266 | 1024 * 1024 * 1024, 267 | super::format_with_zeros(), 268 | super::do_cleanup(), 269 | |dev_path, file_path| { 270 | let device_name = "test-device"; 271 | 272 | let keyfile_path = create_keyfile(file_path); 273 | let keyslot = init_by_keyfile(dev_path, keyfile_path.as_path()); 274 | activate_by_keyfile(dev_path, device_name, keyslot, keyfile_path.as_path(), None); 275 | if run_plaintext_test(file_path, device_name).unwrap() { 276 | panic!("Should not find plaintext"); 277 | } 278 | }, 279 | ) 280 | } 281 | 282 | pub fn test_encrypt_by_password_without_explicit_format() { 283 | loopback::use_loopback( 284 | 1024 * 1024 * 1024, 285 | super::format_with_zeros(), 286 | super::do_cleanup(), 287 | |dev_path, file_path| { 288 | let device_name = "test-device"; 289 | let passphrase = "abadpassphrase"; 290 | 291 | let keyslot = init(dev_path, passphrase); 292 | activate_without_explicit_format(dev_path, device_name, keyslot, passphrase); 293 | if run_plaintext_test(file_path, device_name).unwrap() { 294 | panic!("Should not find plaintext"); 295 | } 296 | }, 297 | ) 298 | } 299 | 300 | pub fn test_unencrypted() { 301 | loopback::use_loopback( 302 | 1024 * 1024 * 1024, 303 | super::format_with_zeros(), 304 | super::do_cleanup(), 305 | |dev_path, file_path| { 306 | let device_name = "test-device"; 307 | 308 | init_null_cipher(dev_path); 309 | activate_null_cipher(dev_path, device_name); 310 | if !run_plaintext_test(file_path, device_name).unwrap() { 311 | panic!("Should find plaintext"); 312 | } 313 | }, 314 | ) 315 | } 316 | -------------------------------------------------------------------------------- /src/luks2/token.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::ptr; 6 | 7 | use crate::{consts::flags::CryptActivate, device::CryptDevice, err::LibcryptErr}; 8 | 9 | use libc::{c_char, c_int, c_uint, c_void}; 10 | 11 | /// Type representing the token status. This type wraps the `CRYPT_TOKEN_*` values and the optional corresponding token type as a string. 12 | pub enum CryptTokenInfo { 13 | /// Token invalid 14 | Invalid, 15 | /// Token is free (empty) 16 | Inactive, 17 | /// Active internal token with driver 18 | Internal(String), 19 | /// Active internal token (reserved name) with missing token driver 20 | InternalUnknown(String), 21 | /// Active external (user defined) token with driver 22 | External(String), 23 | /// Active external (user defined) token with missing token driver 24 | ExternalUnknown(String), 25 | } 26 | 27 | impl CryptTokenInfo { 28 | /// Convert a token status code into `CryptTokenInfo` 29 | pub fn from_status(code: c_uint, type_: Option) -> Result { 30 | Ok(match code { 31 | libcryptsetup_rs_sys::crypt_token_info_CRYPT_TOKEN_INVALID => CryptTokenInfo::Invalid, 32 | libcryptsetup_rs_sys::crypt_token_info_CRYPT_TOKEN_INACTIVE => CryptTokenInfo::Inactive, 33 | libcryptsetup_rs_sys::crypt_token_info_CRYPT_TOKEN_INTERNAL => { 34 | CryptTokenInfo::Internal(type_.ok_or(LibcryptErr::InvalidConversion)?) 35 | } 36 | libcryptsetup_rs_sys::crypt_token_info_CRYPT_TOKEN_INTERNAL_UNKNOWN => { 37 | CryptTokenInfo::InternalUnknown(type_.ok_or(LibcryptErr::InvalidConversion)?) 38 | } 39 | libcryptsetup_rs_sys::crypt_token_info_CRYPT_TOKEN_EXTERNAL => { 40 | CryptTokenInfo::External(type_.ok_or(LibcryptErr::InvalidConversion)?) 41 | } 42 | libcryptsetup_rs_sys::crypt_token_info_CRYPT_TOKEN_EXTERNAL_UNKNOWN => { 43 | CryptTokenInfo::ExternalUnknown(type_.ok_or(LibcryptErr::InvalidConversion)?) 44 | } 45 | _ => return Err(LibcryptErr::InvalidConversion), 46 | }) 47 | } 48 | } 49 | 50 | #[allow(clippy::from_over_into)] 51 | impl Into for CryptTokenInfo { 52 | fn into(self) -> u32 { 53 | match self { 54 | CryptTokenInfo::Invalid => libcryptsetup_rs_sys::crypt_token_info_CRYPT_TOKEN_INVALID, 55 | CryptTokenInfo::Inactive => libcryptsetup_rs_sys::crypt_token_info_CRYPT_TOKEN_INACTIVE, 56 | CryptTokenInfo::Internal(_) => { 57 | libcryptsetup_rs_sys::crypt_token_info_CRYPT_TOKEN_INTERNAL 58 | } 59 | CryptTokenInfo::InternalUnknown(_) => { 60 | libcryptsetup_rs_sys::crypt_token_info_CRYPT_TOKEN_INTERNAL_UNKNOWN 61 | } 62 | CryptTokenInfo::External(_) => { 63 | libcryptsetup_rs_sys::crypt_token_info_CRYPT_TOKEN_EXTERNAL 64 | } 65 | CryptTokenInfo::ExternalUnknown(_) => { 66 | libcryptsetup_rs_sys::crypt_token_info_CRYPT_TOKEN_EXTERNAL_UNKNOWN 67 | } 68 | } 69 | } 70 | } 71 | 72 | /// Token input for `CryptLuks2Token::json_set` 73 | pub enum TokenInput<'a> { 74 | /// Add a new token to any free slot 75 | AddToken(&'a serde_json::Value), 76 | /// Replace the specified token 77 | ReplaceToken(c_uint, &'a serde_json::Value), 78 | /// Remove the specified token 79 | RemoveToken(c_uint), 80 | } 81 | 82 | /// Handle for LUKS2 token operations 83 | pub struct CryptLuks2TokenHandle<'a> { 84 | reference: &'a mut CryptDevice, 85 | } 86 | 87 | impl<'a> CryptLuks2TokenHandle<'a> { 88 | pub(crate) fn new(reference: &'a mut CryptDevice) -> Self { 89 | CryptLuks2TokenHandle { reference } 90 | } 91 | 92 | /// Get contents of a token in JSON format 93 | pub fn json_get(&mut self, token: c_uint) -> Result { 94 | let mut ptr: *const c_char = std::ptr::null(); 95 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_token_json_get( 96 | self.reference.as_ptr(), 97 | token as c_int, 98 | &mut ptr as *mut _, 99 | ))) 100 | .and_then(|_| from_str_ptr!(ptr)) 101 | .and_then(|s| serde_json::from_str(s).map_err(LibcryptErr::JsonError)) 102 | } 103 | 104 | /// Set contents of a token in JSON format 105 | pub fn json_set(&mut self, input: TokenInput<'_>) -> Result { 106 | let (token, json) = match input { 107 | TokenInput::AddToken(json) => (libcryptsetup_rs_sys::CRYPT_ANY_TOKEN, Some(json)), 108 | TokenInput::ReplaceToken(token, json) => (token as i32, Some(json)), 109 | TokenInput::RemoveToken(token) => (token as i32, None), 110 | }; 111 | let json_cstring = match json { 112 | Some(j) => Some( 113 | serde_json::to_string(j) 114 | .map_err(LibcryptErr::JsonError) 115 | .and_then(|s| to_cstring!(s))?, 116 | ), 117 | None => None, 118 | }; 119 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_token_json_set( 120 | self.reference.as_ptr(), 121 | token, 122 | json_cstring 123 | .as_ref() 124 | .map(|cs| cs.as_ptr()) 125 | .unwrap_or(ptr::null()), 126 | ))) 127 | .map(|rc| rc as c_uint) 128 | } 129 | 130 | /// Get the token info for a specific token 131 | pub fn status(&mut self, token: c_uint) -> Result { 132 | let mut ptr: *const c_char = std::ptr::null(); 133 | let code = mutex!(libcryptsetup_rs_sys::crypt_token_status( 134 | self.reference.as_ptr(), 135 | token as c_int, 136 | &mut ptr as *mut _, 137 | )); 138 | CryptTokenInfo::from_status( 139 | code, 140 | match ptr_to_option!(ptr) { 141 | Some(p) => Some(from_str_ptr_to_owned!(p)?), 142 | None => None, 143 | }, 144 | ) 145 | } 146 | 147 | /// Create new LUKS2 keyring token 148 | pub fn luks2_keyring_set( 149 | &mut self, 150 | token: Option, 151 | key_description: &str, 152 | ) -> Result { 153 | let description_cstring = to_cstring!(key_description)?; 154 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_token_luks2_keyring_set( 155 | self.reference.as_ptr(), 156 | token 157 | .map(|t| t as c_int) 158 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_TOKEN), 159 | &libcryptsetup_rs_sys::crypt_token_params_luks2_keyring { 160 | key_description: description_cstring.as_ptr(), 161 | } as *const _, 162 | ))) 163 | .map(|rc| rc as c_uint) 164 | } 165 | 166 | /// Get LUKS2 keyring token description 167 | pub fn luks2_keyring_get(&mut self, token: c_uint) -> Result { 168 | let mut params = libcryptsetup_rs_sys::crypt_token_params_luks2_keyring { 169 | key_description: std::ptr::null(), 170 | }; 171 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_token_luks2_keyring_get( 172 | self.reference.as_ptr(), 173 | token as c_int, 174 | &mut params as *mut _, 175 | ))) 176 | .and_then(|_| from_str_ptr!(params.key_description).map(|s| s.to_string())) 177 | } 178 | 179 | /// Assign token to keyslot 180 | /// 181 | /// `None` for keyslot assigns all keyslots to the token 182 | pub fn assign_keyslot( 183 | &mut self, 184 | token: c_uint, 185 | keyslot: Option, 186 | ) -> Result<(), LibcryptErr> { 187 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_token_assign_keyslot( 188 | self.reference.as_ptr(), 189 | token as c_int, 190 | keyslot 191 | .map(|k| k as c_int) 192 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_SLOT), 193 | ))) 194 | .map(|_| ()) 195 | } 196 | 197 | /// Unassign token from keyslot 198 | /// 199 | /// `None` for keyslot unassigns the token from all active keyslots 200 | pub fn unassign_keyslot( 201 | &mut self, 202 | token: c_uint, 203 | keyslot: Option, 204 | ) -> Result<(), LibcryptErr> { 205 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_token_unassign_keyslot( 206 | self.reference.as_ptr(), 207 | token as c_int, 208 | keyslot 209 | .map(|k| k as c_int) 210 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_SLOT), 211 | ))) 212 | .map(|_| ()) 213 | } 214 | 215 | /// Check if token is assigned 216 | #[allow(clippy::wrong_self_convention)] 217 | pub fn is_assigned(&mut self, token: c_uint, keyslot: c_uint) -> Result { 218 | let rc = mutex!(libcryptsetup_rs_sys::crypt_token_is_assigned( 219 | self.reference.as_ptr(), 220 | token as c_int, 221 | keyslot as c_int, 222 | )); 223 | if rc == 0 { 224 | Ok(true) 225 | } else if rc == libc::ENOENT { 226 | Ok(false) 227 | } else { 228 | Err(LibcryptErr::IOError(std::io::Error::from_raw_os_error(-rc))) 229 | } 230 | } 231 | 232 | /// Activate device or check key using a token 233 | pub fn activate_by_token( 234 | &mut self, 235 | name: Option<&str>, 236 | token: Option, 237 | usrdata: Option<&mut T>, 238 | flags: CryptActivate, 239 | ) -> Result { 240 | let name_cstring_option = match name { 241 | Some(n) => Some(to_cstring!(n)?), 242 | None => None, 243 | }; 244 | let usrdata_ptr = match usrdata { 245 | Some(reference) => (reference as *mut T).cast::(), 246 | None => ptr::null_mut(), 247 | }; 248 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_activate_by_token( 249 | self.reference.as_ptr(), 250 | match name_cstring_option { 251 | Some(ref s) => s.as_ptr(), 252 | None => std::ptr::null(), 253 | }, 254 | token 255 | .map(|t| t as c_int) 256 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_TOKEN), 257 | usrdata_ptr, 258 | flags.bits(), 259 | ))) 260 | .map(|rc| rc as c_uint) 261 | } 262 | 263 | /// Activate device or check key using a token and PIN 264 | #[cfg(cryptsetup24supported)] 265 | pub fn activate_by_token_pin( 266 | &mut self, 267 | name: Option<&str>, 268 | type_: Option<&str>, 269 | token: Option, 270 | pin: &[u8], 271 | usrdata: Option<&mut T>, 272 | flags: CryptActivate, 273 | ) -> Result { 274 | let name_cstring_option = match name { 275 | Some(n) => Some(to_cstring!(n)?), 276 | None => None, 277 | }; 278 | let type_cstring_option = match type_ { 279 | Some(t) => Some(to_cstring!(t)?), 280 | None => None, 281 | }; 282 | let usrdata_ptr = match usrdata { 283 | Some(reference) => (reference as *mut T).cast::(), 284 | None => ptr::null_mut(), 285 | }; 286 | errno_int_success!(mutex!(libcryptsetup_rs_sys::crypt_activate_by_token_pin( 287 | self.reference.as_ptr(), 288 | // NOTE: Must keep as_ref to avoid use after free error. 289 | name_cstring_option 290 | .as_ref() 291 | .map(|s| s.as_ptr()) 292 | .unwrap_or_else(ptr::null), 293 | // NOTE: Must keep as_ref to avoid use after free error. 294 | type_cstring_option 295 | .as_ref() 296 | .map(|s| s.as_ptr()) 297 | .unwrap_or_else(ptr::null), 298 | token 299 | .map(|t| t as c_int) 300 | .unwrap_or(libcryptsetup_rs_sys::CRYPT_ANY_TOKEN), 301 | to_byte_ptr!(pin), 302 | pin.len(), 303 | usrdata_ptr, 304 | flags.bits(), 305 | ))) 306 | .map(|rc| rc as c_uint) 307 | } 308 | } 309 | 310 | /// Register token handler 311 | pub fn register( 312 | name: &'static str, 313 | open: libcryptsetup_rs_sys::crypt_token_open_func, 314 | buffer_free: libcryptsetup_rs_sys::crypt_token_buffer_free_func, 315 | validate: libcryptsetup_rs_sys::crypt_token_validate_func, 316 | dump: libcryptsetup_rs_sys::crypt_token_dump_func, 317 | ) -> Result<(), LibcryptErr> { 318 | if name.get(name.len() - 1..) != Some("\0") { 319 | return Err(LibcryptErr::NoNull(name)); 320 | } 321 | let handler = libcryptsetup_rs_sys::crypt_token_handler { 322 | name: name.as_ptr().cast::(), 323 | open, 324 | buffer_free, 325 | validate, 326 | dump, 327 | }; 328 | errno!(mutex!(libcryptsetup_rs_sys::crypt_token_register( 329 | &handler as *const libcryptsetup_rs_sys::crypt_token_handler, 330 | ))) 331 | } 332 | -------------------------------------------------------------------------------- /src/consts/vals.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | use std::{ffi::CStr, ops::Deref}; 6 | 7 | use libc::{c_char, c_int}; 8 | 9 | use crate::err::LibcryptErr; 10 | 11 | consts_to_from_enum!( 12 | /// Debug log level 13 | CryptDebugLevel, c_int, 14 | All => libcryptsetup_rs_sys::CRYPT_DEBUG_ALL as c_int, 15 | Json => libcryptsetup_rs_sys::CRYPT_DEBUG_JSON as c_int, 16 | None => libcryptsetup_rs_sys::CRYPT_DEBUG_NONE as c_int 17 | ); 18 | 19 | /// Device formatting type options 20 | #[derive(Debug, Eq, PartialEq)] 21 | pub enum EncryptionFormat { 22 | #[allow(missing_docs)] 23 | Plain, 24 | #[allow(missing_docs)] 25 | Luks1, 26 | #[allow(missing_docs)] 27 | Luks2, 28 | #[allow(missing_docs)] 29 | Loopaes, 30 | #[allow(missing_docs)] 31 | Verity, 32 | #[allow(missing_docs)] 33 | Tcrypt, 34 | #[allow(missing_docs)] 35 | Integrity, 36 | } 37 | 38 | impl EncryptionFormat { 39 | /// Get `EncryptionFormat` as a char pointer 40 | pub(crate) fn as_ptr(&self) -> *const c_char { 41 | match *self { 42 | EncryptionFormat::Plain => libcryptsetup_rs_sys::CRYPT_PLAIN.as_ptr().cast::(), 43 | EncryptionFormat::Luks1 => libcryptsetup_rs_sys::CRYPT_LUKS1.as_ptr().cast::(), 44 | EncryptionFormat::Luks2 => libcryptsetup_rs_sys::CRYPT_LUKS2.as_ptr().cast::(), 45 | EncryptionFormat::Loopaes => libcryptsetup_rs_sys::CRYPT_LOOPAES 46 | .as_ptr() 47 | .cast::(), 48 | EncryptionFormat::Verity => { 49 | libcryptsetup_rs_sys::CRYPT_VERITY.as_ptr().cast::() 50 | } 51 | EncryptionFormat::Tcrypt => { 52 | libcryptsetup_rs_sys::CRYPT_TCRYPT.as_ptr().cast::() 53 | } 54 | EncryptionFormat::Integrity => libcryptsetup_rs_sys::CRYPT_INTEGRITY 55 | .as_ptr() 56 | .cast::(), 57 | } 58 | } 59 | 60 | /// Get `EncryptionFormat` from a char pointer 61 | pub(crate) fn from_ptr(p: *const c_char) -> Result { 62 | let p_bytes = unsafe { CStr::from_ptr(p) }.to_bytes_with_nul(); 63 | if libcryptsetup_rs_sys::CRYPT_PLAIN == p_bytes { 64 | Ok(EncryptionFormat::Plain) 65 | } else if libcryptsetup_rs_sys::CRYPT_LUKS1 == p_bytes { 66 | Ok(EncryptionFormat::Luks1) 67 | } else if libcryptsetup_rs_sys::CRYPT_LUKS2 == p_bytes { 68 | Ok(EncryptionFormat::Luks2) 69 | } else if libcryptsetup_rs_sys::CRYPT_LOOPAES == p_bytes { 70 | Ok(EncryptionFormat::Loopaes) 71 | } else if libcryptsetup_rs_sys::CRYPT_VERITY == p_bytes { 72 | Ok(EncryptionFormat::Verity) 73 | } else if libcryptsetup_rs_sys::CRYPT_TCRYPT == p_bytes { 74 | Ok(EncryptionFormat::Tcrypt) 75 | } else if libcryptsetup_rs_sys::CRYPT_INTEGRITY == p_bytes { 76 | Ok(EncryptionFormat::Integrity) 77 | } else { 78 | Err(LibcryptErr::InvalidConversion) 79 | } 80 | } 81 | } 82 | 83 | consts_to_from_enum!( 84 | /// Value indicating the status of a keyslot 85 | KeyslotInfo, 86 | u32, 87 | Invalid => libcryptsetup_rs_sys::crypt_keyslot_info_CRYPT_SLOT_INVALID, 88 | Inactive => libcryptsetup_rs_sys::crypt_keyslot_info_CRYPT_SLOT_INACTIVE, 89 | Active => libcryptsetup_rs_sys::crypt_keyslot_info_CRYPT_SLOT_ACTIVE, 90 | ActiveLast => libcryptsetup_rs_sys::crypt_keyslot_info_CRYPT_SLOT_ACTIVE_LAST, 91 | Unbound => libcryptsetup_rs_sys::crypt_keyslot_info_CRYPT_SLOT_UNBOUND 92 | ); 93 | 94 | consts_to_from_enum!( 95 | /// Value indicating the priority of a keyslot 96 | KeyslotPriority, 97 | i32, 98 | Invalid => libcryptsetup_rs_sys::crypt_keyslot_priority_CRYPT_SLOT_PRIORITY_INVALID, 99 | Ignore => libcryptsetup_rs_sys::crypt_keyslot_priority_CRYPT_SLOT_PRIORITY_IGNORE, 100 | Normal => libcryptsetup_rs_sys::crypt_keyslot_priority_CRYPT_SLOT_PRIORITY_NORMAL, 101 | Prefer => libcryptsetup_rs_sys::crypt_keyslot_priority_CRYPT_SLOT_PRIORITY_PREFER 102 | ); 103 | 104 | /// Logging levels 105 | #[derive(Debug, Eq, PartialEq)] 106 | pub enum CryptLogLevel { 107 | #[allow(missing_docs)] 108 | Normal = libcryptsetup_rs_sys::CRYPT_LOG_NORMAL as isize, 109 | #[allow(missing_docs)] 110 | Error = libcryptsetup_rs_sys::CRYPT_LOG_ERROR as isize, 111 | #[allow(missing_docs)] 112 | Verbose = libcryptsetup_rs_sys::CRYPT_LOG_VERBOSE as isize, 113 | #[allow(missing_docs)] 114 | Debug = libcryptsetup_rs_sys::CRYPT_LOG_DEBUG as isize, 115 | #[allow(missing_docs)] 116 | DebugJson = libcryptsetup_rs_sys::CRYPT_LOG_DEBUG_JSON as isize, 117 | } 118 | 119 | impl TryFrom for CryptLogLevel { 120 | type Error = LibcryptErr; 121 | 122 | fn try_from(v: c_int) -> Result>::Error> { 123 | let level = match v { 124 | i if i == CryptLogLevel::Normal as c_int => CryptLogLevel::Normal, 125 | i if i == CryptLogLevel::Error as c_int => CryptLogLevel::Error, 126 | i if i == CryptLogLevel::Verbose as c_int => CryptLogLevel::Verbose, 127 | i if i == CryptLogLevel::Debug as c_int => CryptLogLevel::Debug, 128 | i if i == CryptLogLevel::DebugJson as c_int => CryptLogLevel::DebugJson, 129 | _ => return Err(LibcryptErr::InvalidConversion), 130 | }; 131 | Ok(level) 132 | } 133 | } 134 | 135 | pub(crate) enum CryptFlagsType { 136 | Activation = libcryptsetup_rs_sys::crypt_flags_type_CRYPT_FLAGS_ACTIVATION as isize, 137 | Requirements = libcryptsetup_rs_sys::crypt_flags_type_CRYPT_FLAGS_REQUIREMENTS as isize, 138 | } 139 | 140 | consts_to_from_enum!( 141 | /// Encryption mode flags 142 | CryptReencryptInfo, 143 | u32, 144 | None => libcryptsetup_rs_sys::crypt_reencrypt_info_CRYPT_REENCRYPT_NONE, 145 | Clean => libcryptsetup_rs_sys::crypt_reencrypt_info_CRYPT_REENCRYPT_CLEAN, 146 | Crash => libcryptsetup_rs_sys::crypt_reencrypt_info_CRYPT_REENCRYPT_CRASH, 147 | Invalid => libcryptsetup_rs_sys::crypt_reencrypt_info_CRYPT_REENCRYPT_INVALID 148 | ); 149 | 150 | consts_to_from_enum!( 151 | /// Encryption mode flags 152 | CryptReencryptModeInfo, 153 | u32, 154 | Reencrypt => libcryptsetup_rs_sys::crypt_reencrypt_mode_info_CRYPT_REENCRYPT_REENCRYPT, 155 | Encrypt => libcryptsetup_rs_sys::crypt_reencrypt_mode_info_CRYPT_REENCRYPT_ENCRYPT, 156 | Decrypt => libcryptsetup_rs_sys::crypt_reencrypt_mode_info_CRYPT_REENCRYPT_DECRYPT 157 | ); 158 | 159 | consts_to_from_enum!( 160 | /// Reencryption direction flags 161 | CryptReencryptDirectionInfo, 162 | u32, 163 | Forward => libcryptsetup_rs_sys::crypt_reencrypt_direction_info_CRYPT_REENCRYPT_FORWARD, 164 | Backward => libcryptsetup_rs_sys::crypt_reencrypt_direction_info_CRYPT_REENCRYPT_BACKWARD 165 | ); 166 | 167 | /// Rust representation of key generator enum 168 | #[derive(Debug, Eq, PartialEq)] 169 | pub enum CryptKdf { 170 | #[allow(missing_docs)] 171 | Pbkdf2, 172 | #[allow(missing_docs)] 173 | Argon2I, 174 | #[allow(missing_docs)] 175 | Argon2Id, 176 | } 177 | 178 | impl CryptKdf { 179 | /// Convert to a `char *` for C 180 | pub(crate) fn as_ptr(&self) -> *const c_char { 181 | match *self { 182 | CryptKdf::Pbkdf2 => libcryptsetup_rs_sys::CRYPT_KDF_PBKDF2 183 | .as_ptr() 184 | .cast::(), 185 | CryptKdf::Argon2I => libcryptsetup_rs_sys::CRYPT_KDF_ARGON2I 186 | .as_ptr() 187 | .cast::(), 188 | CryptKdf::Argon2Id => libcryptsetup_rs_sys::CRYPT_KDF_ARGON2ID 189 | .as_ptr() 190 | .cast::(), 191 | } 192 | } 193 | 194 | /// Convert from a C `char *` 195 | pub(crate) fn from_ptr(ptr: *const c_char) -> Result { 196 | let p_bytes = unsafe { CStr::from_ptr(ptr) }.to_bytes_with_nul(); 197 | if libcryptsetup_rs_sys::CRYPT_KDF_PBKDF2 == p_bytes { 198 | Ok(CryptKdf::Pbkdf2) 199 | } else if libcryptsetup_rs_sys::CRYPT_KDF_ARGON2I == p_bytes { 200 | Ok(CryptKdf::Argon2I) 201 | } else if libcryptsetup_rs_sys::CRYPT_KDF_ARGON2ID == p_bytes { 202 | Ok(CryptKdf::Argon2Id) 203 | } else { 204 | Err(LibcryptErr::InvalidConversion) 205 | } 206 | } 207 | } 208 | 209 | consts_to_from_enum!( 210 | /// Rust representation of random number generator enum 211 | CryptRng, 212 | u32, 213 | Urandom => libcryptsetup_rs_sys::CRYPT_RNG_URANDOM, 214 | Random => libcryptsetup_rs_sys::CRYPT_RNG_RANDOM 215 | ); 216 | 217 | /// LUKS type (1 or 2) 218 | #[derive(Debug, Eq, PartialEq)] 219 | pub enum LuksType { 220 | #[allow(missing_docs)] 221 | Luks1, 222 | #[allow(missing_docs)] 223 | Luks2, 224 | } 225 | 226 | impl LuksType { 227 | /// Convert Rust expression to an equivalent C pointer 228 | pub(crate) fn as_ptr(&self) -> *const c_char { 229 | match *self { 230 | LuksType::Luks1 => libcryptsetup_rs_sys::CRYPT_LUKS1.as_ptr().cast::(), 231 | LuksType::Luks2 => libcryptsetup_rs_sys::CRYPT_LUKS2.as_ptr().cast::(), 232 | } 233 | } 234 | } 235 | 236 | consts_to_from_enum!( 237 | /// Status of a crypt device 238 | CryptStatusInfo, u32, 239 | Invalid => libcryptsetup_rs_sys::crypt_status_info_CRYPT_INVALID, 240 | Inactive => libcryptsetup_rs_sys::crypt_status_info_CRYPT_INACTIVE, 241 | Active => libcryptsetup_rs_sys::crypt_status_info_CRYPT_ACTIVE, 242 | Busy => libcryptsetup_rs_sys::crypt_status_info_CRYPT_BUSY 243 | ); 244 | 245 | consts_to_from_enum!( 246 | /// Pattern for disk wipe 247 | CryptWipePattern, u32, 248 | Zero => libcryptsetup_rs_sys::crypt_wipe_pattern_CRYPT_WIPE_ZERO, 249 | Random => libcryptsetup_rs_sys::crypt_wipe_pattern_CRYPT_WIPE_RANDOM, 250 | EncryptedZero => libcryptsetup_rs_sys::crypt_wipe_pattern_CRYPT_WIPE_ENCRYPTED_ZERO, 251 | Special => libcryptsetup_rs_sys::crypt_wipe_pattern_CRYPT_WIPE_SPECIAL 252 | ); 253 | 254 | /// Size allocated for metadata 255 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] 256 | pub enum MetadataSize { 257 | #[allow(missing_docs)] 258 | Default, 259 | #[allow(missing_docs)] 260 | Kb16, 261 | #[allow(missing_docs)] 262 | Kb32, 263 | #[allow(missing_docs)] 264 | Kb64, 265 | #[allow(missing_docs)] 266 | Kb128, 267 | #[allow(missing_docs)] 268 | Kb256, 269 | #[allow(missing_docs)] 270 | Kb512, 271 | #[allow(missing_docs)] 272 | Kb1024, 273 | #[allow(missing_docs)] 274 | Kb2048, 275 | #[allow(missing_docs)] 276 | Kb4096, 277 | } 278 | 279 | impl TryFrom for MetadataSize { 280 | type Error = LibcryptErr; 281 | 282 | fn try_from(v: u64) -> Result { 283 | let size = match v { 284 | i if i == *MetadataSize::Default => MetadataSize::Default, 285 | i if i == *MetadataSize::Kb16 => MetadataSize::Kb16, 286 | i if i == *MetadataSize::Kb32 => MetadataSize::Kb32, 287 | i if i == *MetadataSize::Kb64 => MetadataSize::Kb64, 288 | i if i == *MetadataSize::Kb128 => MetadataSize::Kb128, 289 | i if i == *MetadataSize::Kb256 => MetadataSize::Kb256, 290 | i if i == *MetadataSize::Kb512 => MetadataSize::Kb512, 291 | i if i == *MetadataSize::Kb1024 => MetadataSize::Kb1024, 292 | i if i == *MetadataSize::Kb2048 => MetadataSize::Kb2048, 293 | i if i == *MetadataSize::Kb4096 => MetadataSize::Kb4096, 294 | _ => return Err(LibcryptErr::InvalidConversion), 295 | }; 296 | Ok(size) 297 | } 298 | } 299 | 300 | impl Deref for MetadataSize { 301 | type Target = u64; 302 | 303 | fn deref(&self) -> &u64 { 304 | match *self { 305 | MetadataSize::Default => &0, 306 | MetadataSize::Kb16 => &0x4000, 307 | MetadataSize::Kb32 => &0x8000, 308 | MetadataSize::Kb64 => &0x10000, 309 | MetadataSize::Kb128 => &0x20000, 310 | MetadataSize::Kb256 => &0x40000, 311 | MetadataSize::Kb512 => &0x80000, 312 | MetadataSize::Kb1024 => &0x100000, 313 | MetadataSize::Kb2048 => &0x200000, 314 | MetadataSize::Kb4096 => &0x400000, 315 | } 316 | } 317 | } 318 | 319 | /// Size in bytes for the keyslots. 320 | /// 321 | /// The value must be divisible by a 4KB block and no larger than 322 | /// 128MB. 323 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] 324 | pub struct KeyslotsSize(u64); 325 | 326 | impl KeyslotsSize { 327 | // 4KB block size in bytes 328 | const FOUR_KB: u64 = 1 << 12; 329 | // 128MB max size in bytes 330 | const MAX_MB: u64 = 1 << 27; 331 | } 332 | 333 | impl Deref for KeyslotsSize { 334 | type Target = u64; 335 | 336 | fn deref(&self) -> &u64 { 337 | &self.0 338 | } 339 | } 340 | 341 | impl TryFrom for KeyslotsSize { 342 | type Error = LibcryptErr; 343 | 344 | fn try_from(v: u64) -> Result { 345 | // Must be divisible by 4KB and less than or equal to 128MB 346 | if v > Self::MAX_MB || v % Self::FOUR_KB != 0 { 347 | return Err(LibcryptErr::InvalidConversion); 348 | } 349 | 350 | Ok(KeyslotsSize(v)) 351 | } 352 | } 353 | 354 | /// State of memory lock 355 | #[derive(Debug, Eq, PartialEq)] 356 | pub enum LockState { 357 | #[allow(missing_docs)] 358 | Unlocked = 0, 359 | #[allow(missing_docs)] 360 | Locked = 1, 361 | } 362 | 363 | impl From for LockState { 364 | fn from(v: c_int) -> Self { 365 | match v { 366 | i if i == LockState::Unlocked as c_int => LockState::Unlocked, 367 | _ => LockState::Locked, 368 | } 369 | } 370 | } 371 | 372 | #[cfg(test)] 373 | mod test { 374 | use super::*; 375 | 376 | #[test] 377 | fn test_metadata_size() { 378 | assert_eq!(MetadataSize::try_from(0).unwrap(), MetadataSize::Default); 379 | assert_eq!(MetadataSize::try_from(0x4000).unwrap(), MetadataSize::Kb16); 380 | assert_eq!(MetadataSize::try_from(0x10000).unwrap(), MetadataSize::Kb64); 381 | assert!(MetadataSize::try_from(0x10001).is_err()); 382 | } 383 | 384 | #[test] 385 | fn test_keyslots_size() { 386 | // Default 387 | assert!(KeyslotsSize::try_from(0).is_ok()); 388 | // Exactly 128MB 389 | assert!(KeyslotsSize::try_from(1 << 27).is_ok()); 390 | // Greater than 128MB 391 | assert!(KeyslotsSize::try_from(1 << 28).is_err()); 392 | // Less than 4KB 393 | assert!(KeyslotsSize::try_from(1 << 11).is_err()); 394 | // Exactly 4KB 395 | assert!(KeyslotsSize::try_from(1 << 12).is_ok()); 396 | // Greater than 4KB and not divisible by 4KB 397 | assert!(KeyslotsSize::try_from(4097).is_err()); 398 | 399 | // Assert that derefs are equal to the starting value 400 | assert!(*KeyslotsSize::try_from(1 << 27).unwrap() == (1 << 27)); 401 | } 402 | } 403 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | /// Wrap all libcryptsetup_rs_sys calls in the macro. It will expand to a 6 | /// feature-flagged mutex lock call. If the `mutex` feature is not enabled, it is 7 | /// a no-op. 8 | macro_rules! mutex { 9 | ( $libcryptsetup_call:expr ) => {{ 10 | #[cfg(feature = "mutex")] 11 | #[allow(unused_variables)] 12 | let lock = $crate::MUTEX.acquire(); 13 | 14 | #[cfg(not(feature = "mutex"))] 15 | if *$crate::THREAD_ID != std::thread::current().id() { 16 | panic!("Enable the mutex feature for this crate to allow calling libcryptsetup methods from multiple threads"); 17 | } 18 | 19 | unsafe { $libcryptsetup_call } 20 | }}; 21 | } 22 | 23 | /// Convert an errno-zero-success return pattern into a `Result<(), LibcryptErr>` 24 | macro_rules! errno { 25 | ( $rc:expr ) => { 26 | match $rc { 27 | i if i < 0 => { 28 | return Err($crate::err::LibcryptErr::IOError( 29 | std::io::Error::from_raw_os_error(-i), 30 | )) 31 | } 32 | i if i > 0 => panic!("Unexpected return value {}", i), 33 | _ => Result::<(), $crate::err::LibcryptErr>::Ok(()), 34 | } 35 | }; 36 | } 37 | 38 | /// Convert an errno-positive-int-success return pattern into a `Result` 39 | macro_rules! errno_int_success { 40 | ( $rc:expr ) => { 41 | match $rc { 42 | i if i < 0 => { 43 | return Err($crate::err::LibcryptErr::IOError( 44 | std::io::Error::from_raw_os_error(-i), 45 | )) 46 | } 47 | i => Result::<_, $crate::err::LibcryptErr>::Ok(i), 48 | } 49 | }; 50 | } 51 | 52 | /// Convert an integer return value into specified type 53 | macro_rules! int_to_return { 54 | ( $rc:expr, $type:ty ) => { 55 | <$type>::from($rc) 56 | }; 57 | } 58 | 59 | /// Try converting an integer return value into specified type 60 | macro_rules! try_int_to_return { 61 | ( $rc:expr, $type:ty ) => { 62 | <$type>::try_from($rc) 63 | }; 64 | } 65 | 66 | /// Convert a pointer to an `Option` containing a pointer 67 | macro_rules! ptr_to_option { 68 | ( $ptr:expr ) => {{ 69 | let p = $ptr; 70 | if p.is_null() { 71 | None 72 | } else { 73 | Some(p) 74 | } 75 | }}; 76 | } 77 | 78 | /// Convert a pointer to a `Option` containing a reference 79 | macro_rules! ptr_to_option_with_reference { 80 | ( $ptr:expr ) => {{ 81 | let p = $ptr; 82 | unsafe { p.as_ref() } 83 | }}; 84 | } 85 | 86 | /// Convert a pointer to an `Result` containing a pointer 87 | macro_rules! ptr_to_result { 88 | ( $ptr:expr ) => {{ 89 | ptr_to_option!($ptr).ok_or($crate::err::LibcryptErr::NullPtr) 90 | }}; 91 | } 92 | 93 | /// Convert a pointer to a `Result` containing a reference 94 | macro_rules! ptr_to_result_with_reference { 95 | ( $ptr:expr ) => {{ 96 | let p = $ptr; 97 | unsafe { p.as_ref() }.ok_or($crate::err::LibcryptErr::NullPtr) 98 | }}; 99 | } 100 | 101 | /// Convert a `Path` type into `CString` 102 | macro_rules! path_to_cstring { 103 | ( $path:expr ) => { 104 | match $path 105 | .to_str() 106 | .ok_or_else(|| LibcryptErr::InvalidConversion) 107 | .and_then(|s| std::ffi::CString::new(s).map_err(LibcryptErr::NullError)) 108 | { 109 | Ok(s) => Ok(s), 110 | Err(e) => Err(e), 111 | } 112 | }; 113 | } 114 | 115 | /// Convert a string type into `CString` 116 | macro_rules! to_cstring { 117 | ( $str:expr ) => { 118 | match std::ffi::CString::new($str.as_bytes()) { 119 | Ok(s) => Ok(s), 120 | Err(e) => Err($crate::err::LibcryptErr::NullError(e)), 121 | } 122 | }; 123 | } 124 | 125 | /// Convert a byte slice into `*const c_char` 126 | macro_rules! to_byte_ptr { 127 | ( $bytes:expr ) => { 128 | $bytes.as_ptr().cast::() 129 | }; 130 | } 131 | 132 | /// Convert a byte slice into `*mut c_char` 133 | macro_rules! to_mut_byte_ptr { 134 | ( $bytes:expr ) => { 135 | $bytes.as_mut_ptr().cast::() 136 | }; 137 | } 138 | 139 | /// Convert a `*const c_char` into a `&str` type 140 | #[macro_export] 141 | macro_rules! from_str_ptr { 142 | ( $str_ptr:expr ) => {{ 143 | let str_ptr = $str_ptr; 144 | unsafe { ::std::ffi::CStr::from_ptr(str_ptr) } 145 | .to_str() 146 | .map_err($crate::LibcryptErr::Utf8Error) 147 | }}; 148 | } 149 | 150 | /// Convert a `*const c_char` into a `String` type 151 | macro_rules! from_str_ptr_to_owned { 152 | ( $str_ptr:expr ) => { 153 | unsafe { ::std::ffi::CStr::from_ptr($str_ptr) } 154 | .to_str() 155 | .map_err($crate::err::LibcryptErr::Utf8Error) 156 | .map(|s| s.to_string()) 157 | }; 158 | } 159 | 160 | /// Convert constants to and from a flag enum 161 | macro_rules! consts_to_from_enum { 162 | ( #[$meta:meta] $flag_enum:ident, $flag_type:ty, $( $name:ident => $constant:expr ),* ) => { 163 | #[$meta] 164 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 165 | pub enum $flag_enum { 166 | $( 167 | #[allow(missing_docs)] 168 | $name, 169 | )* 170 | } 171 | 172 | #[allow(clippy::from_over_into)] 173 | impl std::convert::Into<$flag_type> for $flag_enum { 174 | fn into(self) -> $flag_type { 175 | match self { 176 | $( 177 | $flag_enum::$name => $constant, 178 | )* 179 | } 180 | } 181 | } 182 | 183 | impl std::convert::TryFrom<$flag_type> for $flag_enum { 184 | type Error = $crate::err::LibcryptErr; 185 | 186 | fn try_from(v: $flag_type) -> Result { 187 | Ok(match v { 188 | $( 189 | i if i == $constant => $flag_enum::$name, 190 | )* 191 | _ => return Err($crate::err::LibcryptErr::InvalidConversion), 192 | }) 193 | } 194 | } 195 | }; 196 | } 197 | 198 | #[macro_export] 199 | /// Create a C-compatible static string with a null byte 200 | macro_rules! c_str { 201 | ( $str:tt ) => { 202 | concat!($str, "\0") 203 | }; 204 | } 205 | 206 | #[macro_export] 207 | /// Create a C-compatible callback to determine user confirmation which wraps safe Rust code 208 | macro_rules! c_confirm_callback { 209 | ( $fn_name:ident, $type:ty, $safe_fn_name:ident ) => { 210 | extern "C" fn $fn_name( 211 | msg: *const std::os::raw::c_char, 212 | usrptr: *mut std::os::raw::c_void, 213 | ) -> std::os::raw::c_int { 214 | let msg_str = 215 | $crate::from_str_ptr!(msg).expect("Invalid message string passed to cryptsetup-rs"); 216 | let generic_ptr = usrptr.cast::<$type>(); 217 | let generic_ref = unsafe { generic_ptr.as_mut() }; 218 | 219 | $safe_fn_name(msg_str, generic_ref) as std::os::raw::c_int 220 | } 221 | }; 222 | } 223 | 224 | #[macro_export] 225 | /// Create a C-compatible logging callback which wraps safe Rust code 226 | macro_rules! c_logging_callback { 227 | ( $fn_name:ident, $type:ty, $safe_fn_name:ident ) => { 228 | extern "C" fn $fn_name( 229 | level: std::os::raw::c_int, 230 | msg: *const std::os::raw::c_char, 231 | usrptr: *mut std::os::raw::c_void, 232 | ) { 233 | let level = <$crate::consts::vals::CryptLogLevel as std::convert::TryFrom< 234 | std::os::raw::c_int, 235 | >>::try_from(level) 236 | .expect("Invalid logging level passed to cryptsetup-rs"); 237 | let msg_str = $crate::from_str_ptr!(msg) 238 | .expect("Invalid message string passed to cryptsetup-rs") 239 | .trim(); 240 | let generic_ptr = usrptr.cast::<$type>(); 241 | let generic_ref = unsafe { generic_ptr.as_mut() }; 242 | 243 | $safe_fn_name(level, msg_str, generic_ref); 244 | } 245 | }; 246 | } 247 | 248 | #[macro_export] 249 | /// Create a C-compatible progress callback for wiping a device which wraps safe Rust code 250 | macro_rules! c_progress_callback { 251 | ( $fn_name:ident, $type:ty, $safe_fn_name:ident ) => { 252 | extern "C" fn $fn_name( 253 | size: u64, 254 | offset: u64, 255 | usrptr: *mut std::os::raw::c_void, 256 | ) -> std::os::raw::c_int { 257 | let generic_ptr = usrptr.cast::<$type>(); 258 | let generic_ref = unsafe { generic_ptr.as_mut() }; 259 | 260 | $safe_fn_name(size, offset, generic_ref) as std::os::raw::c_int 261 | } 262 | }; 263 | } 264 | 265 | #[macro_export] 266 | /// Create a C-compatible open callback compatible with `CryptTokenHandler` 267 | macro_rules! c_token_handler_open { 268 | ( $fn_name:ident, $type:ty, $safe_fn_name:ident ) => { 269 | extern "C" fn $fn_name( 270 | cd: *mut libcryptsetup_rs_sys::crypt_device, 271 | token_id: std::os::raw::c_int, 272 | buffer: *mut *mut std::os::raw::c_char, 273 | buffer_len: *mut $crate::SizeT, 274 | usrptr: *mut std::os::raw::c_void, 275 | ) -> std::os::raw::c_int { 276 | let device = $crate::device::CryptDevice::from_ptr(cd); 277 | let generic_ptr = usrptr as *mut $type; 278 | let generic_ref = unsafe { generic_ptr.as_mut() }; 279 | 280 | let buffer: Result, $crate::LibcryptErr> = 281 | $safe_fn_name(device, token_id, generic_ref); 282 | match buffer { 283 | Ok(()) => { 284 | *buffer = Box::into_raw(buffer) as *mut std::os::raw::c_char; 285 | 0 286 | } 287 | Err(_) => -1, 288 | } 289 | } 290 | }; 291 | } 292 | 293 | #[macro_export] 294 | /// Create a C-compatible callback for free compatible with `CryptTokenHandler` 295 | macro_rules! c_token_handler_free { 296 | ( $fn_name:ident, $safe_fn_name:ident ) => { 297 | extern "C" fn $fn_name(buffer: *mut std::os::raw::c_void, buffer_len: $crate::SizeT) { 298 | let boxed_slice = unsafe { 299 | Box::from_raw(std::slice::from_raw_parts_mut( 300 | buffer as *mut u8, 301 | buffer_len as usize, 302 | )) 303 | }; 304 | 305 | $safe_fn_name(boxed_slice) 306 | } 307 | }; 308 | } 309 | 310 | #[macro_export] 311 | /// Create a C-compatible callback for validate compatible with `CryptTokenHandler` 312 | macro_rules! c_token_handler_validate { 313 | ( $fn_name:ident, $safe_fn_name:ident ) => { 314 | extern "C" fn $fn_name( 315 | cd: *mut libcryptsetup_rs_sys::crypt_device, 316 | json: *mut std::os::raw::c_char, 317 | ) -> std::os::raw::c_int { 318 | let device = $crate::device::CryptDevice::from_ptr(cd); 319 | let s = match $crate::from_str_ptr!(json) { 320 | Ok(s) => s, 321 | Err(_) => return -1, 322 | }; 323 | let json_obj = match serde_json::from_str(s) { 324 | Ok(j) => j, 325 | Err(_) => return -1, 326 | }; 327 | 328 | let rc: Result<(), $crate::LibcryptErr> = $safe_fn_name(device, json_obj); 329 | match rc { 330 | Ok(()) => 0, 331 | Err(_) => -1, 332 | } 333 | } 334 | }; 335 | } 336 | 337 | #[macro_export] 338 | /// Create a C-compatible callback for compatible with `CryptTokenHandler` 339 | macro_rules! c_token_handler_dump { 340 | ( $fn_name:ident, $safe_fn_name:ident ) => { 341 | extern "C" fn $fn_name( 342 | cd: *mut libcryptsetup_rs_sys::crypt_device, 343 | json: *mut std::os::raw::c_char, 344 | ) { 345 | let device = $crate::device::CryptDevice::from_ptr(cd); 346 | let s = match $crate::from_str_ptr!(json) { 347 | Ok(s) => s, 348 | Err(_) => return, 349 | }; 350 | let json_obj = match serde_json::from_str(s) { 351 | Ok(j) => j, 352 | Err(_) => return, 353 | }; 354 | 355 | $safe_fn_name(device, json_obj) 356 | } 357 | }; 358 | } 359 | 360 | #[cfg(test)] 361 | mod test { 362 | use crate::consts::vals::CryptLogLevel; 363 | 364 | fn safe_confirm_callback(_msg: &str, usrdata: Option<&mut u32>) -> bool { 365 | *usrdata.unwrap() != 0 366 | } 367 | 368 | c_confirm_callback!(confirm_callback, u32, safe_confirm_callback); 369 | 370 | fn safe_logging_callback(_level: CryptLogLevel, _msg: &str, _usrdata: Option<&mut u32>) {} 371 | 372 | c_logging_callback!(logging_callback, u32, safe_logging_callback); 373 | 374 | fn safe_progress_callback(_size: u64, _offset: u64, usrdata: Option<&mut u32>) -> bool { 375 | *usrdata.unwrap() != 0 376 | } 377 | 378 | c_progress_callback!(progress_callback, u32, safe_progress_callback); 379 | 380 | #[test] 381 | fn test_c_confirm_callback() { 382 | let ret = confirm_callback( 383 | "\0".as_ptr().cast::(), 384 | (&mut 1u32 as *mut u32).cast::(), 385 | ); 386 | assert_eq!(1, ret); 387 | 388 | let ret = confirm_callback( 389 | "\0".as_ptr().cast::(), 390 | (&mut 0u32 as *mut u32).cast::(), 391 | ); 392 | assert_eq!(0, ret); 393 | } 394 | 395 | #[test] 396 | fn test_c_logging_callback() { 397 | logging_callback( 398 | libcryptsetup_rs_sys::CRYPT_LOG_ERROR as i32, 399 | "\0".as_ptr().cast::(), 400 | (&mut 1u32 as *mut u32).cast::(), 401 | ); 402 | 403 | logging_callback( 404 | libcryptsetup_rs_sys::CRYPT_LOG_DEBUG, 405 | "\0".as_ptr().cast::(), 406 | (&mut 0u32 as *mut u32).cast::(), 407 | ); 408 | } 409 | 410 | #[test] 411 | fn test_c_progress_callback() { 412 | let ret = progress_callback(0, 0, (&mut 1u32 as *mut u32).cast::()); 413 | assert_eq!(1, ret); 414 | 415 | let ret = progress_callback(0, 0, (&mut 0u32 as *mut u32).cast::()); 416 | assert_eq!(0, ret); 417 | } 418 | 419 | consts_to_from_enum!( 420 | /// An enum for testing `PartialEq` 421 | PETestEnum, 422 | u16, 423 | This => 0, 424 | Can => 1, 425 | Use => 2, 426 | PartialEq => 3 427 | ); 428 | 429 | #[test] 430 | fn test_enum_partial_eq() { 431 | assert_eq!(PETestEnum::This, PETestEnum::try_from(0).unwrap()); 432 | assert_eq!(PETestEnum::Can, PETestEnum::try_from(1).unwrap()); 433 | assert_eq!(PETestEnum::Use, PETestEnum::try_from(2).unwrap()); 434 | assert_eq!(PETestEnum::PartialEq, PETestEnum::try_from(3).unwrap()); 435 | } 436 | 437 | #[cfg(not(feature = "mutex"))] 438 | #[test] 439 | #[should_panic(expected = "Enable the mutex feature")] 440 | fn test_multiple_threads_no_mutex_feature() { 441 | std::thread::spawn(|| { 442 | crate::get_sector_size(None); 443 | }) 444 | .join() 445 | .unwrap(); 446 | crate::get_sector_size(None); 447 | } 448 | } 449 | --------------------------------------------------------------------------------