Compare commits

..

16 Commits
0.2.3 ... 0.2.7

10 changed files with 983 additions and 493 deletions

946
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "fido2luks"
version = "0.2.3"
version = "0.2.7"
authors = ["shimunn <shimun@shimun.net>"]
edition = "2018"
@@ -14,16 +14,13 @@ categories = ["command-line-utilities"]
license-file = "LICENSE"
[dependencies]
ctap_hmac = "0.2.1"
cryptsetup-rs = "0.2.1"
libcryptsetup-sys = "0.1.2"
ctap_hmac = { version="0.4.2", features = ["request_multiple"] }
hex = "0.3.2"
ring = "0.13.5"
failure = "0.1.5"
rpassword = "4.0.1"
structopt = "0.3.2"
libcryptsetup-rs = "0.2.0"
[profile.release]
lto = true

View File

@@ -9,7 +9,7 @@ Note: This has only been tested under Fedora 31 using a Solo Key, Trezor Model T
### Prerequisites
```
dnf install cargo cryptsetup-devel -y
dnf install clang cargo cryptsetup-devel -y
```
### Device

View File

@@ -1,85 +1,24 @@
use crate::error::*;
use crate::luks;
use crate::*;
use cryptsetup_rs as luks;
use cryptsetup_rs::api::{CryptDeviceHandle, CryptDeviceOpenBuilder, Luks1Params};
use cryptsetup_rs::{CryptDevice, Luks1CryptDevice};
use libcryptsetup_sys::crypt_keyslot_info;
use structopt::StructOpt;
use failure::_core::fmt::{Error, Formatter};
use ctap::{FidoCredential, FidoErrorKind};
use failure::_core::fmt::{Display, Error, Formatter};
use failure::_core::str::FromStr;
use failure::_core::time::Duration;
use std::io::Write;
use std::process::exit;
use std::thread;
use crate::util::read_password;
use std::time::SystemTime;
pub fn add_key_to_luks(
device: PathBuf,
secret: &[u8; 32],
old_secret: Box<dyn Fn() -> Fido2LuksResult<Vec<u8>>>,
exclusive: bool,
) -> Fido2LuksResult<u8> {
fn offer_format(
_dev: CryptDeviceOpenBuilder,
) -> Fido2LuksResult<CryptDeviceHandle<Luks1Params>> {
unimplemented!()
}
let dev =
|| -> luks::device::Result<CryptDeviceOpenBuilder> { luks::open(&device.canonicalize()?) };
let prev_key = old_secret()?;
let mut handle = match dev()?.luks1() {
Ok(handle) => handle,
Err(luks::device::Error::BlkidError(_)) => offer_format(dev()?)?,
Err(luks::device::Error::CryptsetupError(errno)) => {
//if i32::from(errno) == 555
dbg!(errno);
offer_format(dev()?)?
} //TODO: find correct errorno and offer to format as luks
err => err?,
};
handle.set_iteration_time(50);
let slot = handle.add_keyslot(secret, Some(prev_key.as_slice()), None)?;
if exclusive {
for old_slot in 0..8u8 {
if old_slot != slot
&& (handle.keyslot_status(old_slot.into()) == crypt_keyslot_info::CRYPT_SLOT_ACTIVE
|| handle.keyslot_status(old_slot.into())
== crypt_keyslot_info::CRYPT_SLOT_ACTIVE_LAST)
{
handle.destroy_keyslot(old_slot)?;
}
}
}
Ok(slot)
}
pub fn add_password_to_luks(
device: PathBuf,
secret: &[u8; 32],
new_secret: Box<dyn Fn() -> Fido2LuksResult<Vec<u8>>>,
add_password: bool,
) -> Fido2LuksResult<u8> {
let dev = luks::open(&device.canonicalize()?)?;
let mut handle = dev.luks1()?;
let prev_slot = if add_password {
Some(handle.add_keyslot(&secret[..], Some(&secret[..]), None)?)
} else {
None
};
let slot = handle.update_keyslot(&new_secret()?[..], &secret[..], prev_slot)?;
Ok(slot)
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct HexEncoded(Vec<u8>);
pub struct HexEncoded(pub Vec<u8>);
impl std::fmt::Display for HexEncoded {
impl Display for HexEncoded {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
f.write_str(&hex::encode(&self.0))
}
@@ -93,6 +32,30 @@ impl FromStr for HexEncoded {
}
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct CommaSeparated<T: FromStr + Display>(pub Vec<T>);
impl<T: Display + FromStr> Display for CommaSeparated<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
for i in &self.0 {
f.write_str(&i.to_string())?;
f.write_str(",")?;
}
Ok(())
}
}
impl<T: Display + FromStr> FromStr for CommaSeparated<T> {
type Err = <T as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(CommaSeparated(
s.split(',')
.map(|part| <T as FromStr>::from_str(part))
.collect::<Result<Vec<_>, _>>()?,
))
}
}
#[derive(Debug, StructOpt)]
pub struct Args {
/// Request passwords via Stdin instead of using the password helper
@@ -104,9 +67,9 @@ pub struct Args {
#[derive(Debug, StructOpt, Clone)]
pub struct SecretGeneration {
/// FIDO credential id, generate using fido2luks credential
/// FIDO credential ids, seperated by ',' generate using fido2luks credential
#[structopt(name = "credential-id", env = "FIDO2LUKS_CREDENTIAL_ID")]
pub credential_id: HexEncoded,
pub credential_ids: CommaSeparated<HexEncoded>,
/// Salt for secret generation, defaults to 'ask'
///
/// Options:{n}
@@ -136,25 +99,46 @@ pub struct SecretGeneration {
default_value = "15"
)]
pub await_authenticator: u64,
/// Request the password twice to ensure it being correct
#[structopt(
long = "verify-password",
env = "FIDO2LUKS_VERIFY_PASSWORD",
hidden = true
)]
pub verify_password: Option<bool>,
}
impl SecretGeneration {
pub fn patch(&self, args: &Args) -> Self {
pub fn patch(&self, args: &Args, verify_password: Option<bool>) -> Self {
let mut me = self.clone();
if args.interactive {
me.password_helper = PasswordHelper::Stdin;
}
me.verify_password = me.verify_password.or(verify_password);
me
}
pub fn obtain_secret(&self) -> Fido2LuksResult<[u8; 32]> {
let salt = self.salt.obtain(&self.password_helper)?;
pub fn obtain_secret(&self, password_query: &str) -> Fido2LuksResult<[u8; 32]> {
let mut salt = [0u8; 32];
match self.password_helper {
PasswordHelper::Stdin if !self.verify_password.unwrap_or(true) => {
salt.copy_from_slice(&util::sha256(&[&read_password(
password_query,
self.verify_password.unwrap_or(true),
)?
.as_bytes()[..]]));
}
_ => {
salt = self.salt.obtain(&self.password_helper)?;
}
}
let timeout = Duration::from_secs(self.await_authenticator);
let start = SystemTime::now();
while let Ok(el) = start.elapsed() {
if el > timeout {
Err(error::Fido2LuksError::NoAuthenticatorError)?;
return Err(error::Fido2LuksError::NoAuthenticatorError);
}
if get_devices()
.map(|devices| !devices.is_empty())
@@ -164,13 +148,60 @@ impl SecretGeneration {
}
thread::sleep(Duration::from_millis(500));
}
let credentials = &self
.credential_ids
.0
.iter()
.map(|HexEncoded(id)| FidoCredential {
id: id.to_vec(),
public_key: None,
})
.collect::<Vec<_>>();
let credentials = credentials.iter().collect::<Vec<_>>();
Ok(assemble_secret(
&perform_challenge(&self.credential_id.0, &salt)?,
&perform_challenge(&credentials[..], &salt, timeout - start.elapsed().unwrap())?,
&salt,
))
}
}
#[derive(Debug, StructOpt, Clone)]
pub struct LuksSettings {
/// Number of milliseconds required to derive the volume decryption key
/// Defaults to 10ms when using an authenticator or the default by cryptsetup when using a password
#[structopt(long = "kdf-time", name = "kdf-time")]
kdf_time: Option<u64>,
}
#[derive(Debug, StructOpt, Clone)]
pub struct OtherSecret {
/// Use a keyfile instead of a password
#[structopt(short = "d", long = "keyfile", conflicts_with = "fido_device")]
keyfile: Option<PathBuf>,
/// Use another fido device instead of a password
/// Note: this requires for the credential fot the other device to be passed as argument as well
#[structopt(short = "f", long = "fido-device", conflicts_with = "keyfile")]
fido_device: bool,
}
impl OtherSecret {
pub fn obtain(
&self,
secret_gen: &SecretGeneration,
verify_password: bool,
password_question: &str,
) -> Fido2LuksResult<Vec<u8>> {
match &self.keyfile {
Some(keyfile) => util::read_keyfile(keyfile.clone()),
None if self.fido_device => {
Ok(Vec::from(&secret_gen.obtain_secret(password_question)?[..]))
}
None => util::read_password(password_question, verify_password)
.map(|p| p.as_bytes().to_vec()),
}
}
}
#[derive(Debug, StructOpt)]
pub enum Command {
#[structopt(name = "print-secret")]
@@ -189,11 +220,12 @@ pub enum Command {
/// Will wipe all other keys
#[structopt(short = "e", long = "exclusive")]
exclusive: bool,
/// Use a keyfile instead of typing a previous password
#[structopt(short = "d", long = "keyfile")]
keyfile: Option<PathBuf>,
#[structopt(flatten)]
existing_secret: OtherSecret,
#[structopt(flatten)]
secret_gen: SecretGeneration,
#[structopt(flatten)]
luks_settings: LuksSettings,
},
/// Replace a previously added key with a password
#[structopt(name = "replace-key")]
@@ -203,11 +235,12 @@ pub enum Command {
/// Add the password and keep the key
#[structopt(short = "a", long = "add-password")]
add_password: bool,
/// Use a keyfile instead of typing a previous password
#[structopt(short = "d", long = "keyfile")]
keyfile: Option<PathBuf>,
#[structopt(flatten)]
replacement: OtherSecret,
#[structopt(flatten)]
secret_gen: SecretGeneration,
#[structopt(flatten)]
luks_settings: LuksSettings,
},
/// Open the LUKS device
#[structopt(name = "open")]
@@ -250,59 +283,64 @@ pub fn run_cli() -> Fido2LuksResult<()> {
binary,
ref secret_gen,
} => {
let secret = secret_gen.patch(&args).obtain_secret()?;
let secret = secret_gen
.patch(&args, Some(false))
.obtain_secret("Password")?;
if *binary {
stdout.write(&secret[..])?;
stdout.write_all(&secret[..])?;
} else {
stdout.write(hex::encode(&secret[..]).as_bytes())?;
stdout.write_all(hex::encode(&secret[..]).as_bytes())?;
}
Ok(stdout.flush()?)
}
Command::AddKey {
device,
exclusive,
keyfile,
existing_secret,
ref secret_gen,
luks_settings,
} => {
let secret = secret_gen.patch(&args).obtain_secret()?;
let slot = add_key_to_luks(
let secret_gen = secret_gen.patch(&args, None);
let old_secret = existing_secret.obtain(&secret_gen, false, "Existing password")?;
let secret = secret_gen.obtain_secret("Password")?;
let added_slot = luks::add_key(
device.clone(),
&secret,
if let Some(keyfile) = keyfile.clone() {
Box::new(move || util::read_keyfile(keyfile.clone()))
} else {
Box::new(|| {
util::read_password("Old password", true).map(|p| p.as_bytes().to_vec())
})
},
*exclusive,
&old_secret[..],
luks_settings.kdf_time.or(Some(10)),
)?;
println!(
"Added to key to device {}, slot: {}",
device.display(),
slot
);
if *exclusive {
let destroyed = luks::remove_keyslots(&device, &[added_slot])?;
println!(
"Added to key to device {}, slot: {}\nRemoved {} old keys",
device.display(),
added_slot,
destroyed
);
} else {
println!(
"Added to key to device {}, slot: {}",
device.display(),
added_slot
);
}
Ok(())
}
Command::ReplaceKey {
device,
add_password,
keyfile,
replacement,
ref secret_gen,
luks_settings,
} => {
let secret = secret_gen.patch(&args).obtain_secret()?;
let slot = add_password_to_luks(
device.clone(),
&secret,
if let Some(keyfile) = keyfile.clone() {
Box::new(move || util::read_keyfile(keyfile.clone()))
} else {
Box::new(|| {
util::read_password("Password to add", true).map(|p| p.as_bytes().to_vec())
})
},
*add_password,
)?;
let secret_gen = secret_gen.patch(&args, Some(false));
let secret = secret_gen.obtain_secret("Password")?;
let new_secret = replacement.obtain(&secret_gen, true, "Replacement password")?;
let slot = if *add_password {
luks::add_key(device, &new_secret[..], &secret, luks_settings.kdf_time)
} else {
luks::replace_key(device, &new_secret[..], &secret, luks_settings.kdf_time)
}?;
println!(
"Added to password to device {}, slot: {}",
device.display(),
@@ -318,16 +356,22 @@ pub fn run_cli() -> Fido2LuksResult<()> {
} => {
let mut retries = *retries;
loop {
let secret = secret_gen.patch(&args).obtain_secret()?;
match open_container(&device, &name, &secret) {
Err(e) => match e {
Fido2LuksError::WrongSecret if retries > 0 => {
retries -= 1;
eprintln!("{}", e);
continue;
match secret_gen
.patch(&args, Some(false))
.obtain_secret("Password")
.and_then(|secret| luks::open_container(&device, &name, &secret))
{
Err(e) => {
match e {
Fido2LuksError::WrongSecret if retries > 0 => {}
Fido2LuksError::AuthenticatorError { ref cause }
if cause.kind() == FidoErrorKind::Timeout && retries > 0 => {}
e => return Err(e),
}
e => Err(e)?,
},
retries -= 1;
eprintln!("{}", e);
}
res => break res,
}
}

View File

@@ -24,7 +24,7 @@ impl Default for InputSalt {
impl From<&str> for InputSalt {
fn from(s: &str) -> Self {
let mut parts = s.split(":").into_iter();
let mut parts = s.split(':');
match parts.next() {
Some("ask") | Some("Ask") => InputSalt::AskPassword,
Some("file") => InputSalt::File {
@@ -87,6 +87,7 @@ impl InputSalt {
#[derive(Debug, Clone)]
pub enum PasswordHelper {
Script(String),
#[allow(dead_code)]
Systemd,
Stdin,
}
@@ -134,7 +135,7 @@ impl PasswordHelper {
Systemd => unimplemented!(),
Stdin => Ok(util::read_password("Password", true)?),
Script(password_helper) => {
let mut helper_parts = password_helper.split(" ");
let mut helper_parts = password_helper.split(' ');
let password = Command::new((&mut helper_parts).next().unwrap())
.args(helper_parts)

View File

@@ -1,91 +1,49 @@
use crate::error::*;
use crate::util::sha256;
use crate::util;
use ctap::{
self,
extensions::hmac::{FidoHmacCredential, HmacExtension},
AuthenticatorOptions, FidoDevice, FidoError, FidoErrorKind, PublicKeyCredentialRpEntity,
PublicKeyCredentialUserEntity,
self, extensions::hmac::HmacExtension, request_multiple_devices, FidoAssertionRequestBuilder,
FidoCredential, FidoCredentialRequestBuilder, FidoDevice, FidoError, FidoErrorKind,
};
use std::time::Duration;
const RP_ID: &'static str = "fido2luks";
const RP_ID: &str = "fido2luks";
fn authenticator_options() -> Option<AuthenticatorOptions> {
Some(AuthenticatorOptions {
uv: false, //TODO: should get this from config
rk: true,
})
}
fn authenticator_rp() -> PublicKeyCredentialRpEntity<'static> {
PublicKeyCredentialRpEntity {
id: RP_ID,
name: None,
icon: None,
pub fn make_credential_id(name: Option<&str>) -> Fido2LuksResult<FidoCredential> {
let mut request = FidoCredentialRequestBuilder::default().rp_id(RP_ID);
if let Some(user_name) = name {
request = request.user_name(user_name);
}
let request = request.build().unwrap();
let make_credential = |device: &mut FidoDevice| device.make_hmac_credential(&request);
Ok(request_multiple_devices(
get_devices()?
.iter_mut()
.map(|device| (device, &make_credential)),
None,
)?)
}
fn authenticator_user(name: Option<&str>) -> PublicKeyCredentialUserEntity {
PublicKeyCredentialUserEntity {
id: &[0u8],
name: name.unwrap_or(""),
icon: None,
display_name: name,
}
}
pub fn make_credential_id(name: Option<&str>) -> Fido2LuksResult<FidoHmacCredential> {
let mut errs = Vec::new();
match get_devices()? {
ref devs if devs.is_empty() => Err(Fido2LuksError::NoAuthenticatorError)?,
devs => {
for mut dev in devs.into_iter() {
match dev
.make_hmac_credential_full(
authenticator_rp(),
authenticator_user(name),
&[0u8; 32],
&[],
authenticator_options(),
)
.map(|cred| cred.into())
{
//TODO: make credentials device specific
Ok(cred) => {
return Ok(cred);
}
Err(e) => {
errs.push(e);
}
}
}
}
}
Err(errs.pop().ok_or(Fido2LuksError::NoAuthenticatorError)?)?
}
pub fn perform_challenge(credential_id: &[u8], salt: &[u8; 32]) -> Fido2LuksResult<[u8; 32]> {
let cred = FidoHmacCredential {
id: credential_id.to_vec(),
rp_id: RP_ID.to_string(),
pub fn perform_challenge(
credentials: &[&FidoCredential],
salt: &[u8; 32],
timeout: Duration,
) -> Fido2LuksResult<[u8; 32]> {
let request = FidoAssertionRequestBuilder::default()
.rp_id(RP_ID)
.credentials(credentials)
.build()
.unwrap();
let get_assertion = |device: &mut FidoDevice| {
device.get_hmac_assertion(&request, &util::sha256(&[&salt[..]]), None)
};
let mut errs = Vec::new();
match get_devices()? {
ref devs if devs.is_empty() => Err(Fido2LuksError::NoAuthenticatorError)?,
devs => {
for mut dev in devs.into_iter() {
match dev.get_hmac_assertion(&cred, &sha256(&[&salt[..]]), None, None) {
Ok(secret) => {
return Ok(secret.0);
}
Err(e) => {
errs.push(e);
}
}
}
}
}
Err(errs.pop().ok_or(Fido2LuksError::NoAuthenticatorError)?)?
let (_, (secret, _)) = request_multiple_devices(
get_devices()?
.iter_mut()
.map(|device| (device, &get_assertion)),
Some(timeout),
)?;
Ok(secret)
}
pub fn get_devices() -> Fido2LuksResult<Vec<FidoDevice>> {
@@ -94,7 +52,7 @@ pub fn get_devices() -> Fido2LuksResult<Vec<FidoDevice>> {
match FidoDevice::new(&di) {
Err(e) => match e.kind() {
FidoErrorKind::ParseCtap | FidoErrorKind::DeviceUnsupported => (),
err => Err(FidoError::from(err))?,
err => return Err(FidoError::from(err).into()),
},
Ok(dev) => devices.push(dev),
}

View File

@@ -14,7 +14,9 @@ pub enum Fido2LuksError {
#[fail(display = "no authenticator found, please ensure your device is plugged in")]
NoAuthenticatorError,
#[fail(display = "luks err")]
LuksError { cause: cryptsetup_rs::device::Error },
LuksError {
cause: libcryptsetup_rs::LibcryptErr,
},
#[fail(display = "no authenticator found, please ensure your device is plugged in")]
IoError { cause: io::Error },
#[fail(display = "supplied secret isn't valid for this device")]
@@ -44,6 +46,7 @@ pub enum AskPassError {
Mismatch,
}
use libcryptsetup_rs::LibcryptErr;
use std::string::FromUtf8Error;
use Fido2LuksError::*;
@@ -53,17 +56,16 @@ impl From<FidoError> for Fido2LuksError {
}
}
impl From<cryptsetup_rs::device::Error> for Fido2LuksError {
fn from(e: cryptsetup_rs::device::Error) -> Self {
impl From<LibcryptErr> for Fido2LuksError {
fn from(e: LibcryptErr) -> Self {
match e {
cryptsetup_rs::device::Error::CryptsetupError(error_no) if error_no.0 == 1i32 => {
LibcryptErr::IOError(e) if e.raw_os_error().iter().any(|code| code == &1i32) => {
WrongSecret
}
e => LuksError { cause: e },
_ => LuksError { cause: e },
}
}
}
impl From<io::Error> for Fido2LuksError {
fn from(e: io::Error) -> Self {
IoError { cause: e }

82
src/luks.rs Normal file
View File

@@ -0,0 +1,82 @@
use crate::error::*;
use libcryptsetup_rs::{CryptActivateFlags, CryptDevice, CryptInit, EncryptionFormat, KeyslotInfo};
use std::path::Path;
fn load_device_handle<P: AsRef<Path>>(path: P) -> Fido2LuksResult<CryptDevice> {
let mut device = CryptInit::init(path.as_ref())?;
//TODO: determine luks version some way other way than just trying
let mut load = |format| device.context_handle().load::<()>(format, None).map(|_| ());
vec![EncryptionFormat::Luks2, EncryptionFormat::Luks1]
.into_iter()
.fold(None, |res, format| match res {
Some(Ok(())) => res,
Some(e) => Some(e.or_else(|_| load(format))),
None => Some(load(format)),
})
.unwrap()?;
Ok(device)
}
pub fn open_container<P: AsRef<Path>>(path: P, name: &str, secret: &[u8]) -> Fido2LuksResult<()> {
let mut device = load_device_handle(path)?;
device
.activate_handle()
.activate_by_passphrase(Some(name), None, secret, CryptActivateFlags::empty())
.map(|_slot| ())
.map_err(|_e| Fido2LuksError::WrongSecret)
}
pub fn add_key<P: AsRef<Path>>(
path: P,
secret: &[u8],
old_secret: &[u8],
iteration_time: Option<u64>,
) -> Fido2LuksResult<u32> {
let mut device = load_device_handle(path)?;
if let Some(millis) = iteration_time {
device.settings_handle().set_iteration_time(millis)
}
let slot = device
.keyslot_handle(None)
.add_by_passphrase(old_secret, secret)?;
Ok(slot)
}
pub fn remove_keyslots<P: AsRef<Path>>(path: P, exclude: &[u32]) -> Fido2LuksResult<u32> {
let mut device = load_device_handle(path)?;
let mut handle;
let mut destroyed = 0;
//TODO: detect how many keyslots there are instead of trying within a given range
for slot in 0..1024 {
handle = device.keyslot_handle(Some(slot));
match handle.status()? {
KeyslotInfo::Inactive => continue,
KeyslotInfo::Active if !exclude.contains(&slot) => {
handle.destroy()?;
destroyed += 1;
}
_ => (),
}
if let KeyslotInfo::ActiveLast = handle.status()? {
break;
}
}
Ok(destroyed)
}
pub fn replace_key<P: AsRef<Path>>(
path: P,
secret: &[u8],
old_secret: &[u8],
iteration_time: Option<u64>,
) -> Fido2LuksResult<u32> {
let mut device = load_device_handle(path)?;
// Set iteration time not sure wether this applies to luks2 as well
if let Some(millis) = iteration_time {
device.settings_handle().set_iteration_time(millis)
}
Ok(device
.keyslot_handle(None)
.change_by_passphrase(None, None, old_secret, secret)? as u32)
}

View File

@@ -5,9 +5,6 @@ use crate::cli::*;
use crate::config::*;
use crate::device::*;
use crate::error::*;
use cryptsetup_rs as luks;
use cryptsetup_rs::Luks1CryptDevice;
use std::io;
use std::path::PathBuf;
use std::process::exit;
@@ -16,14 +13,9 @@ mod cli;
mod config;
mod device;
mod error;
mod luks;
mod util;
fn open_container(device: &PathBuf, name: &str, secret: &[u8; 32]) -> Fido2LuksResult<()> {
let mut handle = luks::open(device.canonicalize()?)?.luks1()?;
let _slot = handle.activate(name, &secret[..])?;
Ok(())
}
fn assemble_secret(hmac_result: &[u8], salt: &[u8]) -> [u8; 32] {
util::sha256(&[salt, hmac_result])
}

View File

@@ -4,7 +4,7 @@ use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
pub fn sha256<'a>(messages: &[&[u8]]) -> [u8; 32] {
pub fn sha256(messages: &[&[u8]]) -> [u8; 32] {
let mut digest = digest::Context::new(&digest::SHA256);
for m in messages.iter() {
digest.update(m);
@@ -23,7 +23,7 @@ pub fn read_password(q: &str, verify: bool) -> Fido2LuksResult<String> {
{
Err(Fido2LuksError::AskPassError {
cause: AskPassError::Mismatch,
})?
})
}
pass => Ok(pass),
}