Skip to content

Secure Restore

Elman Mohammadi edited this page Oct 6, 2026 · 1 revision

Secure Restore

Restore is treated as a security-critical operation. The V14 reader is intentionally designed so that package validation occurs before plaintext output is allowed to persist.

Restore sequence

The effective order is:

1. Validate package structure
2. Validate header
3. Unlock package root key
4. Derive subkeys
5. Authenticate/decrypt manifest
6. Validate manifest schema and hierarchy references
7. Validate completion record
8. Inventory ciphertext blobs
9. Verify each referenced blob hash
10. Reject unreachable blobs
11. Recompute Merkle root
12. Confirm destination does not already exist
13. Decrypt and validate every restored path
14. Create directories in depth order
15. Restore files atomically
16. Issue a short-lived restore token

The important rule is that steps 1–11 happen before the restore tree is populated.

Destination policy

The root restore destination must not already exist.

This avoids an entire class of problems where an archive is restored into a directory that already contains attacker-controlled or user-controlled files.

If the destination exists, restore fails.

The same rule is applied to individual output paths: existing files, directories, and symbolic links are rejected.

Preflight path walk

The manifest stores a parent/child graph rather than storing one plaintext full path per record.

Before creating output, the reader walks parent links until reaching the root.

Each decrypted name is checked for:

  • empty names
  • . and ..
  • invalid UTF-8
  • null bytes
  • /
  • \
  • control characters
  • :
  • trailing spaces
  • trailing dots
  • Windows device names such as CON, PRN, AUX, NUL, COM1, and LPT1
  • excessive length

Case-folded collision detection

The reader normalizes path comparisons using lowercased UTF-8 values when possible.

Two paths that differ only by case therefore cannot coexist in the restored tree.

This matters because filesystems do not all treat filename case the same way.

File/directory conflicts

V14 rejects structures such as:

report
report/data.txt

when report is itself a file.

It also rejects duplicate normalized paths.

These checks happen before child writes.

Directory creation ordering

Directories are sorted by path depth and created before file restoration.

This avoids depending on manifest ordering for parent availability.

Atomic file restore

Individual files are restored to a temporary path first:

<target>.partial.<random>

Only after successful authenticated decryption and length validation does the implementation rename the temporary file into its final path.

On failure, the temporary output is removed.

Exact plaintext size

The file node records the expected plaintext size.

During secretstream decryption, the reader tracks the number of plaintext bytes written and rejects:

  • data larger than the expected size;
  • a missing final record;
  • a wrong final length;
  • trailing bytes after the final record.

This prevents a structurally valid stream from producing an ambiguous output size.

Failure cleanup

If any restore step fails after the destination directory has been created, the reader removes the restore directory recursively.

The goal is a binary outcome:

success -> complete restore
failure -> no usable partial restore tree

Temporary download handling

The server performs restoration into a temporary directory and then creates a short-lived restore token.

The token maps to the temporary restore directory. It is stored separately from the package and is deleted when consumed or expired.

The downloadable restored content is then produced as a ZIP archive by the restore download handler.

What this does not protect against

Restore safety does not make an already compromised server trustworthy. A process with sufficient host-level privileges can potentially modify files, intercept secrets, or change runtime behavior.

The restore boundary primarily protects against untrusted package content and unsafe filesystem effects caused by malformed or malicious packages.

Clone this wiki locally