vendor patches & crates.io compliant metadata
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is failing

This commit is contained in:
2019-09-25 17:31:44 +02:00
parent 95a4f97f58
commit c44b0c35ba
33 changed files with 4120 additions and 19 deletions

4
patch/cryptsetup-rs/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
target/
Cargo.lock
*.iml
.idea/

View File

@@ -0,0 +1,22 @@
language: rust
rust:
- stable
- beta
- nightly
matrix:
allow_failures:
- rust: nightly
sudo: required
dist: trusty
os:
- linux
script:
- cargo build --all-targets --verbose
- sudo /bin/sh -c "PATH=/home/travis/.cargo/bin:$PATH LD_LIBRARY_PATH=/home/travis/.cargo/lib cargo test --verbose"
addons:
apt:
packages:
- libcryptsetup-dev
notifications:
email:
on_success: never

View File

@@ -0,0 +1,32 @@
[package]
authors = ["Vladimir Lushnikov <vladimir@solidninja.is>"]
description = "Rust wrapper around the libcryptsetup library, allowing manipulation of LUKS devices in Linux"
homepage = "https://github.com/solidninja/cryptsetup-rs"
license = "LGPL-3.0"
name = "cryptsetup-rs"
version = "0.2.0"
[dependencies]
errno = "0.2.3"
libc = "0.2.42"
log = "0.4.2"
[dependencies.blkid-rs]
path = "blkid-rs"
version = "0.1.1"
[dependencies.libcryptsetup-sys]
path = "libcryptsetup-sys"
version = "0.1.1"
[dependencies.uuid]
features = ["v4"]
version = "0.6.5"
[dev-dependencies]
env_logger = "0.5.10"
expectest = "0.10.0"
tempdir = "0.3.7"
[lib]
name = "cryptsetup_rs"

165
patch/cryptsetup-rs/LICENSE Normal file
View File

@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@@ -0,0 +1,29 @@
[![Build Status](https://travis-ci.org/solidninja/cryptsetup-rs.svg?branch=master)](https://travis-ci.org/solidninja/cryptsetup-rs)
[![crates.io Status](https://img.shields.io/crates/v/cryptsetup-rs.svg)](https://crates.io/crates/cryptsetup-rs)
[![docs.rs build](https://docs.rs/cryptsetup-rs/badge.svg)](https://docs.rs/crate/cryptsetup-rs/)
# cryptsetup-rs - Rust bindings to `libcryptsetup` on Linux
A safe binding to `libcryptsetup` that allows working with encrypted disks on Linux.
Features:
* High-level API for open/format/other operations
Documentation for the bindings can be found on [docs.rs](https://docs.rs/crate/cryptsetup-rs/).
The example [`luks_dump.rs`](examples/luks_dump.rs) shows how a command like `cryptsetup luksDump` can
be implemented.
## TODO
* Secure string for passing keys
* High-level API for non-LUKS1 disks (truecrypt, verity)
* LUKS2 and cryptsetup2 support
## Contributing
`cryptsetup-rs` is the work of its contributors and is a free software project licensed under the
LGPLv3 or later.
If you would like to contribute, please follow the [C4](https://rfc.zeromq.org/spec:42/C4/) process.

View File

@@ -0,0 +1,2 @@
target/
Cargo.lock

View File

@@ -0,0 +1,18 @@
[package]
name = "blkid-rs"
version = "0.1.1"
authors = ["Vladimir Lushnikov <vladimir@solidninja.is>"]
description = "Rust implementation of blkid for LUKS volumes"
license = "LGPL-3.0"
homepage = "https://github.com/solidninja/libcryptset-rs"
[lib]
name = "blkid_rs"
[dependencies]
byteorder = "1.1"
uuid = "0.6"
[dev-dependencies]
env_logger = "0.4"
tempdir = "0.3"

View File

@@ -0,0 +1,286 @@
// The following code has been ported from libcryptsetup
extern crate byteorder;
extern crate uuid;
use std::convert;
use std::io;
use std::io::Read;
use std::mem;
use std::str;
pub trait LuksHeader {
fn version(&self) -> u16;
fn cipher_name(&self) -> Result<&str, Error>;
fn cipher_mode(&self) -> Result<&str, Error>;
fn hash_spec(&self) -> Result<&str, Error>;
fn payload_offset(&self) -> u32;
fn key_bytes(&self) -> u32;
fn mk_digest(&self) -> &[u8];
fn mk_digest_salt(&self) -> &[u8];
fn mk_digest_iterations(&self) -> u32;
fn uuid(&self) -> Result<uuid::Uuid, Error>;
}
#[derive(Debug)]
pub enum Error {
InvalidMagic,
InvalidStringEncoding(str::Utf8Error),
InvalidVersion,
InvalidUuid(uuid::ParseError),
ReadError(io::Error),
ReadIncorrectHeaderSize,
HeaderProcessingError,
}
pub struct BlockDevice;
impl BlockDevice {
pub fn read_luks_header<R: Read>(reader: R) -> Result<raw::luks_phdr, Error> {
let luks_phdr_size = mem::size_of::<raw::luks_phdr>();
let mut buf = Vec::<u8>::with_capacity(luks_phdr_size);
let read_size = try!(reader.take(luks_phdr_size as u64).read_to_end(&mut buf));
if read_size != luks_phdr_size {
Err(Error::ReadIncorrectHeaderSize)
} else {
raw::luks_phdr::from_buf(&buf)
}
}
}
impl convert::From<str::Utf8Error> for Error {
fn from(error: str::Utf8Error) -> Error {
Error::InvalidStringEncoding(error)
}
}
impl convert::From<uuid::ParseError> for Error {
fn from(error: uuid::ParseError) -> Error {
Error::InvalidUuid(error)
}
}
impl convert::From<io::Error> for Error {
fn from(error: io::Error) -> Error {
Error::ReadError(error)
}
}
/* FIXME
impl convert::From<byteorder::Error> for Error {
fn from(error: byteorder::Error) -> Error {
match error {
byteorder::Error::UnexpectedEOF => Error::HeaderProcessingError,
byteorder::Error::Io(io_error) => Error::ReadError(io_error),
}
}
}
*/
pub mod raw {
#![allow(non_snake_case)]
use std::convert::From;
use std::io::{Cursor, Read};
use std::mem;
use std::str;
use byteorder::{BigEndian, ReadBytesExt};
use uuid;
const LUKS_VERSION_SUPPORTED: u16 = 1;
const LUKS_MAGIC_L: usize = 6;
const LUKS_CIPHERNAME_L: usize = 32;
const LUKS_CIPHERMODE_L: usize = 32;
const LUKS_HASHSPEC_L: usize = 32;
const LUKS_DIGESTSIZE: usize = 20;
const LUKS_SALTSIZE: usize = 32;
const UUID_STRING_L: usize = 40;
const LUKS_MAGIC: &'static [u8; LUKS_MAGIC_L] = b"LUKS\xba\xbe";
#[repr(C, packed)]
pub struct luks_phdr {
pub magic: [u8; LUKS_MAGIC_L],
pub version: u16,
pub cipherName: [u8; LUKS_CIPHERNAME_L],
pub cipherMode: [u8; LUKS_CIPHERMODE_L],
pub hashSpec: [u8; LUKS_HASHSPEC_L],
pub payloadOffset: u32,
pub keyBytes: u32,
pub mkDigest: [u8; LUKS_DIGESTSIZE],
pub mkDigestSalt: [u8; LUKS_SALTSIZE],
pub mkDigestIterations: u32,
pub uuid: [u8; UUID_STRING_L],
}
impl luks_phdr {
pub fn from_buf(buf: &[u8]) -> Result<luks_phdr, super::Error> {
// FIXME - this is not particularly pretty
if buf.len() != mem::size_of::<luks_phdr>() {
return Err(super::Error::ReadIncorrectHeaderSize);
}
let mut cursor = Cursor::new(buf);
let mut magic_buf = [0u8; LUKS_MAGIC_L];
let magic_len = try!(cursor.read(&mut magic_buf));
if magic_len != LUKS_MAGIC_L || magic_buf != &LUKS_MAGIC[..] {
return Err(super::Error::InvalidMagic);
}
let version = try!(cursor.read_u16::<BigEndian>());
if version != LUKS_VERSION_SUPPORTED {
return Err(super::Error::InvalidVersion);
}
let mut cipher_name_buf = [0u8; LUKS_CIPHERNAME_L];
let cipher_name_len = try!(cursor.read(&mut cipher_name_buf));
if cipher_name_len != LUKS_CIPHERNAME_L {
return Err(super::Error::HeaderProcessingError);
}
let mut cipher_mode_buf = [0u8; LUKS_CIPHERMODE_L];
let cipher_mode_len = try!(cursor.read(&mut cipher_mode_buf));
if cipher_mode_len != LUKS_CIPHERMODE_L {
return Err(super::Error::HeaderProcessingError);
}
let mut hash_spec_buf = [0u8; LUKS_HASHSPEC_L];
let hash_spec_len = try!(cursor.read(&mut hash_spec_buf));
if hash_spec_len != LUKS_HASHSPEC_L {
return Err(super::Error::HeaderProcessingError);
}
let payload_offset = try!(cursor.read_u32::<BigEndian>());
let key_bytes = try!(cursor.read_u32::<BigEndian>());
let mut mk_digest_buf = [0u8; LUKS_DIGESTSIZE];
let mk_digest_len = try!(cursor.read(&mut mk_digest_buf));
if mk_digest_len != LUKS_DIGESTSIZE {
return Err(super::Error::HeaderProcessingError);
}
let mut mk_digest_salt_buf = [0u8; LUKS_SALTSIZE];
let mk_digest_salt_len = try!(cursor.read(&mut mk_digest_salt_buf));
if mk_digest_salt_len != LUKS_SALTSIZE {
return Err(super::Error::HeaderProcessingError);
}
let mk_digest_iterations = try!(cursor.read_u32::<BigEndian>());
let mut uuid_buf = [0u8; UUID_STRING_L];
let uuid_len = try!(cursor.read(&mut uuid_buf));
if uuid_len != UUID_STRING_L {
return Err(super::Error::HeaderProcessingError);
}
let res = luks_phdr {
magic: magic_buf,
version: version,
cipherName: cipher_name_buf,
cipherMode: cipher_mode_buf,
hashSpec: hash_spec_buf,
payloadOffset: payload_offset,
keyBytes: key_bytes,
mkDigest: mk_digest_buf,
mkDigestSalt: mk_digest_salt_buf,
mkDigestIterations: mk_digest_iterations,
uuid: uuid_buf,
};
Ok(res)
}
}
fn u8_buf_to_str(buf: &[u8]) -> Result<&str, super::Error> {
if let Some(pos) = buf.iter().position(|&c| c == 0) {
str::from_utf8(&buf[0..pos]).map_err(From::from)
} else {
str::from_utf8(buf).map_err(From::from)
}
}
impl super::LuksHeader for luks_phdr {
fn version(&self) -> u16 {
self.version
}
fn cipher_name(&self) -> Result<&str, super::Error> {
u8_buf_to_str(&self.cipherName)
}
fn cipher_mode(&self) -> Result<&str, super::Error> {
u8_buf_to_str(&self.cipherMode)
}
fn hash_spec(&self) -> Result<&str, super::Error> {
u8_buf_to_str(&self.hashSpec)
}
fn payload_offset(&self) -> u32 {
self.payloadOffset
}
fn key_bytes(&self) -> u32 {
self.keyBytes
}
fn mk_digest(&self) -> &[u8] {
&self.mkDigest
}
fn mk_digest_salt(&self) -> &[u8] {
&self.mkDigestSalt
}
fn mk_digest_iterations(&self) -> u32 {
self.mkDigestIterations
}
fn uuid(&self) -> Result<uuid::Uuid, super::Error> {
let uuid_str = try!(u8_buf_to_str(&self.uuid));
uuid::Uuid::parse_str(uuid_str).map_err(From::from)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
use uuid;
#[test]
fn test_luks_header_from_byte_buffer() {
let header = b"LUKS\xba\xbe\x00\x01aes\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ecb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00sha256\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00 \xcf^\xb4\xc00q\xbe\xd5\xe6\x90\xc8G\xb3\x00\xbe\xba\xd052qp\x92\x0c\x9c\xa9\x07R\\y_D\x08b\xf1\xe6\x8f\x0c\xa95\xad\xdb\x15+\xa5\xd7\xa7\xbf^\x96B\x90z\x00\x00\x03\xe8a1b49d2d-8a7e-4b04-ab2a-89f3408fd198\x00\x00\x00\x00";
let mut cursor: Cursor<&[u8]> = Cursor::new(header);
let luks_header = BlockDevice::read_luks_header(&mut cursor).unwrap();
assert_eq!(luks_header.version(), 1);
assert_eq!(luks_header.cipher_name().unwrap(), "aes");
assert_eq!(luks_header.cipher_mode().unwrap(), "ecb");
assert_eq!(luks_header.hash_spec().unwrap(), "sha256");
assert_eq!(luks_header.payload_offset(), 4096);
assert_eq!(luks_header.key_bytes(), 32);
assert_eq!(
luks_header.mk_digest(),
&[
207, 94, 180, 192, 48, 113, 190, 213, 230, 144, 200, 71, 179, 0, 190, 186, 208, 53,
50, 113
]
);
assert_eq!(
luks_header.mk_digest_salt(),
&[
112, 146, 12, 156, 169, 7, 82, 92, 121, 95, 68, 8, 98, 241, 230, 143, 12, 169, 53,
173, 219, 21, 43, 165, 215, 167, 191, 94, 150, 66, 144, 122
]
);
assert_eq!(luks_header.mk_digest_iterations(), 1000);
assert_eq!(
luks_header.uuid().unwrap(),
uuid::Uuid::parse_str("a1b49d2d-8a7e-4b04-ab2a-89f3408fd198").unwrap()
)
}
}

View File

@@ -0,0 +1,78 @@
#![deny(warnings)]
#[warn(unused_must_use)]
extern crate cryptsetup_rs;
extern crate env_logger;
use cryptsetup_rs::*;
use std::env;
fn dump_slot(crypt_device: &Luks1CryptDevice, slot: Keyslot) -> Result<()> {
let status = match crypt_device.keyslot_status(slot) {
crypt_keyslot_info::CRYPT_SLOT_INVALID => "INVALID",
crypt_keyslot_info::CRYPT_SLOT_INACTIVE => "DISABLED",
crypt_keyslot_info::CRYPT_SLOT_ACTIVE | crypt_keyslot_info::CRYPT_SLOT_ACTIVE_LAST => "ENABLED",
};
println!("Key Slot {}: {}", slot, status);
match status {
"ENABLED" => /* TODO add keyslot information */ (),
_ => (),
}
Ok(())
}
fn dump(device_path: &str) -> Result<()> {
let dev = open(device_path)?.luks1()?;
println!("LUKS header information for {}", dev.device_name());
println!();
println!("{:<16}{}", "Version:", "1");
println!("{:<16}{}", "Cipher name:", dev.cipher());
println!("{:<16}{}", "Cipher mode:", dev.cipher_mode());
println!("{:<16}{}", "Hash spec:", dev.hash_spec());
println!("{:<16}{}", "Payload offset:", dev.payload_offset());
println!("{:<16}{}", "MK bits:", dev.mk_bits());
print!("{:<16}", "MK digest:");
for b in dev.mk_digest().iter() {
print!("{:x} ", b);
}
println!();
let salt = dev.mk_salt();
print!("{:<16}", "MK salt:");
for b in salt[0..16].iter() {
print!("{:x} ", b);
}
println!();
print!("{:<16}", "");
for b in salt[16..32].iter() {
print!("{:x} ", b);
}
println!();
println!("{:<16}{}", "MK iterations:", dev.mk_iterations());
println!("{:<16}{}", "UUID:", dev.uuid());
println!();
for slot in 0..8 {
dump_slot(&dev, slot)?;
}
Ok(())
}
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
if args.len() != 1 {
println!("Usage: luks_dump <device path>");
::std::process::exit(1);
}
let device_path = args[0].as_str();
if let Err(e) = dump(device_path) {
println!("Error: {:?}", e);
::std::process::exit(2);
}
}

View File

@@ -0,0 +1,21 @@
[package]
name = "libcryptsetup-sys"
version = "0.1.1"
authors = ["Vladimir Lushnikov <vladimir@solidninja.is>"]
license = "LGPL-3.0"
description = "FFI bindings to the libcryptsetup library"
homepage = "https://github.com/solidninja/cryptsetup-rs"
links = "cryptsetup"
build = "build.rs"
[lib]
name = "libcryptsetup_sys"
path = "lib.rs"
[dependencies]
libc = "0.2"
[build-dependencies]
pkg-config = "0.3"

View File

@@ -0,0 +1,8 @@
extern crate pkg_config;
fn main() {
pkg_config::Config::new()
.statik(true)
.find("libcryptsetup")
.unwrap();
}

View File

@@ -0,0 +1,492 @@
#![deny(warnings)]
#![allow(non_camel_case_types)]
extern crate libc;
use libc::{c_char, c_double, c_int, c_uint, c_void, size_t};
use std::str::FromStr;
pub enum crypt_device {}
#[repr(i32)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum crypt_log_level {
CRYPT_LOG_NORMAL = 0,
CRYPT_LOG_ERROR = 1,
CRYPT_LOG_VERBOSE = 2,
CRYPT_LOG_DEBUG = -1,
}
pub type crypt_log_cb = extern "C" fn(crypt_log_level, *const c_char, *mut c_void);
pub type crypt_confirm_cb = extern "C" fn(*const c_char, *mut c_void) -> c_int;
pub type crypt_password_cb =
extern "C" fn(*const c_char, *mut c_char, size_t, *mut c_void) -> c_int;
#[repr(i32)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum crypt_rng_type {
CRYPT_RNG_URANDOM = 0,
CRYPT_RNG_RANDOM = 1,
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum crypt_device_type {
PLAIN,
LUKS1,
LOOPAES,
VERITY,
TCRYPT,
}
#[repr(C)]
pub struct crypt_params_plain {
pub hash: *const c_char,
pub offset: u64,
pub skip: u64,
pub size: u64,
}
#[repr(C)]
pub struct crypt_params_luks1 {
pub hash: *const c_char,
pub data_alignment: size_t,
pub data_device: *const c_char,
}
#[repr(C)]
pub struct crypt_params_loopaes {
pub hash: *const c_char,
pub offset: u64,
pub skip: u64,
}
#[repr(C)]
pub struct crypt_params_verity {
pub hash_name: *const c_char,
pub data_device: *const c_char,
pub hash_device: *const c_char,
pub salt: *const c_char,
pub salt_size: u32,
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(u32)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum crypt_verity_flag {
CRYPT_VERITY_NO_HEADER = (1 << 0),
CRYPT_VERITY_CHECK_HASH = (1 << 1),
CRYPT_VERITY_CREATE_HASH = (1 << 2),
}
#[repr(C)]
pub struct crypt_params_tcrypt {
pub passphrase: *const c_char,
pub passphrase_size: size_t,
pub keyfiles: *const *const c_char,
pub keyfiles_count: c_uint,
pub hash_name: *const c_char,
pub cipher: *const c_char,
pub mode: *const c_char,
pub key_size: size_t,
pub flags: u32,
}
#[repr(u32)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum crypt_tcrypt_flag {
CRYPT_TCRYPT_LEGACY_MODES = (1 << 0),
CRYPT_TCRYPT_HIDDEN_HEADER = (1 << 1),
CRYPT_TCRYPT_BACKUP_HEADER = (1 << 2),
CRYPT_TCRYPT_SYSTEM_HEADER = (1 << 3),
CRYPT_TCRYPT_VERA_MODES = (1 << 4),
}
pub const CRYPT_ANY_SLOT: c_int = -1;
#[repr(u32)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum crypt_activation_flag {
CRYPT_ACTIVATE_READONLY = (1 << 0),
CRYPT_ACTIVATE_NO_UUID = (1 << 1),
CRYPT_ACTIVATE_SHARED = (1 << 2),
CRYPT_ACTIVATE_ALLOW_DISCARDS = (1 << 3),
CRYPT_ACTIVATE_PRIVATE = (1 << 4),
CRYPT_ACTIVATE_CORRUPTED = (1 << 5),
CRYPT_ACTIVATE_SAME_CPU_CRYPT = (1 << 6),
CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS = (1 << 7),
CRYPT_ACTIVATE_IGNORE_CORRUPTION = (1 << 8),
CRYPT_ACTIVATE_RESTART_ON_CORRUPTION = (1 << 9),
CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS = (1 << 10),
}
#[repr(C)]
pub struct crypt_active_device {
pub offset: u64,
pub iv_offset: u64,
pub size: u64,
pub flags: u32,
}
#[repr(C)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum crypt_status_info {
CRYPT_INVALID,
CRYPT_INACTIVE,
CRYPT_ACTIVE,
CRYPT_BUSY,
}
#[repr(C)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum crypt_keyslot_info {
CRYPT_SLOT_INVALID,
CRYPT_SLOT_INACTIVE,
CRYPT_SLOT_ACTIVE,
CRYPT_SLOT_ACTIVE_LAST,
}
#[repr(C)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum crypt_debug_level {
CRYPT_DEBUG_ALL = -1,
CRYPT_DEBUG_NONE = 0,
}
extern "C" {
pub fn crypt_init(cd: *mut *mut crypt_device, device: *const c_char) -> c_int;
pub fn crypt_init_by_name_and_header(
cd: *mut *mut crypt_device,
name: *const c_char,
header_device: *const c_char,
) -> c_int;
pub fn crypt_init_by_name(cd: *mut *mut crypt_device, name: *const c_char) -> c_int;
pub fn crypt_set_log_callback(
cd: *mut crypt_device,
log: Option<crypt_log_cb>,
usrptr: *mut c_void,
);
pub fn crypt_log(cd: *mut crypt_device, level: crypt_log_level, msg: *const c_char);
pub fn crypt_set_confirm_callback(
cd: *mut crypt_device,
confirm: crypt_confirm_cb,
usrptr: *mut c_void,
);
#[deprecated]
pub fn crypt_set_password_callback(
cd: *mut crypt_device,
password: crypt_password_cb,
usrptr: *mut c_void,
);
#[deprecated]
pub fn crypt_set_timeout(cd: *mut crypt_device, timeout: u64);
#[deprecated]
pub fn crypt_set_password_retry(cd: *mut crypt_device, tries: c_int);
pub fn crypt_set_iteration_time(cd: *mut crypt_device, iteration_time_ms: u64);
#[deprecated]
pub fn crypt_set_password_verify(cd: *mut crypt_device, password_verify: c_int);
pub fn crypt_set_data_device(cd: *mut crypt_device, device: *const c_char) -> c_int;
pub fn crypt_set_rng_type(cd: *mut crypt_device, rng_type: crypt_rng_type);
pub fn crypt_get_rng_type(cd: *mut crypt_device) -> c_int;
pub fn crypt_memory_lock(cd: *mut crypt_device, lock: c_int) -> c_int;
pub fn crypt_get_type(cd: *mut crypt_device) -> *const c_char;
pub fn crypt_format(
cd: *mut crypt_device,
crypt_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;
pub fn crypt_set_uuid(cd: *mut crypt_device, uuid: *const c_char) -> c_int;
pub fn crypt_load(
cd: *mut crypt_device,
requested_type: *const c_char,
params: *mut c_void,
) -> c_int;
pub fn crypt_repair(
cd: *mut crypt_device,
requested_type: *const c_char,
params: *mut c_void,
) -> c_int;
pub fn crypt_resize(cd: *mut crypt_device, name: *const c_char, new_size: u64) -> c_int;
pub fn crypt_suspend(cd: *mut crypt_device, name: *const c_char) -> c_int;
pub fn crypt_resume_by_passphrase(
cd: *mut crypt_device,
name: *const c_char,
keyslot: c_int,
passphrase: *const c_char,
passphrase_size: size_t,
) -> c_int;
pub fn crypt_resume_by_keyfile_offset(
cd: *mut crypt_device,
name: *const c_char,
keyslot: c_int,
keyfile: *const c_char,
keyfile_size: size_t,
keyfile_offset: size_t,
) -> c_int;
pub fn crypt_resume_by_keyfile(
cd: *mut crypt_device,
name: *const c_char,
keyslot: c_int,
keyfile: *const c_char,
keyfile_size: size_t,
) -> c_int;
pub fn crypt_free(cd: *mut crypt_device);
pub fn crypt_keyslot_add_by_passphrase(
cd: *mut crypt_device,
keyslot: c_int,
passphrase: *const c_char,
passphrase_size: size_t,
new_passphrase: *const c_char,
new_passphrase_size: size_t,
) -> c_int;
pub fn crypt_keyslot_change_by_passphrase(
cd: *mut crypt_device,
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;
pub fn crypt_keyslot_add_by_keyfile_offset(
cd: *mut crypt_device,
keyslot: c_int,
keyfile: *const c_char,
keyfile_size: size_t,
keyfile_offset: size_t,
new_keyfile: *const c_char,
new_keyfile_size: size_t,
new_keyfile_offset: size_t,
) -> c_int;
pub fn crypt_keyslot_add_by_keyfile(
cd: *mut crypt_device,
keyslot: c_int,
keyfile: *const c_char,
keyfile_size: size_t,
new_keyfile: *const c_char,
new_keyfile_size: size_t,
) -> c_int;
pub fn crypt_keyslot_add_by_volume_key(
cd: *mut crypt_device,
keyslot: c_int,
volume_key: *const c_char,
volume_key_size: size_t,
passphrase: *const c_char,
passphrase_size: size_t,
) -> c_int;
pub fn crypt_keyslot_destroy(cd: *mut crypt_device, keyslot: c_int) -> c_int;
pub fn crypt_get_active_device(
cd: *mut crypt_device,
name: *const c_char,
cad: *mut crypt_active_device,
) -> c_int;
pub fn crypt_activate_by_passphrase(
cd: *mut crypt_device,
name: *const c_char,
keyslot: c_int,
passphrase: *const c_char,
passphrase_size: size_t,
flags: u32,
) -> c_int;
pub fn crypt_activate_by_keyfile_offset(
cd: *mut crypt_device,
name: *const c_char,
keyslot: c_int,
keyfile: *const c_char,
keyfile_size: size_t,
keyfile_offset: size_t,
flags: u32,
) -> c_int;
pub fn crypt_activate_by_keyfile(
cd: *mut crypt_device,
name: *const c_char,
keyslot: c_int,
keyfile: *const c_char,
keyfile_size: size_t,
flags: u32,
) -> c_int;
pub fn crypt_activate_by_volume_key(
cd: *mut crypt_device,
name: *const c_char,
volume_key: *const c_char,
volume_key_size: size_t,
flags: u32,
) -> c_int;
pub fn crypt_deactivate(cd: *mut crypt_device, name: *const c_char) -> c_int;
pub fn crypt_volume_key_get(
cd: *mut crypt_device,
keyslot: c_int,
volume_key: *mut c_char,
volume_key_size: *mut size_t,
passphrase: *const c_char,
passphrase_size: size_t,
) -> c_int;
pub fn crypt_volume_key_verify(
cd: *mut crypt_device,
volume_key: *const c_char,
volume_key_size: size_t,
) -> c_int;
pub fn crypt_status(cd: *mut crypt_device, name: *const c_char) -> crypt_status_info;
pub fn crypt_dump(cd: *mut crypt_device) -> c_int;
pub fn crypt_get_cipher(cd: *mut crypt_device) -> *const c_char;
pub fn crypt_get_cipher_mode(cd: *mut crypt_device) -> *const c_char;
pub fn crypt_get_uuid(cd: *mut crypt_device) -> *const c_char;
pub fn crypt_get_device_name(cd: *mut crypt_device) -> *const c_char;
pub fn crypt_get_data_offset(cd: *mut crypt_device) -> u64;
pub fn crypt_get_iv_offset(cd: *mut crypt_device) -> u64;
pub fn crypt_get_volume_key_size(cd: *mut crypt_device) -> c_int;
pub fn crypt_get_verity_info(cd: *mut crypt_device, vp: *mut crypt_params_verity);
pub fn crypt_benchmark(
cd: *mut crypt_device,
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 c_double,
decryption_mbs: *mut c_double,
) -> c_int;
pub fn crypt_benchmark_kdf(
cd: *mut crypt_device,
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;
pub fn crypt_keyslot_status(cd: *mut crypt_device, keyslot: c_int) -> crypt_keyslot_info;
pub fn crypt_keyslot_max(crypt_device_type: *const c_char) -> c_int;
pub fn crypt_keyslot_area(
cd: *mut crypt_device,
keyslot: c_int,
offset: *mut u64,
length: *mut u64,
) -> c_int;
pub fn crypt_header_backup(
cd: *mut crypt_device,
requested_type: *const c_char,
backup_file: *const c_char,
) -> c_int;
pub fn crypt_header_restore(
cd: *mut crypt_device,
requested_type: *const c_char,
backup_file: *const c_char,
) -> c_int;
#[deprecated]
pub fn crypt_last_error(cd: *mut crypt_device, buf: *mut c_char, size: size_t);
#[deprecated]
pub fn crypt_get_error(buf: *mut c_char, size: size_t);
pub fn crypt_get_dir() -> *const c_char;
pub fn crypt_set_debug_level(level: crypt_debug_level);
}
impl FromStr for crypt_device_type {
type Err = ();
fn from_str(s: &str) -> Result<crypt_device_type, ()> {
match s {
"PLAIN" => Ok(crypt_device_type::PLAIN),
"LUKS1" => Ok(crypt_device_type::LUKS1),
"LOOPAES" => Ok(crypt_device_type::LOOPAES),
"VERITY" => Ok(crypt_device_type::VERITY),
"TCRYPT" => Ok(crypt_device_type::TCRYPT),
_ => Err(()),
}
}
}
impl crypt_device_type {
pub fn to_str(&self) -> &str {
match self {
&crypt_device_type::PLAIN => "PLAIN",
&crypt_device_type::LUKS1 => "LUKS1",
&crypt_device_type::LOOPAES => "LOOPAES",
&crypt_device_type::VERITY => "VERITY",
&crypt_device_type::TCRYPT => "TCRYPT",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CString;
use std::str::FromStr;
#[test]
fn test_device_type_conversion() {
assert_eq!(
Ok(crypt_device_type::PLAIN),
crypt_device_type::from_str("PLAIN")
);
assert_eq!(
Ok(crypt_device_type::LUKS1),
crypt_device_type::from_str("LUKS1")
);
assert_eq!(
Ok(crypt_device_type::LOOPAES),
crypt_device_type::from_str("LOOPAES")
);
assert_eq!(
Ok(crypt_device_type::VERITY),
crypt_device_type::from_str("VERITY")
);
assert_eq!(
Ok(crypt_device_type::TCRYPT),
crypt_device_type::from_str("TCRYPT")
);
}
#[test]
fn test_keyslot_max_gt_zero() {
unsafe {
let luks_type = CString::new("LUKS1").unwrap();
assert!(crypt_keyslot_max(luks_type.as_ptr()) > 0);
}
}
}

View File

@@ -0,0 +1,2 @@
max_width = 120
format_strings = false

View File

@@ -0,0 +1,377 @@
//! High-level API to work with `libcryptsetup` supported devices (disks)
use std::fmt;
use std::fs::File;
use std::path::{Path, PathBuf};
use std::ptr;
use blkid_rs::{BlockDevice, LuksHeader};
use device;
pub use device::enable_debug;
use device::RawDevice;
pub use device::{Error, Keyslot, Result};
use raw;
use uuid;
pub type Luks1CryptDeviceHandle = CryptDeviceHandle<Luks1Params>;
/// Builder to open a crypt device at the specified path
///
/// # Examples
///
/// ```
/// use cryptsetup_rs::*;
/// # fn foo() -> Result<()> {
/// let device = open("/dev/loop0")?.luks1()?;
/// # Ok(())
/// # }
/// ```
pub fn open<P: AsRef<Path>>(path: P) -> Result<CryptDeviceOpenBuilder> {
let cd = device::init(path.as_ref())?;
Ok(CryptDeviceOpenBuilder {
path: path.as_ref().to_owned(),
cd,
})
}
/// Builder to format a crypt device at the specified path
///
/// # Examples
///
/// ```
/// # extern crate uuid;
/// # extern crate cryptsetup_rs;
/// use cryptsetup_rs::*;
/// use uuid::Uuid;
///
/// # fn foo() -> Result<()> {
/// let uuid = Uuid::new_v4();
/// let device = format("/dev/loop0")?
/// .rng_type(crypt_rng_type::CRYPT_RNG_URANDOM)
/// .iteration_time(5000)
/// .luks1("aes", "xts-plain", "sha256", 256, Some(&uuid))?;
/// # Ok(())
/// # }
/// ```
pub fn format<P: AsRef<Path>>(path: P) -> Result<CryptDeviceFormatBuilder> {
let cd = device::init(path.as_ref())?;
Ok(CryptDeviceFormatBuilder {
path: path.as_ref().to_owned(),
cd,
})
}
/// Read the UUID of a LUKS1 container without opening the device
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)
}
/// Struct containing state for the `open()` builder
pub struct CryptDeviceOpenBuilder {
path: PathBuf,
cd: RawDevice,
}
impl CryptDeviceOpenBuilder {
/// Loads an existing LUKS1 crypt device
pub fn luks1(self: CryptDeviceOpenBuilder) -> Result<CryptDeviceHandle<Luks1Params>> {
let _ = 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,
})
}
}
/// Struct containing state for the `format()` builder
pub struct CryptDeviceFormatBuilder {
path: PathBuf,
cd: RawDevice,
}
impl CryptDeviceFormatBuilder {
/// Set the iteration time for the `PBKDF2` function. Note that this does not affect the MK iterations.
pub fn iteration_time(mut self, iteration_time_ms: u64) -> Self {
device::set_iteration_time(&mut self.cd, iteration_time_ms);
self
}
/// Set the random number generator to use
pub fn rng_type(mut self, rng_type: raw::crypt_rng_type) -> Self {
device::set_rng_type(&mut self.cd, rng_type);
self
}
/// Formats a new block device as a LUKS1 crypt device with the specified parameters
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 _ = 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,
})
}
}
/// Trait representing common operations on a crypt device
pub trait CryptDevice {
/// Path the device was opened/created with
fn path(&self) -> &Path;
/// Name of cipher used
fn cipher(&self) -> &str;
/// Name of cipher mode used
fn cipher_mode(&self) -> &str;
/// Path to the underlying device (as reported by `libcryptsetup`)
fn device_name(&self) -> &str;
/// Random number generator used for operations on this crypt device
fn rng_type(&self) -> raw::crypt_rng_type;
/// Sets the random number generator to use
fn set_rng_type(&mut self, rng_type: raw::crypt_rng_type);
/// Sets the iteration time for the `PBKDF2` function. Note that this does not affect the MK iterations.
fn set_iteration_time(&mut self, iteration_time_ms: u64);
/// Volume key size (in bytes)
fn volume_key_size(&self) -> u8;
}
/// Trait for querying the device type at runtime
pub trait CryptDeviceType {
/// Type of the crypt device
fn device_type(&self) -> raw::crypt_device_type;
}
/// Trait representing specific operations on a LUKS1 device
pub trait Luks1CryptDevice {
/// Activate the crypt device, and give it the specified name
fn activate(&mut self, name: &str, key: &[u8]) -> Result<Keyslot>;
/// Add a new keyslot with the specified key
fn add_keyslot(
&mut self,
key: &[u8],
maybe_prev_key: Option<&[u8]>,
maybe_keyslot: Option<Keyslot>,
) -> Result<Keyslot>;
/// Replace an old key with a new one
fn update_keyslot(&mut self, key: &[u8], prev_key: &[u8], maybe_keyslot: Option<Keyslot>) -> Result<Keyslot>;
/// Destroy (and disable) key slot
fn destroy_keyslot(&mut self, slot: Keyslot) -> Result<()>;
/// Dump text-formatted information about the current device to stdout
fn dump(&self);
/// Get the hash algorithm used
fn hash_spec(&self) -> &str;
/// Get status of key slot
fn keyslot_status(&self, keyslot: Keyslot) -> raw::crypt_keyslot_info;
/// Number of bits in the master key
fn mk_bits(&self) -> u32;
/// Master key header digest
fn mk_digest(&self) -> &[u8; 20];
/// Master key `PBKDF2` iterations
fn mk_iterations(&self) -> u32;
/// Master key salt
fn mk_salt(&self) -> &[u8; 32];
/// Get the offset of the payload
fn payload_offset(&self) -> u32;
/// UUID of the current device
fn uuid(&self) -> uuid::Uuid;
}
/// An opaque handle on an initialized crypt device
#[derive(PartialEq)]
pub struct CryptDeviceHandle<P: fmt::Debug> {
/// Pointer to the raw device
cd: RawDevice,
/// Path to the crypt device (useful for diagnostics)
path: PathBuf,
/// Additional parameters depending on type of crypt device opened
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) {
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 {
device::cipher(&self.cd).expect("Initialised device should have cipher")
}
fn cipher_mode(&self) -> &str {
device::cipher_mode(&self.cd).expect("Initialised device should have cipher mode")
}
fn device_name(&self) -> &str {
device::device_name(&self.cd).expect("Initialised device should have an underlying path")
}
fn rng_type(&self) -> raw::crypt_rng_type {
device::rng_type(&self.cd)
}
fn set_rng_type(&mut self, rng_type: raw::crypt_rng_type) {
device::set_rng_type(&mut self.cd, rng_type)
}
fn set_iteration_time(&mut self, iteration_time_ms: u64) {
device::set_iteration_time(&mut self.cd, iteration_time_ms)
}
fn volume_key_size(&self) -> u8 {
device::volume_key_size(&self.cd)
}
}
/// Struct for storing LUKS1 parameters in memory
#[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> {
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> {
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> {
device::luks_update_keyslot(&mut self.cd, key, prev_key, maybe_keyslot)
}
fn destroy_keyslot(&mut self, slot: Keyslot) -> Result<()> {
device::luks_destroy_keyslot(&mut self.cd, slot)
}
fn dump(&self) {
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 {
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 {
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
}
}

View File

@@ -0,0 +1,319 @@
//! Low-level cryptsetup binding that sits directly on top of the `libcryptsetup` C API
//!
//! Consider using the high-level binding in the `api` module instead
use std::ffi;
use std::mem;
use std::path::Path;
use std::ptr;
use std::result;
use std::str;
use blkid_rs;
use errno;
use libc;
use raw;
use uuid;
/// Raw pointer to the underlying `crypt_device` opaque struct
pub type RawDevice = *mut raw::crypt_device;
#[derive(Debug)]
pub enum Error {
/// Error that originates from `libcryptsetup` (with numeric error code)
CryptsetupError(errno::Errno),
/// IO error
IOError(::std::io::Error),
/// Error from the blkid-rs library (while reading LUKS1 header)
BlkidError(blkid_rs::Error),
}
impl From<::std::io::Error> for Error {
fn from(e: ::std::io::Error) -> Self {
Error::IOError(e)
}
}
impl From<blkid_rs::Error> for Error {
fn from(e: blkid_rs::Error) -> Self {
Error::BlkidError(e)
}
}
pub type Result<T> = result::Result<T, Error>;
pub type Keyslot = u8;
const ANY_KEYSLOT: libc::c_int = -1 as libc::c_int;
fn str_from_c_str<'a>(c_str: *const libc::c_char) -> Option<&'a str> {
if c_str.is_null() {
None
} else {
unsafe { Some(ffi::CStr::from_ptr(c_str).to_str().unwrap()) }
}
}
macro_rules! crypt_error {
($res:expr) => {
Err(Error::CryptsetupError(errno::Errno(-$res)))
};
}
macro_rules! check_crypt_error {
($res:expr) => {
if $res != 0 {
crypt_error!($res)
} else {
Ok(())
}
};
}
/// Log function callback used by `libcryptsetup`
#[allow(unused)]
#[no_mangle]
pub extern "C" fn cryptsetup_rs_log_callback(
level: raw::crypt_log_level,
message: *const libc::c_char,
usrptr: *mut libc::c_void,
) {
let msg = str_from_c_str(message).unwrap();
match level {
raw::crypt_log_level::CRYPT_LOG_NORMAL => info!("{}", msg.trim_right()),
raw::crypt_log_level::CRYPT_LOG_ERROR => error!("{}", msg.trim_right()),
raw::crypt_log_level::CRYPT_LOG_VERBOSE => debug!("{}", msg.trim_right()),
raw::crypt_log_level::CRYPT_LOG_DEBUG => debug!("{}", msg.trim_right()),
}
}
/// Enable internal `libcryptsetup` debugging
pub fn enable_debug(debug: bool) {
if debug {
unsafe { raw::crypt_set_debug_level(raw::crypt_debug_level::CRYPT_DEBUG_ALL) };
} else {
unsafe { raw::crypt_set_debug_level(raw::crypt_debug_level::CRYPT_DEBUG_NONE) };
}
}
/// Initialise crypt device and check if provided device exists
pub fn init<P: AsRef<Path>>(path: P) -> Result<RawDevice> {
let mut cd = ptr::null_mut();
let c_path = ffi::CString::new(path.as_ref().to_str().unwrap()).unwrap();
let res = unsafe { raw::crypt_init(&mut cd as *mut *mut raw::crypt_device, c_path.as_ptr()) };
if res != 0 {
crypt_error!(res)
} else {
unsafe {
raw::crypt_set_log_callback(cd, Some(cryptsetup_rs_log_callback), ptr::null_mut());
}
Ok(cd)
}
}
/// Load crypt device parameters from the on-disk header
///
/// Note that typically you cannot query the crypt device for information before this function is
/// called.
pub fn load(cd: &RawDevice, requested_type: raw::crypt_device_type) -> Result<()> {
let c_type = ffi::CString::new(requested_type.to_str()).unwrap();
let res = unsafe { raw::crypt_load(*cd, c_type.as_ptr(), ptr::null_mut()) };
check_crypt_error!(res)
}
/// Get the cipher used by this crypt device
pub fn cipher<'a>(cd: &'a RawDevice) -> Option<&'a str> {
let c_cipher = unsafe { raw::crypt_get_cipher(*cd) };
str_from_c_str(c_cipher)
}
/// Get the cipher mode used by this crypt device
pub fn cipher_mode<'a>(cd: &'a RawDevice) -> Option<&'a str> {
let c_cipher_mode = unsafe { raw::crypt_get_cipher_mode(*cd) };
str_from_c_str(c_cipher_mode)
}
/// Get the path to the device (as `libcryptsetup` sees it)
pub fn device_name<'a>(cd: &'a RawDevice) -> Option<&'a str> {
let c_device_name = unsafe { raw::crypt_get_device_name(*cd) };
str_from_c_str(c_device_name)
}
/// Dump text-formatted information about this device to the console
pub fn dump(cd: &RawDevice) -> Result<()> {
let res = unsafe { raw::crypt_dump(*cd) };
check_crypt_error!(res)
}
/// Releases crypt device context and memory
pub fn free(cd: &mut RawDevice) {
unsafe { raw::crypt_free(*cd) }
}
/// Activate device based on provided key ("passphrase")
pub fn luks_activate(cd: &mut RawDevice, name: &str, key: &[u8]) -> Result<Keyslot> {
let c_name = ffi::CString::new(name).unwrap();
let c_passphrase_len = key.len() as libc::size_t;
// cast the passphrase to a pointer directly - it will not be NUL terminated but the passed length is used
let c_passphrase = key as *const [u8] as *const libc::c_char;
let res = unsafe {
raw::crypt_activate_by_passphrase(*cd, c_name.as_ptr(), ANY_KEYSLOT, c_passphrase, c_passphrase_len, 0u32)
};
if res < 0 {
crypt_error!(res)
} else {
Ok(res as u8)
}
}
/// Add key slot using provided passphrase. If there is no previous passphrase, use the volume key
/// that is in-memory to add the new key slot.
pub fn luks_add_keyslot(
cd: &mut RawDevice,
key: &[u8],
maybe_prev_key: Option<&[u8]>,
maybe_keyslot: Option<Keyslot>,
) -> Result<Keyslot> {
let c_key_len = key.len() as libc::size_t;
let c_key = key as *const [u8] as *const libc::c_char;;
let c_keyslot = maybe_keyslot
.map(|k| k as libc::c_int)
.unwrap_or(ANY_KEYSLOT as libc::c_int);
let res = if let Some(prev_key) = maybe_prev_key {
let c_prev_key_len = prev_key.len() as libc::size_t;
let c_prev_key = prev_key as *const [u8] as *const libc::c_char;;
unsafe { raw::crypt_keyslot_add_by_passphrase(*cd, c_keyslot, c_prev_key, c_prev_key_len, c_key, c_key_len) }
} else {
unsafe {
raw::crypt_keyslot_add_by_volume_key(*cd, c_keyslot, ptr::null(), 0 as libc::size_t, c_key, c_key_len)
}
};
if res < 0 {
crypt_error!(res)
} else {
Ok(res as Keyslot)
}
}
/// Add key slot using provided passphrase. If there is no previous passphrase, use the volume key
/// that is in-memory to add the new key slot.
pub fn luks_update_keyslot(
cd: &mut RawDevice,
key: &[u8],
prev_key: &[u8],
maybe_keyslot: Option<Keyslot>,
) -> Result<Keyslot> {
let c_key_len = key.len() as libc::size_t;
let c_key = key as *const [u8] as *const libc::c_char;;
let c_keyslot = maybe_keyslot
.map(|k| k as libc::c_int)
.unwrap_or(ANY_KEYSLOT as libc::c_int);
let c_prev_key_len = prev_key.len() as libc::size_t;
let c_prev_key = prev_key as *const [u8] as *const libc::c_char;;
let res = unsafe {
raw::crypt_keyslot_change_by_passphrase(*cd, c_keyslot, c_keyslot, c_prev_key, c_prev_key_len, c_key, c_key_len)
};
if res < 0 {
crypt_error!(res)
} else {
Ok(res as Keyslot)
}
}
/// Destroy (and disable) key slot
pub fn luks_destroy_keyslot(cd: &mut RawDevice, keyslot: Keyslot) -> Result<()> {
let res = unsafe { raw::crypt_keyslot_destroy(*cd, keyslot as libc::c_int) };
if res < 0 {
crypt_error!(res)
} else {
Ok(())
}
}
/// Format a new crypt device but do not activate it
///
/// Note this does not add an active keyslot
pub fn luks1_format(
cd: &mut RawDevice,
cipher: &str,
cipher_mode: &str,
hash: &str,
mk_bits: usize,
maybe_uuid: Option<&uuid::Uuid>,
) -> Result<()> {
let c_cipher = ffi::CString::new(cipher).unwrap();
let c_cipher_mode = ffi::CString::new(cipher_mode).unwrap();
let c_hash = ffi::CString::new(hash).unwrap();
let c_uuid = maybe_uuid.map(|uuid| ffi::CString::new(uuid.hyphenated().to_string()).unwrap());
let mut luks_params = raw::crypt_params_luks1 {
hash: c_hash.as_ptr(),
data_alignment: 0,
data_device: ptr::null(),
};
let c_luks_params: *mut raw::crypt_params_luks1 = &mut luks_params;
let c_luks_type = ffi::CString::new(raw::crypt_device_type::LUKS1.to_str()).unwrap();
let c_uuid_ptr = c_uuid.as_ref().map(|u| u.as_ptr()).unwrap_or(ptr::null());
let res = unsafe {
raw::crypt_format(
*cd,
c_luks_type.as_ptr(),
c_cipher.as_ptr(),
c_cipher_mode.as_ptr(),
c_uuid_ptr,
ptr::null(),
mk_bits / 8,
c_luks_params as *mut libc::c_void,
)
};
check_crypt_error!(res)
}
/// Get which RNG is used
pub fn rng_type(cd: &RawDevice) -> raw::crypt_rng_type {
unsafe {
let res = raw::crypt_get_rng_type(*cd);
mem::transmute(res)
}
}
/// Set the number of milliseconds for `PBKDF2` function iteration
pub fn set_iteration_time(cd: &mut RawDevice, iteration_time_ms: u64) {
unsafe {
raw::crypt_set_iteration_time(*cd, iteration_time_ms);
}
}
/// Set which RNG is used
pub fn set_rng_type(cd: &mut RawDevice, rng_type: raw::crypt_rng_type) {
unsafe { raw::crypt_set_rng_type(*cd, rng_type) }
}
/// Get information about a keyslot
pub fn keyslot_status(cd: &RawDevice, slot: Keyslot) -> raw::crypt_keyslot_info {
unsafe { raw::crypt_keyslot_status(*cd, slot as libc::c_int) }
}
/// Get size in bytes of the volume key
pub fn volume_key_size(cd: &RawDevice) -> u8 {
let res = unsafe { raw::crypt_get_volume_key_size(*cd) };
res as u8
}
/// Get device UUID
pub fn uuid<'a>(cd: &'a RawDevice) -> Option<uuid::Uuid> {
let c_uuid_str = unsafe { raw::crypt_get_uuid(*cd) };
str_from_c_str(c_uuid_str).and_then(|uuid_str| uuid::Uuid::parse_str(uuid_str).ok())
}

View File

@@ -0,0 +1,32 @@
//! Rust bindings to `libcryptsetup` - working with encrypted disks on Linux
//!
//! # Example
//!
//! See `api` module documentation for more.
//!
//! ```
//! use cryptsetup_rs::*;
//! # fn foo() -> Result<()> {
//! let device = open("/dev/loop0")?.luks1()?;
//! println!("Device UUID: {}", device.uuid());
//! println!("Device cipher: {}", device.cipher());
//! # Ok(())
//! # }
//! ```
#[warn(unused_must_use)]
extern crate blkid_rs;
extern crate errno;
extern crate libc;
extern crate libcryptsetup_sys as raw;
extern crate uuid;
#[macro_use]
extern crate log;
pub mod api;
pub mod device;
pub use api::{enable_debug, format, luks1_uuid, open};
pub use api::{CryptDevice, CryptDeviceType, Error, Keyslot, Luks1CryptDevice, Luks1CryptDeviceHandle, Result};
pub use raw::{crypt_device_type, crypt_keyslot_info, crypt_rng_type};

View File

@@ -0,0 +1,72 @@
#![deny(warnings)]
extern crate cryptsetup_rs;
extern crate env_logger;
extern crate log;
extern crate tempdir;
extern crate uuid;
#[macro_use]
extern crate expectest;
use std::process::Command;
use expectest::prelude::*;
use tempdir::TempDir;
use uuid::Uuid;
use cryptsetup_rs::*;
struct TestContext {
dir: TempDir,
name: String,
}
impl TestContext {
fn new(name: String) -> TestContext {
env_logger::init();
cryptsetup_rs::enable_debug(true);
let dir = tempdir::TempDir::new(&name).unwrap();
TestContext { name, dir }
}
fn new_crypt_device(&self) -> api::CryptDeviceFormatBuilder {
let crypt_file = self.dir.path().join(format!("{}.image", self.name));
let dd_status = Command::new("dd")
.arg("if=/dev/zero")
.arg(format!("of={}", crypt_file.display()))
.arg("bs=1M")
.arg("count=10")
.status()
.unwrap();
if !dd_status.success() {
panic!("Failed to create disk image at {}", crypt_file.display());
}
cryptsetup_rs::format(crypt_file).unwrap()
}
}
#[test]
fn test_create_new_luks1_cryptdevice_no_errors() {
let ctx = TestContext::new("new_luks1_cryptdevice".to_string());
let uuid = Uuid::new_v4();
let device_format = ctx.new_crypt_device()
.rng_type(crypt_rng_type::CRYPT_RNG_URANDOM)
.iteration_time(42);
let mut dev = device_format
.luks1("aes", "xts-plain", "sha256", 256, Some(&uuid))
.expect("LUKS format should succeed");
dev.dump();
expect!(dev.uuid()).to(be_equal_to(uuid));
expect!(dev.device_type()).to(be_equal_to(crypt_device_type::LUKS1));
expect!(dev.cipher()).to(be_equal_to("aes"));
expect!(dev.cipher_mode()).to(be_equal_to("xts-plain"));
expect!(dev.volume_key_size()).to(be_equal_to(32));
expect!(dev.add_keyslot(b"hello world", None, Some(3))).to(be_ok().value(3));
}