libcryptsetup_rs/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
5use std::{
6    ffi::CString,
7    os::raw::{c_int, c_uint, c_void},
8    ptr,
9};
10
11use libcryptsetup_rs_sys::{crypt_params_reencrypt, CRYPT_ANY_SLOT};
12
13use 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
23type 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
27pub 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: Box<CryptParamsLuks2Ref<'a>>,
34    #[allow(dead_code)]
35    resilience_cstring: CString,
36    #[allow(dead_code)]
37    hash_cstring: CString,
38}
39
40impl CryptParamsReencryptRef<'_> {
41    fn as_ptr(&self) -> *const crypt_params_reencrypt {
42        (&self.inner as *const crypt_params_reencrypt).cast::<crypt_params_reencrypt>()
43    }
44}
45
46/// Parameters for reencryption operations
47pub 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: CryptParamsLuks2,
64    /// Reencryption flags
65    pub flags: CryptReencrypt,
66}
67
68impl<'a> TryInto<CryptParamsReencryptRef<'a>> for &'a CryptParamsReencrypt {
69    type Error = LibcryptErr;
70
71    fn try_into(self) -> Result<CryptParamsReencryptRef<'a>, Self::Error> {
72        let mut luks2_params: Box<CryptParamsLuks2Ref<'a>> = Box::new((&self.luks2).try_into()?);
73
74        let resilience_cstring = to_cstring!(self.resilience)?;
75        let hash_cstring = to_cstring!(self.hash)?;
76
77        let inner = libcryptsetup_rs_sys::crypt_params_reencrypt {
78            mode: self.mode.into(),
79            direction: self.direction.into(),
80            resilience: resilience_cstring.as_ptr(),
81            hash: hash_cstring.as_ptr(),
82            data_shift: self.data_shift,
83            max_hotzone_size: self.max_hotzone_size,
84            device_size: self.device_size,
85            luks2: luks2_params.as_ptr().cast(),
86            flags: self.flags.bits(),
87        };
88        Ok(CryptParamsReencryptRef {
89            inner,
90            reference: self,
91            luks2_params,
92            resilience_cstring,
93            hash_cstring,
94        })
95    }
96}
97
98/// Handle for reencryption operations
99pub struct CryptLuks2ReencryptHandle<'a> {
100    reference: &'a mut CryptDevice,
101}
102
103impl<'a> CryptLuks2ReencryptHandle<'a> {
104    pub(crate) fn new(reference: &'a mut CryptDevice) -> Self {
105        CryptLuks2ReencryptHandle { reference }
106    }
107
108    /// Initialize reencryption metadata on a device by passphrase
109    pub fn reencrypt_init_by_passphrase(
110        &mut self,
111        name: Option<&str>,
112        passphrase: &[u8],
113        keyslot_old: Option<c_uint>,
114        keyslot_new: Option<c_uint>,
115        cipher_and_mode: Option<(&str, &str)>,
116        params: CryptParamsReencrypt,
117    ) -> Result<c_int, LibcryptErr> {
118        let name_cstring = match name {
119            Some(n) => Some(to_cstring!(n)?),
120            None => None,
121        };
122        let (cipher_cstring_err, cipher_mode_cstring_err) = cipher_and_mode
123            .map(|(c, cm)| {
124                (
125                    Some(to_cstring!(c)).transpose(),
126                    Some(to_cstring!(cm)).transpose(),
127                )
128            })
129            .unwrap_or_else(|| (Ok(None), Ok(None)));
130        let cipher_cstring = cipher_cstring_err?;
131        let cipher_mode_cstring = cipher_mode_cstring_err?;
132        let params_reencrypt: CryptParamsReencryptRef<'_> = (&params).try_into()?;
133
134        errno_int_success!(mutex!(
135            libcryptsetup_rs_sys::crypt_reencrypt_init_by_passphrase(
136                self.reference.as_ptr(),
137                name_cstring
138                    .as_ref()
139                    .map(|cs| cs.as_ptr())
140                    .unwrap_or_else(ptr::null),
141                to_byte_ptr!(passphrase),
142                passphrase.len(),
143                keyslot_old.map(|k| k as c_int).unwrap_or(CRYPT_ANY_SLOT),
144                keyslot_new.map(|k| k as c_int).unwrap_or(CRYPT_ANY_SLOT),
145                // NOTE: Must keep as_ref to avoid use after free error.
146                cipher_cstring
147                    .as_ref()
148                    .map(|s| s.as_ptr())
149                    .unwrap_or_else(ptr::null),
150                // NOTE: Must keep as_ref to avoid use after free error.
151                cipher_mode_cstring
152                    .as_ref()
153                    .map(|s| s.as_ptr())
154                    .unwrap_or_else(ptr::null),
155                params_reencrypt.as_ptr()
156            )
157        ))
158    }
159
160    /// Initialize reencryption metadata on a device by passphrase in a keyring
161    pub fn reencrypt_init_by_keyring(
162        &mut self,
163        name: Option<&str>,
164        key_description: &str,
165        keyslot_old: Option<c_uint>,
166        keyslot_new: Option<c_uint>,
167        cipher_and_mode: Option<(&str, &str)>,
168        params: CryptParamsReencrypt,
169    ) -> Result<c_int, LibcryptErr> {
170        let name_cstring = match name {
171            Some(n) => Some(to_cstring!(n)?),
172            None => None,
173        };
174        let (cipher_cstring_err, cipher_mode_cstring_err) = cipher_and_mode
175            .map(|(c, cm)| {
176                (
177                    Some(to_cstring!(c)).transpose(),
178                    Some(to_cstring!(cm)).transpose(),
179                )
180            })
181            .unwrap_or_else(|| (Ok(None), Ok(None)));
182        let cipher_cstring = cipher_cstring_err?;
183        let cipher_mode_cstring = cipher_mode_cstring_err?;
184        let params_reencrypt: CryptParamsReencryptRef<'_> = (&params).try_into()?;
185
186        let description_cstring = to_cstring!(key_description)?;
187        errno_int_success!(mutex!(
188            libcryptsetup_rs_sys::crypt_reencrypt_init_by_keyring(
189                self.reference.as_ptr(),
190                name_cstring
191                    .as_ref()
192                    .map(|cs| cs.as_ptr())
193                    .unwrap_or(ptr::null()),
194                description_cstring.as_ptr(),
195                keyslot_old.map(|k| k as c_int).unwrap_or(CRYPT_ANY_SLOT),
196                keyslot_new.map(|k| k as c_int).unwrap_or(CRYPT_ANY_SLOT),
197                // NOTE: Must keep as_ref to avoid use after free error.
198                cipher_cstring
199                    .as_ref()
200                    .map(|s| s.as_ptr())
201                    .unwrap_or_else(ptr::null),
202                // NOTE: Must keep as_ref to avoid use after free error.
203                cipher_mode_cstring
204                    .as_ref()
205                    .map(|s| s.as_ptr())
206                    .unwrap_or_else(ptr::null),
207                params_reencrypt.as_ptr(),
208            )
209        ))
210    }
211
212    /// Run data reencryption
213    pub fn reencrypt(&mut self, progress: Option<ReencryptProgress>) -> Result<(), LibcryptErr> {
214        errno!(mutex!(libcryptsetup_rs_sys::crypt_reencrypt(
215            self.reference.as_ptr(),
216            progress
217        )))
218    }
219
220    /// Run data reencryption
221    ///
222    /// This method provides a bug fix for the API added in libcryptsetup 2.4.0
223    #[cfg(cryptsetup24supported)]
224    pub fn reencrypt2<T>(
225        &mut self,
226        progress: Option<ReencryptProgress>,
227        usrdata: Option<&mut T>,
228    ) -> Result<(), LibcryptErr> {
229        let usrptr = usrdata
230            .map(|data| (data as *mut T).cast::<c_void>())
231            .unwrap_or_else(ptr::null_mut);
232        errno!(mutex!(libcryptsetup_rs_sys::crypt_reencrypt_run(
233            self.reference.as_ptr(),
234            progress,
235            usrptr,
236        )))
237    }
238
239    /// LUKS2 reencryption status
240    pub fn status(
241        &mut self,
242        params: CryptParamsReencrypt,
243    ) -> Result<CryptReencryptInfo, LibcryptErr> {
244        let mut params_reencrypt: CryptParamsReencryptRef<'_> = (&params).try_into()?;
245        try_int_to_return!(
246            mutex!(libcryptsetup_rs_sys::crypt_reencrypt_status(
247                self.reference.as_ptr(),
248                &mut params_reencrypt.inner as *mut _,
249            )),
250            CryptReencryptInfo
251        )
252    }
253}