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: Option<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: Option<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: Option<Box<CryptParamsLuks2Ref<'a>>> = 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
105pub struct CryptLuks2ReencryptHandle<'a> {
106    reference: &'a mut CryptDevice,
107}
108
109impl<'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<c_uint>,
120        keyslot_new: Option<c_uint>,
121        cipher_and_mode: Option<(&str, &str)>,
122        params: CryptParamsReencrypt,
123    ) -> Result<c_int, LibcryptErr> {
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<'_> = (&params).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<c_uint>,
172        keyslot_new: Option<c_uint>,
173        cipher_and_mode: Option<(&str, &str)>,
174        params: CryptParamsReencrypt,
175    ) -> Result<c_int, LibcryptErr> {
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<'_> = (&params).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<ReencryptProgress>) -> 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<T>(
231        &mut self,
232        progress: Option<ReencryptProgress>,
233        usrdata: Option<&mut T>,
234    ) -> Result<(), LibcryptErr> {
235        let usrptr = usrdata
236            .map(|data| (data as *mut T).cast::<c_void>())
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<CryptReencryptInfo, LibcryptErr> {
250        let mut params_reencrypt: CryptParamsReencryptRef<'_> = (&params).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}