use std::fmt;
use std::fs::File;
use std::path::{Path, PathBuf};
use std::ptr;
use blkid_rs::{BlockDevice, LuksHeader};
pub use crate::device::enable_debug;
use crate::device::RawDevice;
pub use crate::device::{Error, Keyslot, Result};
use raw;
use uuid;
pub type Luks1CryptDeviceHandle = CryptDeviceHandle<Luks1Params>;
pub fn open<P: AsRef<Path>>(path: P) -> Result<CryptDeviceOpenBuilder> {
let cd = crate::device::init(path.as_ref())?;
Ok(CryptDeviceOpenBuilder {
path: path.as_ref().to_owned(),
cd,
})
}
pub fn format<P: AsRef<Path>>(path: P) -> Result<CryptDeviceFormatBuilder> {
let cd = crate::device::init(path.as_ref())?;
Ok(CryptDeviceFormatBuilder {
path: path.as_ref().to_owned(),
cd,
})
}
pub fn luks1_uuid<P: AsRef<Path>>(path: P) -> Result<uuid::Uuid> {
let device_file = File::open(path.as_ref())?;
let luks_phdr = BlockDevice::read_luks_header(device_file)?;
let uuid = luks_phdr.uuid()?;
Ok(uuid)
}
fn load_luks1_params<P: AsRef<Path>>(path: P) -> Result<Luks1Params> {
let device_file = File::open(path.as_ref())?;
let luks_phdr = BlockDevice::read_luks_header(device_file)?;
Luks1Params::from(luks_phdr)
}
pub struct CryptDeviceOpenBuilder {
path: PathBuf,
cd: RawDevice,
}
impl CryptDeviceOpenBuilder {
pub fn luks1(self: CryptDeviceOpenBuilder) -> Result<CryptDeviceHandle<Luks1Params>> {
let _ = crate::device::load(&self.cd, raw::crypt_device_type::LUKS1);
let params = load_luks1_params(&self.path)?;
Ok(CryptDeviceHandle {
cd: self.cd,
path: self.path,
params,
})
}
}
pub struct CryptDeviceFormatBuilder {
path: PathBuf,
cd: RawDevice,
}
impl CryptDeviceFormatBuilder {
pub fn iteration_time(mut self, iteration_time_ms: u64) -> Self {
crate::device::set_iteration_time(&mut self.cd, iteration_time_ms);
self
}
pub fn rng_type(mut self, rng_type: raw::crypt_rng_type) -> Self {
crate::device::set_rng_type(&mut self.cd, rng_type);
self
}
pub fn luks1(
mut self: CryptDeviceFormatBuilder,
cipher: &str,
cipher_mode: &str,
hash: &str,
mk_bits: usize,
maybe_uuid: Option<&uuid::Uuid>,
) -> Result<CryptDeviceHandle<Luks1Params>> {
let _ = crate::device::luks1_format(&mut self.cd, cipher, cipher_mode, hash, mk_bits, maybe_uuid)?;
let params = load_luks1_params(&self.path)?;
Ok(CryptDeviceHandle {
cd: self.cd,
path: self.path,
params,
})
}
}
pub trait CryptDevice {
fn path(&self) -> &Path;
fn cipher(&self) -> &str;
fn cipher_mode(&self) -> &str;
fn device_name(&self) -> &str;
fn rng_type(&self) -> raw::crypt_rng_type;
fn set_rng_type(&mut self, rng_type: raw::crypt_rng_type);
fn set_iteration_time(&mut self, iteration_time_ms: u64);
fn volume_key_size(&self) -> u8;
}
pub trait CryptDeviceType {
fn device_type(&self) -> raw::crypt_device_type;
}
pub trait Luks1CryptDevice {
fn activate(&mut self, name: &str, key: &[u8]) -> Result<Keyslot>;
fn add_keyslot(
&mut self,
key: &[u8],
maybe_prev_key: Option<&[u8]>,
maybe_keyslot: Option<Keyslot>,
) -> Result<Keyslot>;
fn update_keyslot(&mut self, key: &[u8], prev_key: &[u8], maybe_keyslot: Option<Keyslot>) -> Result<Keyslot>;
fn destroy_keyslot(&mut self, slot: Keyslot) -> Result<()>;
fn dump(&self);
fn hash_spec(&self) -> &str;
fn keyslot_status(&self, keyslot: Keyslot) -> raw::crypt_keyslot_info;
fn mk_bits(&self) -> u32;
fn mk_digest(&self) -> &[u8; 20];
fn mk_iterations(&self) -> u32;
fn mk_salt(&self) -> &[u8; 32];
fn payload_offset(&self) -> u32;
fn uuid(&self) -> uuid::Uuid;
}
#[derive(PartialEq)]
pub struct CryptDeviceHandle<P: fmt::Debug> {
cd: RawDevice,
path: PathBuf,
params: P,
}
impl<P: fmt::Debug> fmt::Debug for CryptDeviceHandle<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"CryptDeviceHandle(path={}, raw={:p}, params={:?})",
self.path.display(),
self.cd,
self.params
)
}
}
impl<P: fmt::Debug> Drop for CryptDeviceHandle<P> {
fn drop(&mut self) {
crate::device::free(&mut self.cd);
self.cd = ptr::null_mut();
}
}
impl<P: fmt::Debug> CryptDevice for CryptDeviceHandle<P> {
fn path(&self) -> &Path {
self.path.as_ref()
}
fn cipher(&self) -> &str {
crate::device::cipher(&self.cd).expect("Initialised device should have cipher")
}
fn cipher_mode(&self) -> &str {
crate::device::cipher_mode(&self.cd).expect("Initialised device should have cipher mode")
}
fn device_name(&self) -> &str {
crate::device::device_name(&self.cd).expect("Initialised device should have an underlying path")
}
fn rng_type(&self) -> raw::crypt_rng_type {
crate::device::rng_type(&self.cd)
}
fn set_rng_type(&mut self, rng_type: raw::crypt_rng_type) {
crate::device::set_rng_type(&mut self.cd, rng_type)
}
fn set_iteration_time(&mut self, iteration_time_ms: u64) {
crate::device::set_iteration_time(&mut self.cd, iteration_time_ms)
}
fn volume_key_size(&self) -> u8 {
crate::device::volume_key_size(&self.cd)
}
}
#[derive(Debug, PartialEq)]
pub struct Luks1Params {
hash_spec: String,
payload_offset: u32,
mk_bits: u32,
mk_digest: [u8; 20],
mk_salt: [u8; 32],
mk_iterations: u32,
}
impl Luks1Params {
fn from(header: impl LuksHeader) -> Result<Luks1Params> {
let hash_spec = header.hash_spec()?.to_owned();
let payload_offset = header.payload_offset();
let mk_bits = header.key_bytes() * 8;
let mut mk_digest = [0u8; 20];
mk_digest.copy_from_slice(header.mk_digest());
let mut mk_salt = [0u8; 32];
mk_salt.copy_from_slice(header.mk_digest_salt());
let mk_iterations = header.mk_digest_iterations();
Ok(Luks1Params {
hash_spec,
payload_offset,
mk_bits,
mk_digest,
mk_salt,
mk_iterations,
})
}
}
impl Luks1CryptDevice for CryptDeviceHandle<Luks1Params> {
fn activate(&mut self, name: &str, key: &[u8]) -> Result<Keyslot> {
crate::device::luks_activate(&mut self.cd, name, key)
}
fn add_keyslot(
&mut self,
key: &[u8],
maybe_prev_key: Option<&[u8]>,
maybe_keyslot: Option<Keyslot>,
) -> Result<Keyslot> {
crate::device::luks_add_keyslot(&mut self.cd, key, maybe_prev_key, maybe_keyslot)
}
fn update_keyslot(&mut self, key: &[u8], prev_key: &[u8], maybe_keyslot: Option<Keyslot>) -> Result<Keyslot> {
crate::device::luks_update_keyslot(&mut self.cd, key, prev_key, maybe_keyslot)
}
fn destroy_keyslot(&mut self, slot: Keyslot) -> Result<()> {
crate::device::luks_destroy_keyslot(&mut self.cd, slot)
}
fn dump(&self) {
crate::device::dump(&self.cd).expect("Dump should be fine for initialised device")
}
fn hash_spec(&self) -> &str {
self.params.hash_spec.as_ref()
}
fn keyslot_status(&self, keyslot: Keyslot) -> raw::crypt_keyslot_info {
crate::device::keyslot_status(&self.cd, keyslot)
}
fn mk_bits(&self) -> u32 {
self.params.mk_bits
}
fn mk_digest(&self) -> &[u8; 20] {
&self.params.mk_digest
}
fn mk_iterations(&self) -> u32 {
self.params.mk_iterations
}
fn mk_salt(&self) -> &[u8; 32] {
&self.params.mk_salt
}
fn payload_offset(&self) -> u32 {
self.params.payload_offset
}
fn uuid(&self) -> uuid::Uuid {
crate::device::uuid(&self.cd).expect("LUKS1 device should have UUID")
}
}
impl CryptDeviceType for CryptDeviceHandle<Luks1Params> {
fn device_type(&self) -> raw::crypt_device_type {
raw::crypt_device_type::LUKS1
}
}