Implement Apple Watch TOTP interface (#57)

This commit is contained in:
2026-08-16 14:38:16 +02:00
parent df1d49339c
commit e54d91a83a
6 changed files with 789 additions and 126 deletions

View File

@@ -4,8 +4,8 @@ use std::fs;
use ironstorage::{
mobile_watch::{
MobileWatchSnapshotState, WatchPersistenceAction, WatchRuntime, WatchSnapshotApply,
WatchSnapshotEntry, WatchSnapshotReceiver, WatchSnapshotSender,
MobileWatchSnapshotState, WatchPersistenceAction, WatchPresentationState, WatchRuntime,
WatchSnapshotApply, WatchSnapshotEntry, WatchSnapshotReceiver, WatchSnapshotSender,
},
otp::OtpAlgorithm,
repository::{EntryPath, SecretBytes},
@@ -184,6 +184,22 @@ fn watch_runtime_generates_view_ready_totp_and_clears_secrets_when_locked() -> T
)?;
let mut runtime = WatchRuntime::default();
assert_eq!(
runtime.presentation_at(59)?.state(),
WatchPresentationState::Syncing
);
runtime.sync_finished();
assert_eq!(
runtime.presentation_at(59)?.state(),
WatchPresentationState::Empty
);
runtime.sync_started();
runtime.sync_unavailable();
assert_eq!(
runtime.presentation_at(59)?.state(),
WatchPresentationState::Unavailable
);
runtime.sync_started();
let update = runtime.apply_snapshot(snapshot.snapshot().expose().to_vec())?;
assert_eq!(update.apply(), WatchSnapshotApply::Replaced);
assert_eq!(update.persistence(), WatchPersistenceAction::Replace);
@@ -196,9 +212,16 @@ fn watch_runtime_generates_view_ready_totp_and_clears_secrets_when_locked() -> T
assert_eq!(records[0].code().expose(), b"94287082");
assert_eq!(records[0].valid_until(), 60);
assert_eq!(records[0].remaining_at(59), 1);
let presentation = runtime.presentation_at(59)?;
assert_eq!(presentation.state(), WatchPresentationState::Ready);
assert_eq!(presentation.records()[0].remaining(), 1);
runtime.protected_data_unavailable();
assert!(runtime.records_at(59).is_err());
assert_eq!(
runtime.presentation_at(59)?.state(),
WatchPresentationState::Locked
);
let restored = runtime.apply_snapshot(snapshot.snapshot().expose().to_vec())?;
assert_eq!(restored.persistence(), WatchPersistenceAction::Replace);
@@ -209,5 +232,19 @@ fn watch_runtime_generates_view_ready_totp_and_clears_secrets_when_locked() -> T
assert_eq!(revoked.apply(), WatchSnapshotApply::Revoked);
assert_eq!(revoked.persistence(), WatchPersistenceAction::Delete);
assert!(runtime.records_at(59)?.is_empty());
assert_eq!(
runtime.presentation_at(59)?.state(),
WatchPresentationState::Empty
);
runtime.apply_snapshot(snapshot.snapshot().expose().to_vec())?;
assert_eq!(
runtime.presentation_at(59)?.state(),
WatchPresentationState::Stale
);
assert!(runtime.apply_snapshot(vec![0; 64]).is_err());
assert_eq!(
runtime.presentation_at(59)?.state(),
WatchPresentationState::Error
);
Ok(())
}