cryptsetup_rs/
device.rs

1//! Low-level cryptsetup binding that sits directly on top of the `libcryptsetup` C API
2//!
3//! Consider using the high-level binding in the `api` module instead
4
5use std::boxed::Box;
6use std::error;
7use std::ffi;
8use std::fmt::{Display, Formatter};
9use std::marker::PhantomData;
10use std::mem;
11use std::path::Path;
12use std::ptr;
13use std::result;
14use std::str;
15use std::sync::Once;
16
17use errno;
18use libc;
19use uuid::Uuid;
20
21use crate::api::crypt_status_info;
22use blkid_rs;
23use raw;
24
25/// Raw pointer to the underlying `crypt_device` opaque struct
26pub type RawDevice = *mut raw::crypt_device;
27pub type Luks2TokenId = i32;
28
29static INIT_LOGGING: Once = Once::new();
30
31#[derive(Debug)]
32#[non_exhaustive]
33pub enum Error {
34    /// Error that originates from `libcryptsetup` (with numeric error code)
35    CryptsetupError(errno::Errno),
36    /// IO error
37    IOError(::std::io::Error),
38    /// Error from the blkid-rs library (while reading LUKS1 header)
39    BlkidError(blkid_rs::Error),
40    /// The operation tried was not valid for the LUKS version
41    InvalidLuksVersion,
42    /// Invalid JSON (with message)
43    InvalidJson(String),
44}
45
46impl Display for Error {
47    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48        match self {
49            Error::CryptsetupError(e) => write!(f, "Cryptsetup error: {}", e),
50            Error::IOError(io) => write!(f, "Underlying IO error: {}", io),
51            Error::BlkidError(e) => write!(f, "Blkid error: {}", e),
52            Error::InvalidLuksVersion => write!(f, "Invalid or unexpected LUKS version"),
53            Error::InvalidJson(msg) => write!(f, "Invalid JSON encountered: {}", msg),
54        }
55    }
56}
57
58impl error::Error for Error {
59    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
60        match &self {
61            &Error::IOError(e) => Some(e),
62            &Error::BlkidError(e) => Some(e),
63            _ => None,
64        }
65    }
66}
67
68impl From<::std::io::Error> for Error {
69    fn from(e: ::std::io::Error) -> Self {
70        Error::IOError(e)
71    }
72}
73
74impl From<blkid_rs::Error> for Error {
75    fn from(e: blkid_rs::Error) -> Self {
76        Error::BlkidError(e)
77    }
78}
79
80pub type Result<T> = result::Result<T, Error>;
81pub type Keyslot = u8;
82
83const ANY_KEYSLOT: libc::c_int = -1 as libc::c_int;
84
85fn str_from_c_str<'a>(c_str: *const libc::c_char) -> Option<&'a str> {
86    if c_str.is_null() {
87        None
88    } else {
89        unsafe { Some(ffi::CStr::from_ptr(c_str).to_str().unwrap()) }
90    }
91}
92
93macro_rules! crypt_error {
94    ($res:expr) => {
95        Err(Error::CryptsetupError(errno::Errno(-$res)))
96    };
97}
98
99macro_rules! check_crypt_error {
100    ($res:expr) => {
101        if $res != 0 {
102            crypt_error!($res)
103        } else {
104            Ok(())
105        }
106    };
107}
108
109/// Log function callback used by `libcryptsetup`
110#[allow(unused)]
111#[no_mangle]
112pub extern "C" fn cryptsetup_rs_log_callback(
113    level: raw::crypt_log_level,
114    message: *const libc::c_char,
115    usrptr: *mut libc::c_void,
116) {
117    let msg = str_from_c_str(message).unwrap();
118    match level {
119        raw::crypt_log_level::CRYPT_LOG_NORMAL => info!(target: "cryptsetup", "{}", msg.trim_end()),
120        raw::crypt_log_level::CRYPT_LOG_ERROR => error!(target: "cryptsetup", "{}", msg.trim_end()),
121        raw::crypt_log_level::CRYPT_LOG_VERBOSE => debug!(target: "cryptsetup", "{}", msg.trim_end()),
122        raw::crypt_log_level::CRYPT_LOG_DEBUG => debug!(target: "cryptsetup", "{}", msg.trim_end()),
123        raw::crypt_log_level::CRYPT_LOG_DEBUG_JSON => debug!(target: "cryptsetup", "{}", msg.trim_end()), // TODO - really?
124    }
125}
126
127fn init_logging() {
128    INIT_LOGGING.call_once(|| unsafe {
129        raw::crypt_set_log_callback(ptr::null_mut(), Some(cryptsetup_rs_log_callback), ptr::null_mut());
130    });
131}
132
133/// Initialise crypt device and check if provided device exists
134pub fn init<P: AsRef<Path>>(path: P) -> Result<RawDevice> {
135    init_logging();
136    let mut cd = ptr::null_mut();
137    let c_path = ffi::CString::new(path.as_ref().to_str().unwrap()).unwrap();
138
139    let res = unsafe { raw::crypt_init(&mut cd as *mut *mut raw::crypt_device, c_path.as_ptr()) };
140
141    if res != 0 {
142        crypt_error!(res)
143    } else {
144        Ok(cd)
145    }
146}
147
148/// Initialise crypt device by header device and data device, and check if provided device exists
149pub fn init_detached_header<P1: AsRef<Path>, P2: AsRef<Path>>(header_path: P1, device_path: P2) -> Result<RawDevice> {
150    init_logging();
151    let mut cd = ptr::null_mut();
152
153    let c_header_path = ffi::CString::new(header_path.as_ref().to_str().unwrap()).unwrap();
154    let c_device_path = ffi::CString::new(device_path.as_ref().to_str().unwrap()).unwrap();
155
156    let res = unsafe {
157        raw::crypt_init_data_device(
158            &mut cd as *mut *mut raw::crypt_device,
159            c_header_path.as_ptr(),
160            c_device_path.as_ptr(),
161        )
162    };
163
164    if res != 0 {
165        crypt_error!(res)
166    } else {
167        Ok(cd)
168    }
169}
170
171/// Initialise active crypt device by name (and error out if inactive)
172pub fn init_by_name(name: &str) -> Result<RawDevice> {
173    init_logging();
174    let mut cd = ptr::null_mut();
175    let c_name = ffi::CString::new(name).unwrap();
176
177    let res = unsafe { raw::crypt_init_by_name(&mut cd as *mut *mut raw::crypt_device, c_name.as_ptr()) };
178
179    if res != 0 {
180        crypt_error!(res)
181    } else {
182        Ok(cd)
183    }
184}
185
186/// Load crypt device parameters from the on-disk header
187///
188/// Note that typically you cannot query the crypt device for information before this function is
189/// called.
190pub fn load(cd: &RawDevice, requested_type: raw::crypt_device_type) -> Result<()> {
191    let c_type = ffi::CString::new(requested_type.to_str()).unwrap();
192
193    let res = unsafe { raw::crypt_load(*cd, c_type.as_ptr(), ptr::null_mut()) };
194
195    check_crypt_error!(res)
196}
197
198/// Get the cipher used by this crypt device
199pub fn cipher<'a>(cd: &'a RawDevice) -> Option<&'a str> {
200    let c_cipher = unsafe { raw::crypt_get_cipher(*cd) };
201    str_from_c_str(c_cipher)
202}
203
204/// Get the cipher mode used by this crypt device
205pub fn cipher_mode<'a>(cd: &'a RawDevice) -> Option<&'a str> {
206    let c_cipher_mode = unsafe { raw::crypt_get_cipher_mode(*cd) };
207    str_from_c_str(c_cipher_mode)
208}
209
210/// Deactivate crypt device, removing active device-mapper mapping from kernel.
211pub fn deactivate(cd: RawDevice, name: &str) -> Result<()> {
212    let c_name = ffi::CString::new(name).expect("name to cstr");
213    let res = unsafe { raw::crypt_deactivate(cd, c_name.as_ptr()) };
214    check_crypt_error!(res)
215}
216
217/// Get the path to the device (as `libcryptsetup` sees it)
218pub fn device_name<'a>(cd: &'a RawDevice) -> Option<&'a str> {
219    let c_device_name = unsafe { raw::crypt_get_device_name(*cd) };
220    str_from_c_str(c_device_name)
221}
222
223/// Dump text-formatted information about this device to the console
224pub fn dump(cd: &RawDevice) -> Result<()> {
225    let res = unsafe { raw::crypt_dump(*cd) };
226    check_crypt_error!(res)
227}
228
229/// Releases crypt device context and memory
230pub fn free(cd: &mut RawDevice) {
231    unsafe { raw::crypt_free(*cd) }
232}
233
234/// Get status info about a device name
235pub fn status(cd: &mut RawDevice, name: &str) -> crypt_status_info {
236    let c_name = ffi::CString::new(name).unwrap();
237
238    unsafe { raw::crypt_status(*cd, c_name.as_ptr()) }
239}
240
241/// Get status info about a device name (only)
242pub fn status_only(name: &str) -> crypt_status_info {
243    let c_name = ffi::CString::new(name).unwrap();
244
245    unsafe { raw::crypt_status(ptr::null_mut(), c_name.as_ptr()) }
246}
247
248/// Activate device based on provided key ("passphrase")
249pub fn luks_activate(cd: &mut RawDevice, name: &str, key: &[u8]) -> Result<Keyslot> {
250    let c_name = ffi::CString::new(name).unwrap();
251    let c_passphrase_len = key.len() as libc::size_t;
252    // cast the passphrase to a pointer directly - it will not be NUL terminated but the passed length is used
253    let c_passphrase = key as *const [u8] as *const libc::c_char;
254
255    let res = unsafe {
256        raw::crypt_activate_by_passphrase(*cd, c_name.as_ptr(), ANY_KEYSLOT, c_passphrase, c_passphrase_len, 0u32)
257    };
258
259    if res < 0 {
260        crypt_error!(res)
261    } else {
262        Ok(res as u8)
263    }
264}
265
266/// Add key slot using provided passphrase. If there is no previous passphrase, use the volume key
267/// that is in-memory to add the new key slot.
268pub fn luks_add_keyslot(
269    cd: &mut RawDevice,
270    key: &[u8],
271    maybe_prev_key: Option<&[u8]>,
272    maybe_keyslot: Option<Keyslot>,
273) -> Result<Keyslot> {
274    let c_key_len = key.len() as libc::size_t;
275    let c_key = key as *const [u8] as *const libc::c_char;
276    let c_keyslot = maybe_keyslot
277        .map(|k| k as libc::c_int)
278        .unwrap_or(ANY_KEYSLOT as libc::c_int);
279
280    let res = if let Some(prev_key) = maybe_prev_key {
281        let c_prev_key_len = prev_key.len() as libc::size_t;
282        let c_prev_key = prev_key as *const [u8] as *const libc::c_char;
283
284        unsafe { raw::crypt_keyslot_add_by_passphrase(*cd, c_keyslot, c_prev_key, c_prev_key_len, c_key, c_key_len) }
285    } else {
286        unsafe {
287            raw::crypt_keyslot_add_by_volume_key(*cd, c_keyslot, ptr::null(), 0 as libc::size_t, c_key, c_key_len)
288        }
289    };
290
291    if res < 0 {
292        crypt_error!(res)
293    } else {
294        Ok(res as Keyslot)
295    }
296}
297
298/// Add key slot using provided passphrase.
299pub fn luks_update_keyslot(
300    cd: &mut RawDevice,
301    key: &[u8],
302    prev_key: &[u8],
303    maybe_keyslot: Option<Keyslot>,
304) -> Result<Keyslot> {
305    let c_key_len = key.len() as libc::size_t;
306    let c_key = key as *const [u8] as *const libc::c_char;
307    let c_keyslot = maybe_keyslot
308        .map(|k| k as libc::c_int)
309        .unwrap_or(ANY_KEYSLOT as libc::c_int);
310
311    let c_prev_key_len = prev_key.len() as libc::size_t;
312    let c_prev_key = prev_key as *const [u8] as *const libc::c_char;
313
314    let res = unsafe {
315        raw::crypt_keyslot_change_by_passphrase(*cd, c_keyslot, c_keyslot, c_prev_key, c_prev_key_len, c_key, c_key_len)
316    };
317
318    if res < 0 {
319        crypt_error!(res)
320    } else {
321        Ok(res as Keyslot)
322    }
323}
324
325/// Destroy (and disable) key slot
326pub fn luks_destroy_keyslot(cd: &mut RawDevice, keyslot: Keyslot) -> Result<()> {
327    let res = unsafe { raw::crypt_keyslot_destroy(*cd, keyslot as libc::c_int) };
328    if res < 0 {
329        crypt_error!(res)
330    } else {
331        Ok(())
332    }
333}
334
335fn generic_format(
336    cd: &mut RawDevice,
337    cipher: &str,
338    cipher_mode: &str,
339    mk_bits: usize,
340    maybe_uuid: Option<&uuid::Uuid>,
341    type_: raw::crypt_device_type,
342    c_params: *mut libc::c_void,
343) -> Result<()> {
344    let c_cipher = ffi::CString::new(cipher).unwrap();
345    let c_cipher_mode = ffi::CString::new(cipher_mode).unwrap();
346    let c_uuid = maybe_uuid.map(|uuid| ffi::CString::new(uuid.hyphenated().to_string()).unwrap());
347
348    let c_luks_type = ffi::CString::new(type_.to_str()).unwrap();
349    let c_uuid_ptr = c_uuid.as_ref().map(|u| u.as_ptr()).unwrap_or(ptr::null());
350    let res = unsafe {
351        raw::crypt_format(
352            *cd,
353            c_luks_type.as_ptr(),
354            c_cipher.as_ptr(),
355            c_cipher_mode.as_ptr(),
356            c_uuid_ptr,
357            ptr::null(),
358            mk_bits / 8,
359            c_params,
360        )
361    };
362
363    check_crypt_error!(res)
364}
365
366/// Format a new crypt device as LUKS1 but do not activate it
367///
368/// Note this does not add an active keyslot
369pub fn luks1_format(
370    cd: &mut RawDevice,
371    cipher: &str,
372    cipher_mode: &str,
373    hash: &str,
374    mk_bits: usize,
375    maybe_uuid: Option<&uuid::Uuid>,
376) -> Result<()> {
377    let c_hash = ffi::CString::new(hash).unwrap();
378    let mut luks_params = raw::crypt_params_luks1 {
379        hash: c_hash.as_ptr(),
380        data_alignment: 0,
381        data_device: ptr::null(),
382    };
383    let c_luks_params: *mut raw::crypt_params_luks1 = &mut luks_params;
384    generic_format(
385        cd,
386        cipher,
387        cipher_mode,
388        mk_bits,
389        maybe_uuid,
390        raw::crypt_device_type::LUKS1,
391        c_luks_params as *mut libc::c_void,
392    )
393}
394
395/// equivalent to `raw::crypt_pbkdf_type`
396pub struct Luks2FormatPbkdf<'a> {
397    pub type_: raw::crypt_pbkdf_algo_type,
398    pub hash: &'a str,
399    pub time_ms: u32,
400    pub iterations: u32,
401    pub max_memory_kb: u32,
402    pub parallel_threads: u32,
403    pub flags: u32,
404}
405
406/// equivalent to `raw::crypt_params_integrity` (with omitted params for constants)
407pub struct Luks2FormatIntegrity<'a> {
408    journal_size: u64,
409    journal_watermark: u64,
410    journal_commit_time: u64,
411    interleave_sectors: u32,
412    tag_size: u32,
413    sector_size: u32,
414    buffer_sectors: u32,
415    journal_integrity_algorithm: &'a str,
416    journal_encryption_algorithm: &'a str,
417}
418
419/// Format a new crypt device as LUKS2 but do not activate it
420///
421/// Note this does not add an active keyslot
422pub fn luks2_format<'a>(
423    cd: &mut RawDevice,
424    cipher: &str,
425    cipher_mode: &str,
426    mk_bits: usize,
427    data_alignment: usize,
428    sector_size: u32,
429    label: Option<&'a str>,
430    subsystem: Option<&'a str>,
431    data_device: Option<&Path>,
432    maybe_uuid: Option<&uuid::Uuid>,
433    pbkdf: Option<&'a Luks2FormatPbkdf>,
434    integrity: Option<&'a Luks2FormatIntegrity>,
435) -> Result<()> {
436    let maybe_pbkdf = pbkdf.map(|p| {
437        let c_type = ffi::CString::new(p.type_.to_str()).unwrap();
438        let c_hash = ffi::CString::new(p.hash).unwrap();
439
440        let res = raw::crypt_pbkdf_type {
441            type_: c_type.as_ptr(),
442            hash: c_hash.as_ptr(),
443            time_ms: p.time_ms,
444            iterations: p.iterations,
445            max_memory_kb: p.max_memory_kb,
446            parallel_threads: p.parallel_threads,
447            flags: p.flags,
448        };
449
450        (res, (c_type, c_hash))
451    });
452    let maybe_integrity = integrity.map(|i| {
453        let c_journal_integrity = ffi::CString::new(i.journal_integrity_algorithm).unwrap();
454        let c_journal_crypt = ffi::CString::new(i.journal_encryption_algorithm).unwrap();
455
456        let res = raw::crypt_params_integrity {
457            journal_size: i.journal_size,
458            journal_watermark: i.journal_watermark as libc::c_uint,
459            journal_commit_time: i.journal_commit_time as libc::c_uint,
460            interleave_sectors: i.interleave_sectors,
461            tag_size: i.tag_size,
462            sector_size: i.sector_size,
463            buffer_sectors: i.buffer_sectors,
464            integrity: ptr::null(), // always null
465            integrity_key_size: 0,  // always 0
466            journal_integrity: c_journal_integrity.as_ptr(),
467            journal_integrity_key: ptr::null(), // only for crypt_load
468            journal_integrity_key_size: 0,      // only for crypt_load
469            journal_crypt: c_journal_crypt.as_ptr(),
470            journal_crypt_key: ptr::null(), // only for crypt_load
471            journal_crypt_key_size: 0,      // only for crypt_load
472        };
473
474        (res, (c_journal_integrity, c_journal_crypt))
475    });
476
477    let maybe_data_device = data_device
478        .map(|p| p.to_str())
479        .flatten()
480        .map(|s| ffi::CString::new(s).unwrap());
481    let maybe_label = label.map(|l| ffi::CString::new(l).unwrap());
482    let maybe_subsystem = subsystem.map(|s| ffi::CString::new(s).unwrap());
483
484    let mut luks2_params = raw::crypt_params_luks2 {
485        pbkdf: maybe_pbkdf
486            .as_ref()
487            .map_or(ptr::null(), |(p, _)| p as *const raw::crypt_pbkdf_type),
488        integrity: maybe_integrity.as_ref().map_or(ptr::null(), |(_, (i, _))| i.as_ptr()),
489        integrity_params: maybe_integrity
490            .as_ref()
491            .map_or(ptr::null(), |(i, _)| i as *const raw::crypt_params_integrity),
492        data_alignment,
493        data_device: maybe_data_device.as_ref().map_or(ptr::null(), |d| d.as_ptr()),
494        sector_size,
495        label: maybe_label.as_ref().map_or(ptr::null(), |l| l.as_ptr()),
496        subsystem: maybe_subsystem.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
497    };
498    let c_luks2_params: *mut raw::crypt_params_luks2 = &mut luks2_params;
499
500    let res = generic_format(
501        cd,
502        cipher,
503        cipher_mode,
504        mk_bits,
505        maybe_uuid,
506        raw::crypt_device_type::LUKS2,
507        c_luks2_params as *mut libc::c_void,
508    );
509
510    let _discard = (
511        maybe_pbkdf,
512        maybe_integrity,
513        maybe_data_device,
514        maybe_label,
515        maybe_subsystem,
516        luks2_params,
517    );
518    res
519}
520
521#[repr(i32)]
522pub enum TokenHandlerResult {
523    Success = 0,
524    Failure = 1,
525}
526
527/// In-memory representation of handler (needed because cryptsetup doesn't copy the handler struct)
528pub struct Luks2TokenHandlerBox<H>
529where
530    H: Luks2TokenHandler + Luks2TokenHandlerRaw,
531{
532    _c_name: ffi::CString,
533    c_handler: Box<raw::crypt_token_handler>,
534    _handler: PhantomData<H>,
535}
536
537impl<H: Luks2TokenHandler + Luks2TokenHandlerRaw> Luks2TokenHandlerBox<H> {
538    pub fn new() -> Luks2TokenHandlerBox<H> {
539        let c_name = ffi::CString::new(H::name()).expect("valid name");
540        let c_handler = Box::new(raw::crypt_token_handler {
541            name: c_name.as_ptr(),
542            open: H::raw_open_func,
543            buffer_free: Some(H::raw_free_func),
544            validate: Some(H::raw_validate_func),
545            dump: Some(H::raw_dump_func),
546        });
547        Luks2TokenHandlerBox {
548            _c_name: c_name,
549            c_handler,
550            _handler: PhantomData,
551        }
552    }
553}
554
555/// Equivalence trait for `raw::crypt_token_handler`
556///
557/// The implementation makes use of traits to generate the raw C functions as a default trait implementation in
558/// `Luks2TokenHandlerRaw`. There isn't really an alternative (safe) way to create the C functions because the
559/// libcryptsetup interface does not offer user pointers as parameters on all of the callback functions (eliminating
560/// the possibility of using boxed closures)
561pub trait Luks2TokenHandler {
562    /// Display name of token handler
563    fn name() -> &'static str;
564
565    /// Return the key (the vector returned will be disowned and passed to the free function later)
566    fn open(cd: RawDevice, token_id: Luks2TokenId) -> (Vec<u8>, TokenHandlerResult);
567
568    /// Free the key (by passing it the reconstructed) vector
569    fn free(buf: Vec<u8>);
570
571    /// Whether the handler can validate json
572    fn can_validate() -> bool;
573
574    /// Validate the token handler JSON representation
575    fn is_valid(cd: RawDevice, json: String) -> Option<TokenHandlerResult>;
576
577    /// Dump debug information about the token handler implementation
578    fn dump(cd: RawDevice, json: String);
579}
580
581/// Companion trait to `Luks2TokenHandler` which contains the raw FFI implementation. Users should implement this trait
582/// but not override the implementation.
583pub trait Luks2TokenHandlerRaw: Luks2TokenHandler {
584    extern "C" fn raw_open_func(
585        cd: *mut raw::crypt_device,
586        token: libc::c_int,
587        buffer: *mut *mut libc::c_char,
588        buffer_len: *mut libc::size_t,
589        _usrptr: *mut libc::c_void,
590    ) -> libc::c_int {
591        let (mut buf, res) = Self::open(cd, token as Luks2TokenId);
592
593        // capacity shrinking is approximate, but we only have a single pointer :/
594        buf.shrink_to_fit();
595        assert!(buf.capacity() == buf.len());
596
597        let buf_ptr = buf.as_mut_ptr();
598        let len = buf.len();
599        mem::forget(buf);
600
601        unsafe {
602            *buffer = buf_ptr as *mut libc::c_char;
603            *buffer_len = len as libc::size_t;
604        }
605
606        res as i32 as libc::c_int
607    }
608
609    extern "C" fn raw_free_func(buffer: *mut libc::c_void, buffer_len: libc::size_t) {
610        let buf = unsafe { Vec::from_raw_parts(buffer as *mut libc::c_char as *mut u8, buffer_len, buffer_len) };
611        Self::free(buf)
612    }
613
614    extern "C" fn raw_dump_func(cd: *mut raw::crypt_device, token_json: *const libc::c_char) {
615        let json = str_from_c_str(token_json).map_or_else(|| String::new(), |s| s.to_string());
616        Self::dump(cd, json)
617    }
618
619    extern "C" fn raw_validate_func(cd: *mut raw::crypt_device, token_json: *const libc::c_char) -> libc::c_int {
620        let res = if Self::can_validate() {
621            let json = str_from_c_str(token_json).map_or_else(|| String::new(), |s| s.to_string());
622            Self::is_valid(cd, json).expect("validation result")
623        } else {
624            TokenHandlerResult::Success
625        };
626
627        res as i32 as libc::c_int
628    }
629}
630
631/// Register a LUKS2 token handler
632///
633/// Note: the implementation relies on a struct with a box containing the actual handler C struct. The handler C struct
634///     must not be deallocated while the handler is registered.
635pub fn luks2_register_token_handler<H: Luks2TokenHandlerRaw>(handler_box: &Luks2TokenHandlerBox<H>) -> Result<()> {
636    let res = unsafe { raw::crypt_token_register(handler_box.c_handler.as_ref()) };
637    check_crypt_error!(res)
638}
639
640/// Get the status of LUKS2 token id (and if successful, the type name of the token)
641pub fn luks2_token_status(cd: &mut RawDevice, token_id: Luks2TokenId) -> (raw::crypt_token_info, Option<String>) {
642    let mut type_ptr: *const libc::c_char = ptr::null();
643    let res =
644        unsafe { raw::crypt_token_status(*cd, token_id as libc::c_int, &mut type_ptr as *mut *const libc::c_char) };
645
646    let token_type = if !type_ptr.is_null() {
647        str_from_c_str(type_ptr).map(|s| s.to_string())
648    } else {
649        None
650    };
651
652    (res, token_type)
653}
654
655/// Get the token's JSON value for a token id
656pub fn luks2_token_json(cd: &mut RawDevice, token_id: Luks2TokenId) -> Result<String> {
657    let mut json_ptr: *const libc::c_char = ptr::null();
658    let res =
659        unsafe { raw::crypt_token_json_get(*cd, token_id as libc::c_int, &mut json_ptr as *mut *const libc::c_char) };
660
661    if res < 0 {
662        crypt_error!(res)
663    } else {
664        let json = str_from_c_str(json_ptr)
665            .map(|s| s.to_string())
666            .expect("valid json string");
667        Ok(json)
668    }
669}
670
671/// Set the token's JSON value and allocate it to a token id (new token id will be allocated if no token id is passed)
672///
673/// Note: the JSON string passed in must have a "type" field with the token handler type and a list of "keyslots"
674pub fn luks2_token_json_allocate(
675    cd: &mut RawDevice,
676    json: &str,
677    token_id: Option<Luks2TokenId>,
678) -> Result<Luks2TokenId> {
679    let c_json = ffi::CString::new(json).unwrap();
680
681    println!("BEFORE JSON ALLOCATE: {}", json);
682
683    let res = unsafe { raw::crypt_token_json_set(*cd, token_id.unwrap_or(raw::CRYPT_ANY_TOKEN), c_json.as_ptr()) };
684    let _deferred = (c_json,);
685
686    if res < 0 {
687        crypt_error!(res)
688    } else {
689        Ok(res as Luks2TokenId)
690    }
691}
692
693/// Removes a token by its id
694pub fn luks2_token_remove(cd: &mut RawDevice, token_id: Luks2TokenId) -> Result<()> {
695    let res = unsafe { raw::crypt_token_json_set(*cd, token_id, ptr::null()) };
696
697    check_crypt_error!(res)
698}
699
700/// Assigns a token id to a keyslot (or, if no keyslot is specified, all active keyslots)
701pub fn luks2_token_assign_keyslot(cd: &mut RawDevice, token_id: Luks2TokenId, keyslot: Option<Keyslot>) -> Result<()> {
702    let res = unsafe {
703        raw::crypt_token_assign_keyslot(
704            *cd,
705            token_id,
706            keyslot.map_or(raw::CRYPT_ANY_SLOT, |ks| ks as libc::c_int),
707        )
708    };
709
710    check_crypt_error!(res)
711}
712
713/// Unassigns a token id from a keyslot (or, if no keyslot is specified, all active keyslots)
714pub fn luks2_token_unassign_keyslot(
715    cd: &mut RawDevice,
716    token_id: Luks2TokenId,
717    keyslot: Option<Keyslot>,
718) -> Result<()> {
719    let res = unsafe {
720        raw::crypt_token_unassign_keyslot(
721            *cd,
722            token_id,
723            keyslot.map_or(raw::CRYPT_ANY_SLOT, |ks| ks as libc::c_int),
724        )
725    };
726
727    check_crypt_error!(res)
728}
729
730/// Get information about token assignment for a particular keyslot
731pub fn luks2_token_is_assigned(cd: &mut RawDevice, token_id: Luks2TokenId, keyslot: Keyslot) -> Result<bool> {
732    let res = unsafe { raw::crypt_token_is_assigned(*cd, token_id, keyslot as libc::c_int) };
733
734    if res == 0 {
735        Ok(true)
736    } else if res == libc::ENOENT {
737        Ok(false)
738    } else {
739        crypt_error!(res)
740    }
741}
742
743/// Activate device, or when name is not provided, check the key can open the device
744pub fn luks2_activate_by_token(
745    cd: &mut RawDevice,
746    name: Option<&str>,
747    token_id: Option<Luks2TokenId>,
748) -> Result<Keyslot> {
749    let c_name_opt = name.and_then(|n| ffi::CString::new(n).ok());
750
751    let res = unsafe {
752        raw::crypt_activate_by_token(
753            *cd,
754            c_name_opt.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
755            token_id.unwrap_or(raw::CRYPT_ANY_TOKEN),
756            ptr::null_mut(),
757            0,
758        )
759    };
760
761    let _deferred = (c_name_opt,);
762
763    if res < 0 {
764        crypt_error!(res)
765    } else {
766        Ok(res as Keyslot)
767    }
768}
769
770pub fn luks2_set_pbkdf_type(cd: &mut RawDevice, pbkdf: &Luks2FormatPbkdf) -> Result<()> {
771    let c_type = ffi::CString::new(pbkdf.type_.to_str()).unwrap();
772    let c_hash = ffi::CString::new(pbkdf.hash).unwrap();
773
774    let c_pbkdf_type = raw::crypt_pbkdf_type {
775        type_: c_type.as_ptr(),
776        hash: c_hash.as_ptr(),
777        time_ms: pbkdf.time_ms,
778        iterations: pbkdf.iterations,
779        max_memory_kb: pbkdf.max_memory_kb,
780        parallel_threads: pbkdf.parallel_threads,
781        flags: pbkdf.flags,
782    };
783
784    let res = unsafe { raw::crypt_set_pbkdf_type(*cd, &c_pbkdf_type as *const raw::crypt_pbkdf_type) };
785
786    let _discard = (c_type, c_hash, c_pbkdf_type);
787
788    check_crypt_error!(res)
789}
790
791/// Get which RNG is used
792pub fn rng_type(cd: &RawDevice) -> raw::crypt_rng_type {
793    unsafe {
794        let res = raw::crypt_get_rng_type(*cd);
795        mem::transmute(res)
796    }
797}
798
799/// Set the number of milliseconds for `PBKDF2` function iteration
800#[deprecated]
801pub fn set_iteration_time(cd: &mut RawDevice, iteration_time_ms: u64) {
802    unsafe {
803        #[allow(deprecated)]
804        raw::crypt_set_iteration_time(*cd, iteration_time_ms);
805    }
806}
807
808/// Set which RNG is used
809pub fn set_rng_type(cd: &mut RawDevice, rng_type: raw::crypt_rng_type) {
810    unsafe { raw::crypt_set_rng_type(*cd, rng_type) }
811}
812
813/// Get information about a keyslot
814pub fn keyslot_status(cd: &RawDevice, slot: Keyslot) -> raw::crypt_keyslot_info {
815    unsafe { raw::crypt_keyslot_status(*cd, slot as libc::c_int) }
816}
817
818/// Get size in bytes of the volume key
819pub fn volume_key_size(cd: &RawDevice) -> u8 {
820    let res = unsafe { raw::crypt_get_volume_key_size(*cd) };
821    res as u8
822}
823
824/// Get device UUID
825pub fn uuid(cd: &RawDevice) -> Option<Uuid> {
826    let c_uuid_str = unsafe { raw::crypt_get_uuid(*cd) };
827    str_from_c_str(c_uuid_str).and_then(|uuid_str| Uuid::parse_str(uuid_str).ok())
828}