Build secure Rust watchOS TOTP core (#56)
This commit is contained in:
@@ -4,8 +4,8 @@ use std::fs;
|
||||
|
||||
use ironstorage::{
|
||||
mobile_watch::{
|
||||
MobileWatchSnapshotState, WatchSnapshotApply, WatchSnapshotEntry, WatchSnapshotReceiver,
|
||||
WatchSnapshotSender,
|
||||
MobileWatchSnapshotState, WatchPersistenceAction, WatchRuntime, WatchSnapshotApply,
|
||||
WatchSnapshotEntry, WatchSnapshotReceiver, WatchSnapshotSender,
|
||||
},
|
||||
otp::OtpAlgorithm,
|
||||
repository::{EntryPath, SecretBytes},
|
||||
@@ -15,7 +15,7 @@ type TestResult = Result<(), Box<dyn std::error::Error>>;
|
||||
|
||||
fn entry(path: &str, issuer: &str, account: &str, secret: &[u8]) -> WatchSnapshotEntry {
|
||||
WatchSnapshotEntry::new(
|
||||
EntryPath::parse(path).expect("fixture path"),
|
||||
EntryPath::parse(path).expect("fixture path").to_string(),
|
||||
Some(issuer.to_owned()),
|
||||
account.to_owned(),
|
||||
OtpAlgorithm::Sha256,
|
||||
@@ -165,3 +165,49 @@ fn journal_keeps_revisions_monotonic_across_sender_reloads() -> TestResult {
|
||||
assert_eq!(changed.revision(), 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn watch_runtime_generates_view_ready_totp_and_clears_secrets_when_locked() -> TestResult {
|
||||
let directory = tempfile::tempdir()?;
|
||||
let mut sender = WatchSnapshotSender::load(directory.path().join("watch-snapshot.toml"));
|
||||
let snapshot = sender.prepare(
|
||||
"paired-watch",
|
||||
vec![WatchSnapshotEntry::new(
|
||||
"otp/alice".to_owned(),
|
||||
Some("Acme".to_owned()),
|
||||
"alice".to_owned(),
|
||||
OtpAlgorithm::Sha1,
|
||||
8,
|
||||
30,
|
||||
SecretBytes::new(b"12345678901234567890".to_vec()),
|
||||
)],
|
||||
)?;
|
||||
|
||||
let mut runtime = WatchRuntime::default();
|
||||
let update = runtime.apply_snapshot(snapshot.snapshot().expose().to_vec())?;
|
||||
assert_eq!(update.apply(), WatchSnapshotApply::Replaced);
|
||||
assert_eq!(update.persistence(), WatchPersistenceAction::Replace);
|
||||
assert_eq!(update.selected_entries(), 1);
|
||||
assert!(!update.receipt().is_empty());
|
||||
|
||||
let records = runtime.records_at(59)?;
|
||||
assert_eq!(records.len(), 1);
|
||||
assert_eq!(records[0].path(), "otp/alice");
|
||||
assert_eq!(records[0].code().expose(), b"94287082");
|
||||
assert_eq!(records[0].valid_until(), 60);
|
||||
assert_eq!(records[0].remaining_at(59), 1);
|
||||
|
||||
runtime.protected_data_unavailable();
|
||||
assert!(runtime.records_at(59).is_err());
|
||||
|
||||
let restored = runtime.apply_snapshot(snapshot.snapshot().expose().to_vec())?;
|
||||
assert_eq!(restored.persistence(), WatchPersistenceAction::Replace);
|
||||
assert_eq!(runtime.records_at(59)?[0].code().expose(), b"94287082");
|
||||
|
||||
let revocation = sender.prepare("paired-watch", Vec::new())?;
|
||||
let revoked = runtime.apply_snapshot(revocation.snapshot().expose().to_vec())?;
|
||||
assert_eq!(revoked.apply(), WatchSnapshotApply::Revoked);
|
||||
assert_eq!(revoked.persistence(), WatchPersistenceAction::Delete);
|
||||
assert!(runtime.records_at(59)?.is_empty());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user