trackslash
VAULT-87 P2

Test restores end to end, against a corpus of old backups

0
Sub-issue of VAULT-83 P2 Test that lock, unlock, encryption and restore work end to end

Description

Part of VAULT-83.

Why

BackupRoundTripTests (4 tests) runs the whole path: export, encrypt, PDF, detach, decrypt, import. But it:

  • uses a random KeyData, not a backup password;
  • always restores into an in-memory SwiftData store;
  • never goes through the view models.

BackupImportFlowViewModelTests is all mocks. The repo has no backup made by an older build, and only the PDF step is tested with malformed input.

Tests to add

  • A corpus of old backups:
    • PDFs, plus QR payloads where the format differs, made by past builds.
    • Cover each format that still has to restore:
      • legacy v2 with a plaintext search passphrase;
      • from before QuickType and preview mode;
      • from before VAULT-75's padding;
      • with encrypted items;
      • an auto-backup.
    • Each has a known password and the contents it should restore to.
    • Build from the release tags (v2.0.0+100007 onward). For older formats, build from the last commit that wrote them.
    • Each must restore to exactly the expected items, tags, killphrases, search passphrases and lock states.
  • With a password, all the way through: backup password → Backup key derivation (cheap parameters) → BackupKeyDecryptorViewModel → BackupImportFlowViewModel → VaultDataModel.importMerge and importOverride.
  • Into an encrypted vault: restore into an EncryptedVaultStore that has an App Lock Password. After reopening from disk with the password, the imported items are there.
  • Into a duress vault: importing while a duress vault is open changes only that vault's slot. The other vault's items and its backup settings (VAULT-70) aren't touched.
  • QR codes, end to end:
    • Take the shards from DeviceTransferExportViewModel.
    • Feed them to BackupImportScanningHandler shuffled, with duplicates and a shard from another export mixed in.
    • Then decrypt and import.
  • Auto-backup: a saved auto-backup file, padded to its fixed size, restores through the import flow.
  • From another device: restore a backup made with a different killphrase key. Both sides use .zero today. Check that killphrases and search passphrases still work afterwards. If they don't, that's a bug: fix it or file a follow-up.
  • Malformed and hostile input:
    • Cover every step of the import: the PDF, the QR shards, the EncryptedVault JSON, decompression, and the payload JSON.
    • Truncated, oversized, reordered and inconsistent input must end in an error. It must never crash, and never use unbounded memory or time.
    • Add a small seeded fuzz test for each decoder, with a time limit.
    • Write the cases from the code. Anything that doesn't fail safely today follows VAULT-82's disclosure rule.

Done when

  • The corpus is in the repo, with a README giving each file's build, password and expected contents, and every file restores.
  • Each bullet above has a test.

GitHub

0

No branches or pull requests linked.

Comments

1
Bradley

Done in https://github.com/badbundle/vault-app/pull/699, merged as 03f8f062.

  • The corpus is in Vault/Tests/VaultFeedTests/Fixtures/Backups/, with a README giving each file's build, password and contents:
    • an auto-backup padded to 32 KiB (kept as the backup its PDF carries);
    • a randomly padded PDF;
    • a PDF from before the QuickType and preview choices;
    • a PDF with plain-text search passphrases (from before #519);
    • a device transfer's QR codes.
  • How it was made: between the release tags only the payload's fields and the padding changed, so each format was recorded with today's writer set to that format. The pre-#519 one was hand-built from the payload at that commit.
  • Restores: every restore goes through the password screen, into merge, override, an encrypted vault reopened from disk, and a duress vault. Only the duress vault's own slot changes.
  • Malformed input: each step (the PDF, the QR codes, the EncryptedVault JSON, decompression and the payload JSON) has malformed-input tests and a seeded fuzz test.
  • A fix these tests found: the bounded decompressor from #677 ignored anything after the payload's compressed stream. It now refuses it.
  • Another device: a backup made before #686 (keys travel with backups) restores its items with their digests as they were. Its killphrases and passphrases can't match under another device's keys, as the FAQ now says.