Implement biometric-protected GPG unlock
This commit is contained in:
@@ -8,6 +8,11 @@ use std::{error::Error, fmt, sync::Arc};
|
||||
use ironstorage::{
|
||||
config::ConfigError,
|
||||
mobile::{self, MobileShellState as StorageShellState, MobileTab as StorageTab},
|
||||
mobile_authentication::{
|
||||
MobileAuthenticationError as StorageAuthenticationError,
|
||||
MobileAuthenticationErrorKind as StorageAuthenticationErrorKind,
|
||||
MobileAuthenticationState as StorageAuthenticationState,
|
||||
},
|
||||
mobile_home::{
|
||||
self, MobileHomeChangeKind as StorageHomeChangeKind,
|
||||
MobileHomeChangeStatus as StorageHomeChangeStatus, MobileHomeError as StorageHomeError,
|
||||
@@ -491,6 +496,135 @@ impl From<StoragePasswordError> for MobilePasswordFfiError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, uniffi::Enum)]
|
||||
pub enum MobileAuthenticationErrorKind {
|
||||
PassphraseRequired,
|
||||
InvalidPassphrase,
|
||||
Cancelled,
|
||||
BiometryUnavailable,
|
||||
Configuration,
|
||||
KeyMaterial,
|
||||
Entry,
|
||||
SecureStorage,
|
||||
Expired,
|
||||
}
|
||||
|
||||
impl From<StorageAuthenticationErrorKind> for MobileAuthenticationErrorKind {
|
||||
fn from(kind: StorageAuthenticationErrorKind) -> Self {
|
||||
match kind {
|
||||
StorageAuthenticationErrorKind::PassphraseRequired => Self::PassphraseRequired,
|
||||
StorageAuthenticationErrorKind::InvalidPassphrase => Self::InvalidPassphrase,
|
||||
StorageAuthenticationErrorKind::Cancelled => Self::Cancelled,
|
||||
StorageAuthenticationErrorKind::BiometryUnavailable => Self::BiometryUnavailable,
|
||||
StorageAuthenticationErrorKind::Configuration => Self::Configuration,
|
||||
StorageAuthenticationErrorKind::KeyMaterial => Self::KeyMaterial,
|
||||
StorageAuthenticationErrorKind::Entry => Self::Entry,
|
||||
StorageAuthenticationErrorKind::SecureStorage => Self::SecureStorage,
|
||||
StorageAuthenticationErrorKind::Expired => Self::Expired,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, uniffi::Record)]
|
||||
pub struct MobileAuthenticationState {
|
||||
pub unlocked: bool,
|
||||
pub biometric_unlock_enabled: bool,
|
||||
pub remaining_seconds: u64,
|
||||
}
|
||||
|
||||
impl From<StorageAuthenticationState> for MobileAuthenticationState {
|
||||
fn from(state: StorageAuthenticationState) -> Self {
|
||||
Self {
|
||||
unlocked: state.unlocked(),
|
||||
biometric_unlock_enabled: state.biometric_unlock_enabled(),
|
||||
remaining_seconds: state.remaining_seconds(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, uniffi::Error)]
|
||||
pub enum MobileAuthenticationFfiError {
|
||||
Failed {
|
||||
kind: MobileAuthenticationErrorKind,
|
||||
title: String,
|
||||
detail: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Display for MobileAuthenticationFfiError {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Failed { title, detail, .. } => write!(formatter, "{title}: {detail}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for MobileAuthenticationFfiError {}
|
||||
|
||||
impl From<StorageAuthenticationError> for MobileAuthenticationFfiError {
|
||||
fn from(error: StorageAuthenticationError) -> Self {
|
||||
Self::Failed {
|
||||
kind: error.kind().into(),
|
||||
title: error.title().to_owned(),
|
||||
detail: error.detail().to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct MobileAuthentication {
|
||||
authentication: ironstorage::mobile_authentication::MobileAuthentication,
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl MobileAuthentication {
|
||||
pub fn state(&self) -> Result<MobileAuthenticationState, MobileAuthenticationFfiError> {
|
||||
self.authentication
|
||||
.state()
|
||||
.map(Into::into)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn unlock_entry(
|
||||
&self,
|
||||
path: String,
|
||||
passphrase: Option<String>,
|
||||
) -> Result<MobileAuthenticationState, MobileAuthenticationFfiError> {
|
||||
self.authentication
|
||||
.unlock_entry(
|
||||
&path,
|
||||
passphrase
|
||||
.map(|value| ironstorage::repository::SecretBytes::new(value.into_bytes())),
|
||||
)
|
||||
.map(Into::into)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn set_biometric_unlock(
|
||||
&self,
|
||||
enabled: bool,
|
||||
) -> Result<MobileAuthenticationState, MobileAuthenticationFfiError> {
|
||||
self.authentication
|
||||
.set_biometric_unlock(enabled)
|
||||
.map(Into::into)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn touch_user_activity(&self) -> Result<(), MobileAuthenticationFfiError> {
|
||||
self.authentication
|
||||
.touch_user_activity()
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn manual_lock(&self) -> Result<(), MobileAuthenticationFfiError> {
|
||||
self.authentication.manual_lock().map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn cancel(&self) -> Result<(), MobileAuthenticationFfiError> {
|
||||
self.authentication.cancel().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, uniffi::Enum)]
|
||||
pub enum MobileOnboardingPhase {
|
||||
Validating,
|
||||
@@ -711,6 +845,13 @@ pub fn mobile_password_page(
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
pub fn mobile_authentication() -> Result<Arc<MobileAuthentication>, MobileAuthenticationFfiError> {
|
||||
Ok(Arc::new(MobileAuthentication {
|
||||
authentication: ironstorage::mobile_authentication::MobileAuthentication::load()?,
|
||||
}))
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
pub fn mobile_onboarding_operation(
|
||||
server_url: String,
|
||||
|
||||
Reference in New Issue
Block a user