cryptsetup_rs/
api.rs

1//! High-level API to work with `libcryptsetup` supported devices (disks)
2//! The main focus is on LUKS1 and LUKS2 devices
3
4use std::fmt;
5use std::fs::File;
6use std::path::{Path, PathBuf};
7use std::ptr;
8
9use either::Either;
10use either::Either::{Left, Right};
11use uuid;
12
13use blkid_rs::{LuksHeader, LuksVersionedHeader};
14use raw;
15pub use raw::{crypt_pbkdf_algo_type, crypt_status_info, crypt_token_info};
16
17pub use crate::device::{
18    Error, Keyslot, Luks2TokenHandler, Luks2TokenHandlerBox, Luks2TokenHandlerRaw, Luks2TokenId, Result,
19};
20use crate::device::{Luks2FormatPbkdf, RawDevice};
21pub use crate::global::enable_debug;
22use crate::luks1::Luks1Params;
23use crate::luks2::Luks2Params;
24pub use crate::luks2_meta::{Luks2Metadata, Luks2Token};
25
26pub type Luks1CryptDeviceHandle = CryptDeviceHandle<Luks1Params>;
27pub type Luks2CryptDeviceHandle = CryptDeviceHandle<Luks2Params>;
28
29/// Builder to open a crypt device at the specified path
30///
31/// # Examples
32///
33/// ```
34/// use cryptsetup_rs::*;
35/// # fn foo() -> Result<()> {
36/// let device = open("/dev/loop0")?.luks1()?;
37/// # Ok(())
38/// # }
39/// ```
40pub fn open<P: AsRef<Path>>(path: P) -> Result<CryptDeviceOpenBuilder> {
41    let cd = crate::device::init(path.as_ref())?;
42    Ok(CryptDeviceOpenBuilder {
43        path: path.as_ref().to_owned(),
44        cd,
45    })
46}
47
48/// Query the status of an active crypt device
49pub fn status(name: &str) -> crypt_status_info {
50    crate::device::status_only(name)
51}
52
53/// Builder to format a crypt device at the specified path
54///
55/// # Examples
56///
57/// ```
58/// # extern crate uuid;
59/// # extern crate cryptsetup_rs;
60/// use cryptsetup_rs::*;
61/// use uuid::Uuid;
62///
63/// # fn foo() -> Result<()> {
64/// let uuid = Uuid::new_v4();
65/// let device = format("/dev/loop0")?
66///     .rng_type(crypt_rng_type::CRYPT_RNG_URANDOM)
67///     .iteration_time(5000)
68///     .luks1("aes", "xts-plain", "sha256", 256, Some(&uuid))?;
69/// # Ok(())
70/// # }
71/// ```
72///
73/// For LUKS2:
74///
75/// ```
76/// # extern crate cryptsetup_rs;
77/// use cryptsetup_rs::*;
78///
79/// # fn foo() -> Result<()> {
80/// let device = format("/dev/loop0")?
81///     .luks2("aes", "xts-plain", 256, None, None, None)
82///     .label("test")
83///     .argon2i("sha256", 200, 1, 1024, 1)
84///     .start();
85/// # Ok(())
86/// # }
87/// ```
88///
89pub fn format<P: AsRef<Path>>(path: P) -> Result<CryptDeviceFormatBuilder> {
90    let cd = crate::device::init(path.as_ref())?;
91    Ok(CryptDeviceFormatBuilder {
92        path: path.as_ref().to_owned(),
93        cd,
94    })
95}
96
97/// Read the LUKS version used by a LUKS container without opening the device
98pub fn luks_version<P: AsRef<Path>>(path: P) -> Result<u16> {
99    let device_file = File::open(path.as_ref())?;
100    let header = LuksHeader::read(device_file)?;
101    Ok(header.version())
102}
103
104/// Read the LUKS version used by a LUKS container without opening the device
105pub fn luks_uuid<P: AsRef<Path>>(path: P) -> Result<uuid::Uuid> {
106    let device_file = File::open(path.as_ref())?;
107    let uuid = LuksHeader::read(device_file)?.uuid()?;
108    Ok(uuid)
109}
110
111/// Read the UUID of a LUKS1 container without opening the device
112///
113/// Please use `luks_uuid()` instead
114#[deprecated]
115pub fn luks1_uuid<P: AsRef<Path>>(path: P) -> Result<uuid::Uuid> {
116    luks_uuid(path)
117}
118
119/// Struct containing state for the `open()` builder
120pub struct CryptDeviceOpenBuilder {
121    path: PathBuf,
122    cd: RawDevice,
123}
124
125impl CryptDeviceOpenBuilder {
126    /// Loads an existing LUKS1 crypt device
127    pub fn luks1(self: CryptDeviceOpenBuilder) -> Result<CryptDeviceHandle<Luks1Params>> {
128        let _ = crate::device::load(&self.cd, raw::crypt_device_type::LUKS1);
129        let params = Luks1Params::from_path(&self.path)?;
130        Ok(CryptDeviceHandle {
131            cd: self.cd,
132            path: self.path,
133            params,
134        })
135    }
136
137    /// Loads an existing LUKS2 crypt device
138    pub fn luks2(self: CryptDeviceOpenBuilder) -> Result<CryptDeviceHandle<Luks2Params>> {
139        let _ = crate::device::load(&self.cd, raw::crypt_device_type::LUKS2);
140        let params = Luks2Params::from_path(&self.path)?;
141        Ok(CryptDeviceHandle {
142            cd: self.cd,
143            path: self.path,
144            params,
145        })
146    }
147
148    pub fn luks(
149        self: CryptDeviceOpenBuilder,
150    ) -> Result<Either<CryptDeviceHandle<Luks1Params>, CryptDeviceHandle<Luks2Params>>> {
151        match luks_version(&self.path)? {
152            1 => self.luks1().map(|d| Left(d)),
153            2 => self.luks2().map(|d| Right(d)),
154            _ => Err(Error::InvalidLuksVersion),
155        }
156    }
157}
158
159/// Struct containing state for the `format()` builder
160pub struct CryptDeviceFormatBuilder {
161    path: PathBuf,
162    cd: RawDevice,
163}
164
165#[derive(Default)]
166struct Luks2FormatBuilderParams<'a> {
167    label: Option<&'a str>,
168    subsystem: Option<&'a str>,
169    data_device: Option<&'a Path>,
170    pbkdf: Option<Luks2FormatPbkdf<'a>>,
171}
172
173pub struct CryptDeviceLuks2FormatBuilder<'a> {
174    path: PathBuf,
175    cd: RawDevice,
176    // common params
177    cipher: &'a str,
178    cipher_mode: &'a str,
179    mk_bits: usize,
180    maybe_uuid: Option<&'a uuid::Uuid>,
181    // luks2 specifics
182    data_alignment: usize,
183    sector_size: u32,
184    other: Luks2FormatBuilderParams<'a>,
185}
186
187impl<'a> CryptDeviceLuks2FormatBuilder<'a> {
188    /// Set device primary label
189    pub fn label(mut self, label: &'a str) -> Self {
190        self.other.label = Some(label);
191        self
192    }
193
194    /// Set device secondary label, 'subsystem'
195    pub fn subsystem(mut self, subsystem: &'a str) -> Self {
196        self.other.subsystem = Some(subsystem);
197        self
198    }
199
200    /// Set path to data device (this will result in a split header)
201    pub fn data_device(mut self, p: &'a Path) -> Self {
202        self.other.data_device = Some(p);
203        self
204    }
205
206    /// Set PBKDF parameters for pbkdf2
207    pub fn pbkdf2(mut self, hash: &'a str, time_ms: u32, iterations: u32) -> Self {
208        self.other.pbkdf = Some(Luks2FormatPbkdf {
209            type_: crypt_pbkdf_algo_type::pbkdf2,
210            hash,
211            time_ms,
212            iterations,
213            max_memory_kb: 0,
214            parallel_threads: 0,
215            flags: 0,
216        });
217        self
218    }
219
220    /// Set PBKDF parameters for argon2i
221    pub fn argon2i(
222        mut self,
223        hash: &'a str,
224        time_ms: u32,
225        iterations: u32,
226        max_memory_kb: u32,
227        parallel_threads: u32,
228    ) -> Self {
229        self.other.pbkdf = Some(Luks2FormatPbkdf {
230            type_: crypt_pbkdf_algo_type::argon2i,
231            hash,
232            time_ms,
233            iterations,
234            max_memory_kb,
235            parallel_threads,
236            flags: 0,
237        });
238        self
239    }
240
241    /// Set PBKDF parameters for argon2id
242    pub fn argon2id(
243        mut self,
244        hash: &'a str,
245        time_ms: u32,
246        iterations: u32,
247        max_memory_kb: u32,
248        parallel_threads: u32,
249    ) -> Self {
250        self.other.pbkdf = Some(Luks2FormatPbkdf {
251            type_: crypt_pbkdf_algo_type::argon2id,
252            hash,
253            time_ms,
254            iterations,
255            max_memory_kb,
256            parallel_threads,
257            flags: 0,
258        });
259        self
260    }
261
262    /// Format a new block device as a LUKS2 crypt device with specified parameters
263    pub fn start(mut self) -> Result<CryptDeviceHandle<Luks2Params>> {
264        let _ = crate::device::luks2_format(
265            &mut self.cd,
266            self.cipher,
267            self.cipher_mode,
268            self.mk_bits,
269            self.data_alignment,
270            self.sector_size,
271            self.other.label,
272            self.other.subsystem,
273            self.other.data_device,
274            self.maybe_uuid,
275            self.other.pbkdf.as_ref(),
276            None,
277        )?;
278        let params = Luks2Params::from_path(&self.path)?;
279        Ok(CryptDeviceHandle {
280            cd: self.cd,
281            path: self.path,
282            params,
283        })
284    }
285}
286
287impl CryptDeviceFormatBuilder {
288    /// Set the iteration time for the `PBKDF2` function. Note that this does not affect the MK iterations.
289    pub fn iteration_time(mut self, iteration_time_ms: u64) -> Self {
290        #[allow(deprecated)]
291        crate::device::set_iteration_time(&mut self.cd, iteration_time_ms);
292        self
293    }
294
295    /// Set the random number generator to use
296    pub fn rng_type(mut self, rng_type: raw::crypt_rng_type) -> Self {
297        crate::device::set_rng_type(&mut self.cd, rng_type);
298        self
299    }
300
301    /// Formats a new block device as a LUKS1 crypt device with the specified parameters
302    pub fn luks1(
303        mut self: CryptDeviceFormatBuilder,
304        cipher: &str,
305        cipher_mode: &str,
306        hash: &str,
307        mk_bits: usize,
308        maybe_uuid: Option<&uuid::Uuid>,
309    ) -> Result<CryptDeviceHandle<Luks1Params>> {
310        let _ = crate::device::luks1_format(&mut self.cd, cipher, cipher_mode, hash, mk_bits, maybe_uuid)?;
311        let params = Luks1Params::from_path(&self.path)?;
312        Ok(CryptDeviceHandle {
313            cd: self.cd,
314            path: self.path,
315            params,
316        })
317    }
318
319    /// Set the format to LUKS2, and build further options
320    pub fn luks2<'a>(
321        self: CryptDeviceFormatBuilder,
322        cipher: &'a str,
323        cipher_mode: &'a str,
324        mk_bits: usize,
325        maybe_uuid: Option<&'a uuid::Uuid>,
326        maybe_data_alignment: Option<u32>,
327        maybe_sector_size: Option<u32>,
328    ) -> CryptDeviceLuks2FormatBuilder<'a> {
329        CryptDeviceLuks2FormatBuilder {
330            path: self.path,
331            cd: self.cd,
332            cipher,
333            cipher_mode,
334            mk_bits,
335            maybe_uuid,
336            data_alignment: maybe_data_alignment.unwrap_or(0) as usize,
337            sector_size: maybe_sector_size.unwrap_or(512),
338            other: Default::default(),
339        }
340    }
341}
342
343/// Trait representing common operations on a crypt device
344pub trait CryptDevice {
345    /// Path the device was opened/created with
346    fn path(&self) -> &Path;
347
348    /// Name of cipher used
349    fn cipher(&self) -> &str;
350
351    /// Name of cipher mode used
352    fn cipher_mode(&self) -> &str;
353
354    /// Path to the underlying device (as reported by `libcryptsetup`)
355    fn device_name(&self) -> &str;
356
357    /// Random number generator used for operations on this crypt device
358    fn rng_type(&self) -> raw::crypt_rng_type;
359
360    /// Sets the random number generator to use
361    fn set_rng_type(&mut self, rng_type: raw::crypt_rng_type);
362
363    /// Sets the iteration time for the `PBKDF2` function. Note that this does not affect the MK iterations.
364    fn set_iteration_time(&mut self, iteration_time_ms: u64);
365
366    /// Volume key size (in bytes)
367    fn volume_key_size(&self) -> u8;
368}
369
370/// Trait for querying the device type at runtime
371pub trait CryptDeviceType {
372    /// Type of the crypt device
373    fn device_type(&self) -> raw::crypt_device_type;
374}
375
376// TODO: consider different state for activated device, this would require tracking status
377
378pub trait LuksCryptDevice: CryptDevice + CryptDeviceType {
379    /// Activate the crypt device, and give it the specified name
380    fn activate(&mut self, name: &str, key: &[u8]) -> Result<Keyslot>;
381
382    /// Deactivate the crypt device, remove the device-mapper mapping and key information from kernel
383    fn deactivate(self, name: &str) -> Result<()>;
384
385    /// Destroy (and disable) key slot
386    fn destroy_keyslot(&mut self, slot: Keyslot) -> Result<()>;
387
388    /// Get status of key slot
389    fn keyslot_status(&self, keyslot: Keyslot) -> raw::crypt_keyslot_info;
390
391    /// Dump text-formatted information about the current device to stdout
392    fn dump(&self);
393
394    /// UUID of the current device
395    fn uuid(&self) -> uuid::Uuid;
396
397    /// Add a new keyslot with the specified key
398    fn add_keyslot(
399        &mut self,
400        key: &[u8],
401        maybe_prev_key: Option<&[u8]>,
402        maybe_keyslot: Option<Keyslot>,
403    ) -> Result<Keyslot>;
404
405    /// Replace an old key with a new one
406    fn update_keyslot(&mut self, key: &[u8], prev_key: &[u8], maybe_keyslot: Option<Keyslot>) -> Result<Keyslot>;
407}
408
409/// Trait representing specific operations on a LUKS1 device
410pub trait Luks1CryptDevice: LuksCryptDevice {
411    /// Get the hash algorithm used
412    fn hash_spec(&self) -> &str;
413
414    /// Number of bits in the master key
415    fn mk_bits(&self) -> u32;
416
417    /// Master key header digest
418    fn mk_digest(&self) -> &[u8; 20];
419
420    /// Master key `PBKDF2` iterations
421    fn mk_iterations(&self) -> u32;
422
423    /// Master key salt
424    fn mk_salt(&self) -> &[u8; 32];
425
426    /// Get the offset of the payload
427    fn payload_offset(&self) -> u32;
428}
429
430/// Trait representing specific operations on a LUKS2 device
431pub trait Luks2CryptDevice: LuksCryptDevice {
432    /// Register a LUKS2 token handler
433    fn register_new_token_handler<Handler: Luks2TokenHandlerRaw>() -> Result<Luks2TokenHandlerBox<Handler>>;
434
435    /// Register a LUKS2 token handler given a reference to it
436    fn register_token_handler<Handler: Luks2TokenHandlerRaw>(handler: &Luks2TokenHandlerBox<Handler>) -> Result<()>;
437
438    /// Get token status for a given token id
439    fn token_status(&mut self, token_id: Luks2TokenId) -> (crypt_token_info, Option<String>);
440
441    /// Get a token by id
442    fn get_token(&mut self, token_id: Luks2TokenId) -> Result<Luks2Token>;
443
444    /// Add a token with a specific id
445    fn add_token_with_id(&mut self, token: &Luks2Token, token_id: Luks2TokenId) -> Result<()>;
446
447    /// Add a token, returning the allocated token id
448    fn add_token(&mut self, token: &Luks2Token) -> Result<Luks2TokenId>;
449
450    /// Remove a token by id
451    fn remove_token(&mut self, token_id: Luks2TokenId) -> Result<()>;
452
453    /// Assign a token id to a keyslot (or all active keyslots if no keyslot is specified)
454    fn assign_token_to_keyslot(&mut self, token_id: Luks2TokenId, keyslot_opt: Option<Keyslot>) -> Result<()>;
455
456    /// Unassing a token from a keyslot (or all active keyslots if no keyslot is specified)
457    fn unassign_token_keyslot(&mut self, token_id: Luks2TokenId, keyslot_opt: Option<Keyslot>) -> Result<()>;
458
459    /// Check whether a token is assigned to a given keyslot
460    fn token_keyslot_is_assigned(&mut self, token_id: Luks2TokenId, keyslot: Keyslot) -> Result<bool>;
461
462    /// Activate the crypt device with the specified name and token
463    fn activate_with_token(&mut self, name: &str, token_id: Luks2TokenId) -> Result<Keyslot>;
464
465    /// Check activation of a device with a token
466    fn check_activation_with_token(&mut self, token_id: Luks2TokenId) -> Result<Keyslot>;
467
468    /// Set PBKDF parameters (used during next keyslot registration)
469    fn set_pbkdf_params(
470        &mut self,
471        type_: crypt_pbkdf_algo_type,
472        hash: &str,
473        time_ms: u32,
474        iterations: u32,
475        max_memory_kb: u32,
476        parallel_threads: u32,
477    ) -> Result<()>;
478}
479
480/// An opaque handle on an initialized crypt device
481#[derive(PartialEq)]
482pub struct CryptDeviceHandle<P: fmt::Debug> {
483    /// Pointer to the raw device
484    pub(crate) cd: RawDevice,
485
486    /// Path to the crypt device (useful for diagnostics)
487    pub(crate) path: PathBuf,
488
489    /// Additional parameters depending on type of crypt device opened
490    pub(crate) params: P,
491}
492
493impl<P: fmt::Debug> fmt::Debug for CryptDeviceHandle<P> {
494    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
495        write!(
496            f,
497            "CryptDeviceHandle(path={}, raw={:p}, params={:?})",
498            self.path.display(),
499            self.cd,
500            self.params
501        )
502    }
503}
504
505impl<P: fmt::Debug> Drop for CryptDeviceHandle<P> {
506    fn drop(&mut self) {
507        crate::device::free(&mut self.cd);
508        self.cd = ptr::null_mut();
509    }
510}
511
512impl<P: fmt::Debug> CryptDevice for CryptDeviceHandle<P> {
513    fn path(&self) -> &Path {
514        self.path.as_ref()
515    }
516
517    fn cipher(&self) -> &str {
518        crate::device::cipher(&self.cd).expect("Initialised device should have cipher")
519    }
520
521    fn cipher_mode(&self) -> &str {
522        crate::device::cipher_mode(&self.cd).expect("Initialised device should have cipher mode")
523    }
524
525    fn device_name(&self) -> &str {
526        crate::device::device_name(&self.cd).expect("Initialised device should have an underlying path")
527    }
528
529    fn rng_type(&self) -> raw::crypt_rng_type {
530        crate::device::rng_type(&self.cd)
531    }
532
533    fn set_rng_type(&mut self, rng_type: raw::crypt_rng_type) {
534        crate::device::set_rng_type(&mut self.cd, rng_type)
535    }
536
537    fn set_iteration_time(&mut self, iteration_time_ms: u64) {
538        #[allow(deprecated)]
539        crate::device::set_iteration_time(&mut self.cd, iteration_time_ms)
540    }
541
542    fn volume_key_size(&self) -> u8 {
543        crate::device::volume_key_size(&self.cd)
544    }
545}