Implement Android TOTP and confirmed HOTP presentation (#107)
Android ARM verification / rust (push) Failing after 49m4s
Android ARM verification / android (push) Failing after 9s
Dependency security audit / rustsec (push) Successful in 21s

This commit is contained in:
hugo committed 2026-10-12 11:43:23 +02:00
1 parent d466b170ab
commit 6a864a8d7f
26 files changed
+1938 -94

No files matched your search

+103
View File
@@ -1247,6 +1247,61 @@ pub struct MobileTotpDetail {
pub watch: MobileWatchSnapshotStatus,
}
#[derive(Clone, Copy, uniffi::Enum)]
pub enum MobileOtpCodeKind {
Totp,
Hotp,
}
#[derive(Clone, uniffi::Record)]
pub struct MobileOtpCodePresentation {
pub document_revision: String,
pub kind: MobileOtpCodeKind,
pub issuer: Option<String>,
pub account: String,
pub code: Option<String>,
pub grouped_code: Option<String>,
pub period: Option<u64>,
pub valid_until: Option<u64>,
pub remaining_seconds: Option<u64>,
pub counter: Option<u64>,
pub next_counter: Option<u64>,
}
impl From<ironstorage::mobile_totp::MobileOtpCodePresentation> for MobileOtpCodePresentation {
fn from(value: ironstorage::mobile_totp::MobileOtpCodePresentation) -> Self {
let text = |bytes: ironstorage::repository::SecretBytes| {
String::from_utf8(bytes.expose().to_vec())
.expect("storage-generated OTP codes are ASCII")
};
Self {
document_revision: value.document_revision,
kind: match value.kind {
ironstorage::otp::OtpKind::Totp => MobileOtpCodeKind::Totp,
ironstorage::otp::OtpKind::Hotp => MobileOtpCodeKind::Hotp,
},
issuer: value.issuer,
account: value.account,
code: value.code.map(text),
grouped_code: value.grouped_code.map(text),
period: value.period,
valid_until: value.valid_until,
remaining_seconds: value.remaining_seconds,
counter: value.counter,
next_counter: value.next_counter,
}
}
}
#[uniffi::export]
pub fn mobile_otp_window_current(valid_until: u64, period: u64, unix_seconds: u64) -> bool {
ironstorage::otp::OtpCodeValidity::Timed {
valid_until,
period,
}
.is_current(unix_seconds)
}
impl From<StorageTotpDetail> for MobileTotpDetail {
fn from(detail: StorageTotpDetail) -> Self {
Self {
@@ -2010,6 +2065,44 @@ impl MobileAuthentication {
.map_err(Into::into)
}
pub fn presented_otp_code(
&self,
path: String,
revision: String,
unix_seconds: u64,
consumed_counter: Option<u64>,
) -> Result<Option<MobileOtpCodePresentation>, MobileAuthenticationFfiError> {
self.authentication
.presented_otp_code(&path, &revision, unix_seconds, consumed_counter)
.map(|value| value.map(Into::into))
.map_err(Into::into)
}
pub fn copy_presented_otp_code(
&self,
path: String,
revision: String,
unix_seconds: u64,
consumed_counter: Option<u64>,
) -> Result<MobileEntryCopy, MobileAuthenticationFfiError> {
self.authentication
.copy_presented_otp_code(&path, &revision, unix_seconds, consumed_counter)
.map(Into::into)
.map_err(Into::into)
}
pub fn advance_presented_hotp(
&self,
path: String,
revision: String,
confirmed: bool,
) -> Result<MobileOtpCodePresentation, MobileAuthenticationFfiError> {
self.authentication
.advance_presented_hotp(&path, &revision, confirmed)
.map(Into::into)
.map_err(Into::into)
}
pub fn prepare_presented_entry_mutation(
&self,
path: String,
@@ -2146,6 +2239,16 @@ impl MobileAuthentication {
.map_err(Into::into)
}
pub fn cached_totp_navigation(
&self,
) -> Result<Option<Vec<password_navigation::MobileNavigationRow>>, MobileAuthenticationFfiError>
{
self.authentication
.cached_totp_navigation()
.map(|rows| rows.map(|rows| rows.into_iter().map(Into::into).collect()))
.map_err(Into::into)
}
pub fn search_cached_totp_page(
&self,
query: String,