extern crate libc;
extern crate nix;
#[macro_use]
extern crate bitflags;
#[cfg(test)]
extern crate tempdir;
use libc::*;
use std::path::Path;
use std::os::unix::ffi::OsStrExt;
enum CryptDevice_ {}
#[link(name = "cryptsetup")]
extern "C" {
fn crypt_init(cd: *mut *mut CryptDevice_, device: *const c_char) -> c_int;
fn crypt_init_by_name_and_header(cd: *mut *mut CryptDevice_, name: *const c_char, header_device: *const c_char) -> c_int;
fn crypt_set_log_callback(cd: *mut CryptDevice_, callback: extern fn(level:c_int, msg: *const c_char, usrptr: *mut c_void), usrptr: *mut c_void);
fn crypt_log(cd: *mut CryptDevice_, level:c_int, msg: *const c_char);
fn crypt_set_confirm_callback(cd: *mut CryptDevice_, callback: extern fn(msg: *const c_char, usrptr: *mut c_void), usrptr: *mut c_void);
fn crypt_set_iteration_time(cd: *mut CryptDevice_, iteration_time_ms: u64) -> c_int;
fn crypt_set_data_device(cd: *mut CryptDevice_, device: *const c_char) -> c_int;
fn crypt_set_rng_type(cd: *mut CryptDevice_, rng_type: c_int);
fn crypt_get_rng_type(cd: *mut CryptDevice_) -> c_int;
fn crypt_memory_lock(cd: *mut CryptDevice_, lock: c_int) -> c_int;
fn crypt_get_type(cd: *mut CryptDevice_) -> *const c_char;
fn crypt_format(cd: *mut CryptDevice_, type_: *const c_char, cipher: *const c_char,
cipher_mode: *const c_char, uuid: *const c_char, volume_key: *const c_char, volume_key_size: size_t, params: *mut c_void) -> c_int;
fn crypt_set_uuid(cd: *mut CryptDevice_, uuid: *const c_char) -> c_int;
fn crypt_load(cd: *mut CryptDevice_, requested_type: *const c_char, params: *const c_void) -> c_int;
fn crypt_repair(cd: *mut CryptDevice_, requested_type: *const c_char, params: *const c_void) -> c_int;
fn crypt_resize(cd: *mut CryptDevice_, name: *const c_char, new_size: u64) -> c_int;
fn crypt_suspend(cd: *mut CryptDevice_, name: *const c_char) -> c_int;
fn crypt_resume_by_passphrase(cd: *mut CryptDevice_, name: *const c_char, keyslot: c_int, passphrase: *const c_char, passphrase_size: size_t) -> c_int;
fn crypt_resume_by_keyfile_offset(cd: *mut CryptDevice_, name: *const c_char, keyslot: c_int, keyfile: *const c_char, keyfile_size: size_t, keyfile_offset: size_t) -> c_int;
fn crypt_free(cd: *mut CryptDevice_);
fn crypt_keyslot_add_by_passphrase(cd: *mut CryptDevice_, keyslot:c_int, passphrase: *const c_char, passphrase_size: size_t, new_passphrase: *const c_char, new_passphrase_size: size_t) -> c_int;
fn crypt_keyslot_change_by_passphrase(cd: *mut CryptDevice_, keyslot_old:c_int, keyslot_new: c_int, passphrase: *const c_char, passphrase_size: size_t, new_passphrase: *const c_char, new_passphrase_size: size_t) -> c_int;
fn crypt_keyslot_add_by_keyfile_offset(cd: *mut CryptDevice_, name: *const c_char, keyslot: c_int, keyfile: *const c_char, keyfile_size: size_t, keyfile_offset: size_t) -> c_int;
fn crypt_keyslot_add_by_volume_key(cd: *mut CryptDevice_, keyslot:c_int, volume_key: *const c_char, volume_key_size: size_t, passphrase: *const c_char, passphrase_size: size_t) -> c_int;
fn crypt_keyslot_destroy(cd: *mut CryptDevice_, keyslot:c_int) -> c_int;
fn crypt_get_active_device(cd: *mut CryptDevice_, name: *const c_char, cad: *mut ActiveDevice) -> c_int;
fn crypt_activate_by_passphrase(cd: *mut CryptDevice_, name: *const c_char, keyslot: c_int, passphrase: *const c_char, passphrase_size: size_t, flags: u32) -> c_int;
fn crypt_activate_by_keyfile_offset(cd: *mut CryptDevice_, name: *const c_char, keyslot: c_int, keyfile: *const c_char, keyfile_size: size_t, keyfile_offset: size_t, flags: u32) -> c_int;
fn crypt_activate_by_volume_key(cd: *mut CryptDevice_, name: *const c_char, volume_key: *const c_char, volume_key_size: size_t, flags: u32) -> c_int;
fn crypt_deactivate(cd: *mut CryptDevice_, name: *const c_char) -> c_int;
fn crypt_volume_key_get(cd: *mut CryptDevice_, keyslot:c_int, volume_key: *mut c_char,
volume_key_size: *mut size_t, passphrase: *const c_char, passphrase_size: size_t) -> c_int;
fn crypt_volume_key_verify(cd: *mut CryptDevice_, volume_key: *const c_char, volume_key_size: size_t) -> c_int;
fn crypt_status(cd: *mut CryptDevice_, name: *const c_char) -> StatusInfo;
fn crypt_dump(cd: *mut CryptDevice_) -> c_int;
fn crypt_get_cipher(cd: *mut CryptDevice_) -> *const c_char;
fn crypt_get_cipher_mode(cd: *mut CryptDevice_) -> *const c_char;
fn crypt_get_uuid(cd: *mut CryptDevice_) -> *const c_char;
fn crypt_get_device_name(cd: *mut CryptDevice_) -> *const c_char;
fn crypt_get_data_offset(cd: *mut CryptDevice_) -> u64;
fn crypt_get_iv_offset(cd: *mut CryptDevice_) -> u64;
fn crypt_get_volume_key_size(cd: *mut CryptDevice_) -> c_int;
fn crypt_get_verity_info(cd: *mut CryptDevice_, vp: *mut CParamsVerity) -> c_int;
fn crypt_benchmark(cd: *mut CryptDevice_, cipher: *const c_char, cipher_mode: *const c_char, volume_key_size: size_t,
iv_size: size_t, buffer_size: size_t, encryption_mbs: *mut f64, decryption_mbs: *mut f64) -> c_int;
fn crypt_benchmark_kdf(cd: *mut CryptDevice_, kdf: *const c_char, hash: *const c_char, password: *const c_char,
password_size: size_t, salt: *const c_char, salt_size: size_t, iterations_sec: *mut u64) -> c_int;
fn crypt_keyslot_status(cd: *mut CryptDevice_, keyslot: c_int) -> KeyslotInfo;
fn crypt_keyslot_max(cd: *mut CryptDevice_) -> c_int;
fn crypt_keyslot_area(cd: *mut CryptDevice_, keyslot: c_int, offset: *mut u64, length: *mut u64) -> c_int;
fn crypt_header_backup(cd: *mut CryptDevice_, requested_type: *const c_char, backup_file: *const c_char) -> c_int;
fn crypt_header_restore(cd: *mut CryptDevice_, requested_type: *const c_char, backup_file: *const c_char) -> c_int;
fn crypt_get_dir(cd: *mut CryptDevice_) -> *const c_char;
fn crypt_set_debug_level(level: c_int);
}
#[repr(C)]
pub enum StatusInfo {
Invalid, Inactive, Active, Busy
}
#[repr(C)]
pub enum KeyslotInfo {
Invalid, Inactive, Active, ActiveLast
}
use std::marker::PhantomData;
pub trait Callbacks {
fn log(&mut self, level: c_int, msg: &str);
fn confirm(&mut self, msg: &str);
}
extern "C" fn log_callback<C:Callbacks>(level: c_int,
msg: *const c_char,
usrptr: *mut c_void) {
unsafe {
let mut cb:&mut C = &mut * (usrptr as *mut C);
let len = strlen(msg);
let msg = std::slice::from_raw_parts(msg as *const u8, len as usize);
let msg = std::str::from_utf8(msg).unwrap();
cb.log(level, msg)
}
}
extern "C" fn confirm_callback<C:Callbacks>(msg: *const c_char,
usrptr: *mut c_void) {
unsafe {
let mut cb:&mut C = &mut * (usrptr as *mut C);
let len = strlen(msg);
let msg = std::slice::from_raw_parts(msg as *const u8, len as usize);
let msg = std::str::from_utf8(msg).unwrap();
cb.confirm(msg)
}
}
pub struct CryptDevice<'cb, Cb: 'cb> {
cd: *mut CryptDevice_,
callback: PhantomData<&'cb Cb>
}
impl<'cb, Cb> Drop for CryptDevice<'cb, Cb> {
fn drop(&mut self) {
unsafe {
crypt_free(self.cd)
}
}
}
#[derive(Debug)]
pub enum Error {
Errno(nix::Errno),
WrongKeyLen(usize, usize),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use Error::*;
match *self {
Errno(e) => e.fmt(f),
WrongKeyLen(a,b) => write!(f, "Wrong volume key length: {:?}, should be {:?}", a, b),
}
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
use Error::*;
match *self {
Errno(ref e) => e.description(),
WrongKeyLen(_,_) => "Wrong volume key length",
}
}
fn cause(&self) -> Option<&std::error::Error> {
use Error::*;
match *self {
Errno(ref err) => Some(err),
WrongKeyLen(_,_) => None
}
}
}
fn c_path<P:AsRef<Path>>(path:P) -> Vec<u8> {
let mut path = path.as_ref().as_os_str().as_bytes().to_vec();
let null_term = match path.last() {
Some(l) if *l == 0 => true,
_ => false
};
if !null_term {
path.push(0);
}
path
}
const CRYPT_ANY_SLOT:c_int = -1;
bitflags! {
#[repr(C)] pub flags ActivationFlags: u32 {
const ACTIVATE_READONLY = 1<<0,
const ACTIVATE_NO_UUID = 1<<1,
const ACTIVATE_SHARED = 1<<2,
const ACTIVATE_ALLOW_DISCARDS = 1<<3,
const ACTIVATE_PRIVATE = 1<<4,
const ACTIVATE_CORRUPTED = 1<<5,
const ACTIVATE_SAME_CPU_CRYPT = 1<<6,
const ACTIVATE_SUBMIT_FROM_CRYPT_CPUS = 1<<7,
const ACTIVATE_IGNORE_CORRUPTION = 1<<8,
const ACTIVATE_RESTART_ON_CORRUPTION = 1<<9,
const ACTIVATE_IGNORE_ZERO_BLOCKS = 1<<10,
}
}
#[repr(C)]
struct Luks1_ {
hash: *const c_char,
data_alignment: size_t,
data_device: *const c_char,
}
#[derive(Debug)]
pub enum Parameters<'a> {
Luks1 { hash: Hash, data_alignment: usize, data_device: Option<&'a str> }
}
#[derive(Debug)]
pub enum Type {
Plain,
Luks1,
LoopAES,
Verity,
TCrypt
}
impl CryptDevice<'static, ()> {
pub fn init<P:AsRef<Path>>(path: P) -> Result<Self, Error> {
let path = c_path(path);
let mut ptr = std::ptr::null_mut();
let e = unsafe {
crypt_init(&mut ptr, path.as_ptr() as *const c_char)
};
if e == 0 {
Ok(CryptDevice {
cd: ptr,
callback: PhantomData
})
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn init_name_header<P:AsRef<Path>>(name: &str, header_device: Option<P>) -> Result<Self, Error> {
let mut ptr = std::ptr::null_mut();
let header_device = header_device.map(|p| c_path(p));
let c_name = std::ffi::CString::new(name).unwrap();
let e = unsafe {
crypt_init_by_name_and_header(&mut ptr, c_name.as_ptr(), if let Some(h) = header_device { h.as_ptr() as *const c_char } else { std::ptr::null() })
};
if e == 0 {
Ok(CryptDevice {
cd: ptr,
callback: PhantomData
})
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
}
impl<'cb, Cb> CryptDevice<'cb, Cb> {
pub fn with_callbacks<'cb_, Cb_:Callbacks + 'cb_>(self, usrptr: &'cb_ mut Cb_) -> Result<CryptDevice<'cb_, Cb_>, Error> {
unsafe { crypt_set_log_callback(self.cd, log_callback::<Cb_>, usrptr as *mut Cb_ as *mut c_void) }
unsafe { crypt_set_confirm_callback(self.cd, confirm_callback::<Cb_>, usrptr as *mut Cb_ as *mut c_void) }
let result = CryptDevice {
cd: self.cd,
callback: PhantomData
};
std::mem::forget(self);
Ok(result)
}
pub fn log(&mut self, level: usize, msg: &str) {
let msg = std::ffi::CString::new(msg).unwrap();
unsafe {
crypt_log(self.cd, level as c_int, msg.as_ptr())
}
}
pub fn set_data_device<P:AsRef<Path>>(&mut self, device: P) -> Result<(), Error> {
let c_device = c_path(device);
let e = unsafe {
crypt_set_data_device(
self.cd,
c_device.as_ptr() as *const c_char
)
};
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn set_iteration_time(&mut self, iteration_time: std::time::Duration) -> Result<(), Error> {
let time =
iteration_time.as_secs() * 1000
+ (iteration_time.subsec_nanos() as u64) / 1000000;
let e = unsafe {
crypt_set_iteration_time(
self.cd,
time
)
};
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn set_rng_type(&mut self, t:RngType) {
unsafe {
crypt_set_rng_type(
self.cd,
t as c_int
)
}
}
pub fn get_rng_type(&mut self) -> RngType {
let e = unsafe {
crypt_get_rng_type(self.cd)
};
if e == 0 {
RngType::URandom
} else if e == 1 {
RngType::Random
} else {
panic!("undefined RNG type {:?}", e)
}
}
pub fn get_type(&mut self) -> Result<&str, Error> {
unsafe {
let p = crypt_get_type(self.cd);
let t = std::slice::from_raw_parts(p as *const u8, strlen(p) as usize);
Ok(std::str::from_utf8(t).unwrap())
}
}
pub fn format(&mut self, parameters: Parameters, cipher: Cipher, uuid: Option<&[u8]>, volume_key: Option<&[u8]>) -> Result<(), Error> {
let uuid = uuid.map(|u| std::ffi::CString::new(u).unwrap());
let (cipher, cipher_mode, key_len) = match cipher {
Cipher::AesXts { iv, bits } => {
("aes\0",
match iv {
Iv::Plain => "xts-plain\0",
Iv::Plain64 => "xts-plain64\0",
},
bits / 4)
}
};
match parameters {
Parameters::Luks1 { hash, data_alignment, data_device } => {
let luks1 = "LUKS1\0";
let data_device = data_device.map(|d| std::ffi::CString::new(d).unwrap());
let e = unsafe {
crypt_format(
self.cd,
luks1.as_ptr() as *const c_char,
cipher.as_ptr() as *const c_char,
cipher_mode.as_ptr() as *const c_char,
if let Some(uuid) = uuid { uuid.as_ptr() } else { std::ptr::null() },
if let Some(volume_key) = volume_key {
if volume_key.len() != key_len {
return Err(Error::WrongKeyLen(key_len, volume_key.len()))
}
volume_key.as_ptr() as *const c_char
} else { std::ptr::null() },
key_len as size_t,
(&mut Luks1_ {
hash: hash.as_ptr(),
data_alignment: data_alignment as size_t,
data_device: if let Some(dev) = data_device { dev.as_ptr() } else { std::ptr::null() }
}) as *mut Luks1_ as *mut c_void
)
};
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
}
}
pub fn set_uuid(&mut self, uuid: &str) -> Result<(), Error> {
let uuid = std::ffi::CString::new(uuid).unwrap();
let e = unsafe {
crypt_set_uuid(self.cd, uuid.as_ptr())
};
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn load(&mut self) -> Result<(), Error> {
unsafe {
let p = crypt_get_type(self.cd);
let e = crypt_load(self.cd, p, std::ptr::null_mut());
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
}
pub fn repair(&mut self, parameters: Parameters) -> Result<(), Error> {
match parameters {
Parameters::Luks1 { hash, data_alignment, data_device } => {
let luks1 = "LUKS1\0";
let e = unsafe {
crypt_repair(
self.cd,
luks1.as_ptr() as *const c_char,
(&mut Luks1_ {
hash: hash.as_ptr(),
data_alignment: data_alignment as size_t,
data_device: if let Some(dev) = data_device { dev.as_ptr() as *const c_char } else { std::ptr::null() }
}) as *mut Luks1_ as *mut c_void
)
};
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
}
}
pub fn resize(&mut self, name: &str, new_size: u64) -> Result<(), Error> {
let name = std::ffi::CString::new(name).unwrap();
let e = unsafe {
crypt_resize(self.cd, name.as_ptr(), new_size)
};
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn suspend(&mut self, name:&str) -> Result<(), Error> {
let c_name = std::ffi::CString::new(name).unwrap();
unsafe {
let e = crypt_suspend(self.cd, c_name.as_ptr());
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
}
pub fn resume_by_passphrase(&mut self, name: &str, keyslot: Option<usize>, passphrase: &[u8]) -> Result<usize, Error> {
let c_name = std::ffi::CString::new(name).unwrap();
let e = unsafe {
crypt_resume_by_passphrase(self.cd,
c_name.as_ptr(),
if let Some(keyslot) = keyslot { keyslot as c_int } else { CRYPT_ANY_SLOT },
passphrase.as_ptr() as *const c_char,
passphrase.len() as size_t)
};
if e >= 0 {
Ok(e as usize)
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn resume_by_keyfile<P:AsRef<Path>>(&mut self, name: &str, keyslot: Option<usize>, keyfile: P, offset: usize) -> Result<usize, Error> {
let c_name = std::ffi::CString::new(name).unwrap();
let keyfile = c_path(keyfile);
let e = unsafe {
crypt_resume_by_keyfile_offset(self.cd,
c_name.as_ptr() as *const c_char,
if let Some(keyslot) = keyslot { keyslot as c_int } else { CRYPT_ANY_SLOT },
keyfile.as_ptr() as *const c_char,
keyfile.len() as size_t,
offset as size_t)
};
if e >= 0 {
Ok(e as usize)
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn keyslot_add_by_passphrase(&mut self, keyslot: Option<usize>, passphrase:&[u8], new_passphrase: &[u8]) -> Result<usize, Error> {
let e = unsafe {
crypt_keyslot_add_by_passphrase(
self.cd,
if let Some(keyslot) = keyslot { keyslot as c_int } else { CRYPT_ANY_SLOT },
passphrase.as_ptr() as *const c_char,
passphrase.len() as size_t,
new_passphrase.as_ptr() as *const c_char,
new_passphrase.len() as size_t
)
};
if e >= 0 {
Ok(e as usize)
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn keyslot_change_by_passphrase(&mut self, keyslot_old: Option<usize>, keyslot_new: usize, passphrase:&[u8], new_passphrase: &[u8]) -> Result<usize, Error> {
let e = unsafe {
crypt_keyslot_change_by_passphrase(
self.cd,
if let Some(keyslot) = keyslot_old { keyslot as c_int } else { CRYPT_ANY_SLOT },
keyslot_new as c_int,
passphrase.as_ptr() as *const c_char,
passphrase.len() as size_t,
new_passphrase.as_ptr() as *const c_char,
new_passphrase.len() as size_t
)
};
if e >= 0 {
Ok(e as usize)
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn keyslot_add_by_keyfile<P:AsRef<Path>>(&mut self, name: &str, keyslot: Option<usize>, keyfile: P, offset: usize) -> Result<usize, Error> {
let c_name = std::ffi::CString::new(name).unwrap();
let keyfile = c_path(keyfile);
let e = unsafe {
crypt_keyslot_add_by_keyfile_offset(self.cd,
c_name.as_ptr() as *const c_char,
if let Some(keyslot) = keyslot { keyslot as c_int } else { CRYPT_ANY_SLOT },
keyfile.as_ptr() as *const c_char,
keyfile.len() as size_t,
offset as size_t)
};
if e >= 0 {
Ok(e as usize)
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn keyslot_add_by_volume_key(&mut self, keyslot: Option<usize>, volume_key: Option<&[u8]>, passphrase:&[u8]) -> Result<usize, Error> {
let e = unsafe {
crypt_keyslot_add_by_volume_key(
self.cd,
if let Some(keyslot) = keyslot { keyslot as c_int } else { CRYPT_ANY_SLOT },
if let Some(volume_key) = volume_key { volume_key.as_ptr() as *const c_char } else { std::ptr::null() },
if let Some(volume_key) = volume_key { volume_key.len() as size_t } else { 0 },
passphrase.as_ptr() as *const c_char,
passphrase.len() as size_t
)
};
if e >= 0 {
Ok(e as usize)
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn keyslot_destroy(&mut self, keyslot: usize) -> Result<(), Error> {
let e = unsafe {
crypt_keyslot_destroy(self.cd, keyslot as c_int)
};
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn get_active_device(&mut self, name: &str) -> Result<ActiveDevice, Error> {
let name = std::ffi::CString::new(name).unwrap();
unsafe {
let mut active_device = std::mem::zeroed();
let e = crypt_get_active_device(self.cd, name.as_ptr(), &mut active_device);
if e == 0 {
Ok(active_device)
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
}
pub fn activate_by_passphrase(&mut self, name: &str, keyslot: Option<usize>, passphrase: &[u8], flags: ActivationFlags) -> Result<usize, Error> {
let c_name = std::ffi::CString::new(name).unwrap();
let e = unsafe {
crypt_activate_by_passphrase(self.cd,
c_name.as_ptr() as *const c_char,
if let Some(keyslot) = keyslot { keyslot as c_int } else { CRYPT_ANY_SLOT },
passphrase.as_ptr() as *const c_char,
passphrase.len() as size_t,
flags.bits())
};
if e >= 0 {
Ok(e as usize)
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn activate_by_keyfile<P:AsRef<Path>>(&mut self, name: &str, keyslot: Option<usize>, keyfile: P, offset: usize, flags: ActivationFlags) -> Result<usize, Error> {
let c_name = std::ffi::CString::new(name).unwrap();
let keyfile = c_path(keyfile);
let e = unsafe {
crypt_activate_by_keyfile_offset(self.cd,
c_name.as_ptr() as *const c_char,
if let Some(keyslot) = keyslot { keyslot as c_int } else { CRYPT_ANY_SLOT },
keyfile.as_ptr() as *const c_char,
keyfile.len() as size_t,
offset as size_t,
flags.bits())
};
if e >= 0 {
Ok(e as usize)
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn activate_by_volume_key(&mut self, name: &str, volume_key: &[u8], flags: ActivationFlags) -> Result<usize, Error> {
let c_name = std::ffi::CString::new(name).unwrap();
let e = unsafe {
crypt_activate_by_volume_key(self.cd,
c_name.as_ptr() as *const c_char,
volume_key.as_ptr() as *const c_char,
volume_key.len() as size_t,
flags.bits())
};
if e >= 0 {
Ok(e as usize)
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn deactivate(&mut self, name: &str) -> Result<(), Error> {
let c_name = std::ffi::CString::new(name).unwrap();
let e = unsafe {
crypt_deactivate(self.cd, c_name.as_ptr() as *const c_char)
};
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn volume_key_get(&mut self, keyslot: Option<usize>, key: &mut [u8], passphrase: &[u8]) -> Result<(), Error> {
assert_eq!(key.len(), self.get_volume_key_size());
unsafe {
let mut kl = key.len();
let passphrase_len = passphrase.len();
let e = crypt_volume_key_get(self.cd,
if let Some(k) = keyslot { k as c_int } else { CRYPT_ANY_SLOT },
key.as_mut_ptr() as *mut c_char,
&mut kl,
passphrase.as_ptr() as *const c_char,
passphrase_len as size_t
);
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
}
pub fn volume_key_verify(&mut self, key: &[u8]) -> Result<(), Error> {
unsafe {
let e = crypt_volume_key_verify(self.cd, key.as_ptr() as *const c_char, key.len());
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
}
pub fn status(&mut self, name: &str) -> StatusInfo {
let name = std::ffi::CString::new(name).unwrap();
unsafe {
crypt_status(self.cd, name.as_ptr())
}
}
pub fn dump(&mut self) -> Result<(), Error> {
let e = unsafe {
crypt_dump(self.cd)
};
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn get_cipher(&mut self) -> &str {
unsafe {
let p = crypt_get_cipher(self.cd);
std::str::from_utf8_unchecked(std::slice::from_raw_parts(p as *const u8, strlen(p)))
}
}
pub fn get_cipher_mode(&mut self) -> &str {
unsafe {
let p = crypt_get_cipher_mode(self.cd);
std::str::from_utf8_unchecked(std::slice::from_raw_parts(p as *const u8, strlen(p)))
}
}
pub fn get_uuid(&mut self) -> &str {
unsafe {
let p = crypt_get_uuid(self.cd);
std::str::from_utf8_unchecked(std::slice::from_raw_parts(p as *const u8, strlen(p)))
}
}
pub fn get_device_name(&mut self) -> &str {
unsafe {
let p = crypt_get_device_name(self.cd);
std::str::from_utf8_unchecked(std::slice::from_raw_parts(p as *const u8, strlen(p)))
}
}
pub fn get_data_offset(&mut self) -> u64 {
unsafe {
crypt_get_data_offset(self.cd)
}
}
pub fn get_iv_offset(&mut self) -> u64 {
unsafe {
crypt_get_iv_offset(self.cd)
}
}
pub fn get_volume_key_size(&mut self) -> usize {
unsafe {
crypt_get_volume_key_size(self.cd) as usize
}
}
pub fn get_verity_info(&mut self) -> Result<ParamsVerity, Error> {
unsafe {
let mut params = std::mem::zeroed();
let e = crypt_get_verity_info(self.cd, &mut params);
if e == 0 {
Ok(ParamsVerity {
hash_name: from_cstr(params.hash_name),
data_device: from_cstr(params.data_device),
hash_device: from_cstr(params.hash_device),
salt: {
assert!(!params.salt.is_null());
std::slice::from_raw_parts(params.salt as *const u8, params.salt_size as usize)
},
hash_type: params.hash_type,
data_block_size: params.data_block_size,
hash_block_size: params.hash_block_size,
data_size: params.data_size,
hash_area_offset: params.hash_area_offset,
flags: params.flags
})
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
}
pub fn benchmark(&mut self, cipher: &str, cipher_mode: &str, volume_key_size: usize, iv_size: usize, buffer_size: usize) -> Result<(f64, f64), Error> {
let mut a = 0f64;
let mut b = 0f64;
let e = unsafe {
crypt_benchmark(self.cd, cipher.as_ptr() as *const c_char, cipher_mode.as_ptr() as *const c_char, volume_key_size as size_t, iv_size as size_t, buffer_size as size_t, &mut a, &mut b)
};
if e == 0 {
Ok((a, b))
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn benchmark_kdf(&mut self, kdf: &str, hash: &str, password:&[u8], salt:&[u8]) -> Result<u64, Error> {
let mut a = 0;
let e = unsafe {
crypt_benchmark_kdf(self.cd, kdf.as_ptr()as *const c_char, hash.as_ptr()as *const c_char, password.as_ptr()as *const c_char, password.len() as size_t, salt.as_ptr() as *const c_char, salt.len() as size_t, &mut a)
};
if e == 0 {
Ok(a)
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn keyslot_status(&mut self, keyslot: Option<usize>) -> KeyslotInfo {
unsafe {
crypt_keyslot_status(self.cd,
if let Some(keyslot) = keyslot { keyslot as c_int } else { CRYPT_ANY_SLOT })
}
}
pub fn keyslot_max(&mut self) -> Result<usize, Error> {
let e = unsafe {
crypt_keyslot_max(self.cd)
};
if e >= 0 {
Ok(e as usize)
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn keyslot_area(&mut self, keyslot: usize) -> Result<KeyslotArea, Error> {
let mut a = KeyslotArea {
offset: 0,
length: 0
};
let e = unsafe {
crypt_keyslot_area(self.cd, keyslot as c_int, &mut a.offset, &mut a.length)
};
if e >= 0 {
Ok(a)
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn header_backup<P:AsRef<Path>>(&mut self, requested_type: Option<Type>, backup_file: P) -> Result<(), Error> {
let c_backup_file = c_path(backup_file);
let e = unsafe {
crypt_header_backup(
self.cd,
match requested_type {
Some(Type::Plain) => b"PLAIN\0".as_ptr(),
Some(Type::Luks1) => b"LUKS1\0".as_ptr(),
Some(Type::LoopAES) => b"LOOPAES\0".as_ptr(),
Some(Type::Verity) => b"VERITY\0".as_ptr(),
Some(Type::TCrypt) => b"TCRYPT\0".as_ptr(),
None => std::ptr::null()
} as *const c_char,
c_backup_file.as_ptr() as *const c_char
)
};
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn header_restore<P:AsRef<Path>>(&mut self, requested_type: Option<Type>, backup_file: P) -> Result<(), Error> {
let c_backup_file = c_path(backup_file);
let e = unsafe {
crypt_header_restore(
self.cd,
match requested_type {
Some(Type::Plain) => b"PLAIN\0".as_ptr(),
Some(Type::Luks1) => b"LUKS1\0".as_ptr(),
Some(Type::LoopAES) => b"LOOPAES\0".as_ptr(),
Some(Type::Verity) => b"VERITY\0".as_ptr(),
Some(Type::TCrypt) => b"TCRYPT\0".as_ptr(),
None => std::ptr::null()
} as *const c_char,
c_backup_file.as_ptr() as *const c_char
)
};
if e == 0 {
Ok(())
} else {
Err(Error::Errno(nix::errno::from_i32(-e)))
}
}
pub fn get_dir(&mut self) -> &str {
unsafe {
let p = crypt_get_dir(self.cd);
std::str::from_utf8_unchecked(std::slice::from_raw_parts(p as *const u8, strlen(p)))
}
}
}
pub struct KeyslotArea {
pub offset: u64,
pub length: u64
}
unsafe fn from_cstr<'a>(cstr:*const c_char) -> &'a str {
assert!(!cstr.is_null());
std::str::from_utf8(std::slice::from_raw_parts(cstr as *const u8, strlen(cstr))).unwrap()
}
#[derive(Debug)]
pub struct ParamsVerity<'a> {
pub hash_name: &'a str,
pub data_device: &'a str,
pub hash_device: &'a str,
pub salt: &'a [u8],
pub hash_type: u32,
pub data_block_size: u32,
pub hash_block_size: u32,
pub data_size: u64,
pub hash_area_offset: u64,
pub flags: u32
}
#[repr(C)]
struct CParamsVerity {
hash_name: *const c_char,
data_device: *const c_char,
hash_device: *const c_char,
salt: *const c_char,
salt_size: u32,
hash_type: u32,
data_block_size: u32,
hash_block_size: u32,
data_size: u64,
hash_area_offset: u64,
flags: u32
}
pub fn memory_lock() -> bool {
unsafe { crypt_memory_lock(std::ptr::null_mut(), 1) == 1 }
}
pub fn memory_unlock() -> bool {
unsafe { crypt_memory_lock(std::ptr::null_mut(), 0) == 1 }
}
pub fn set_debug_level(level: usize) {
unsafe {
crypt_set_debug_level(level as c_int)
}
}
pub enum RngType {
URandom = 0,
Random = 1,
}
#[derive(Debug)]
#[repr(C)]
pub struct ActiveDevice {
pub offset: u64,
pub iv_offset: u64,
pub size: u64,
pub flags: ActivationFlags
}
#[derive(Debug)]
pub enum Hash {
Sha1,
Sha256
}
impl Hash {
fn as_ptr(&self) -> *const c_char {
match *self {
Hash::Sha1 => "sha1\0".as_ptr() as *const c_char,
Hash::Sha256 => "sha256\0".as_ptr() as *const c_char,
}
}
}
#[derive(Debug)]
pub enum Cipher {
AesXts { iv: Iv, bits: usize }
}
#[derive(Debug)]
pub enum Iv {
Plain,
Plain64
}
#[cfg(test)]
const TEST_DEVICE:&'static str = "/dev/sdc1";
#[test]
fn format() {
let mut dev = CryptDevice::init(&TEST_DEVICE).unwrap();
println!("device initialized {:?}", dev.get_device_name());
dev.format(
Parameters::Luks1 { hash: Hash::Sha1, data_alignment: 0, data_device: None },
Cipher::AesXts { iv: Iv::Plain, bits: 128 },
None,
None
).unwrap();
println!("formatted");
dev.keyslot_add_by_volume_key(None, None, b"foo").unwrap();
dev.keyslot_add_by_passphrase(None, b"foo", b"blabla").unwrap();
}
#[test]
fn activate() {
let mut dev = CryptDevice::init(TEST_DEVICE).unwrap();
dev.load().unwrap();
let device_name = "test";
dev.activate_by_passphrase(device_name, None, b"blabla", ActivationFlags::empty()).unwrap();
println!("{:?}", dev.get_active_device(device_name));
dev.deactivate(device_name).unwrap();
}
#[test]
fn suspend_resume() {
let mut dev = CryptDevice::init(TEST_DEVICE).unwrap();
dev.load().unwrap();
let device_name = "test";
dev.activate_by_passphrase(device_name, None, b"blabla", ActivationFlags::empty()).unwrap();
println!("{:?}", dev.get_active_device(device_name));
dev.suspend(device_name).unwrap();
dev.resume_by_passphrase(device_name, None, b"blabla").unwrap();
dev.deactivate(device_name).unwrap();
}