# PCH-02-005 — Document the legacy PBKDF2 import KDF + recommend re-export **Severity:** Informational **Target files:** `src/shared/backup.ts:146` (`derivePeachExportKey`), `src/popup/views/Import.tsx` (re-export notice) **Report:** peach-code-review.md, Finding PCH-02-005 ## Correction to the fix brief The brief's comment text says "100,000 iterations". The code (and the report) say **250,000** (`backup.ts:166`). The comment below uses the value the code actually enforces — a comment that misstates its own parameters is worse than no comment. ## Edit 1 — KDF comment at backup.ts:146 ```typescript // BEFORE (backup.ts:146-150) export async function derivePeachExportKey( password: string, salt: Uint8Array, opts: { raw?: boolean } = {} ): Promise { // AFTER /** * LEGACY IMPORT KDF (PCH-02-005): * This path derives with PBKDF2-SHA256 (250,000 iterations) for backwards * compatibility with `.json` exports sealed before the Argon2id migration — * the encrypted-JSON export UI was removed in v2.5.0 (see header), so this * KDF is import-only (plus the test fixture generator above). * * PBKDF2 has no memory-hardness and is materially weaker than the current * Argon2id envelope (m=64 MiB, t=3, p=4 — peachPayload.ts) against GPU/ASIC * attackers at equal wall-clock cost. Do NOT use this helper for any new * format. Users importing an old `.json` backup are prompted to export a * fresh backup afterwards (Import.tsx legacy-KDF notice). */ export async function derivePeachExportKey( password: string, salt: Uint8Array, opts: { raw?: boolean } = {} ): Promise { ``` (The existing LV-1 NFC comment inside the function body stays as is.) ## Edit 2 — post-import re-export notice in Import.tsx Verified: there is currently **no** user-facing recommendation after a successful PBKDF2 (`peach-json`) import. The import success path keeps the view mounted and already surfaces a post-import `duplicateWarning` notice (Import.tsx:790) — the legacy-KDF notice mirrors that exact mechanism. 2a. Add a signal next to `duplicateWarning` (Import.tsx:~425): ```typescript // AFTER the existing line: const [duplicateWarning, setDuplicateWarning] = createSignal(0); // ADD: // PCH-02-005: set when a legacy PBKDF2-encrypted `.json` backup imports // successfully — prompts the user to re-export under the Argon2id format. const [legacyKdfNotice, setLegacyKdfNotice] = createSignal(false); ``` 2b. In `handleImport`, alongside `setDuplicateWarning(dupes)` (the success tail, Import.tsx:~604): ```typescript // BEFORE setDuplicateWarning(dupes); await props.onImport(); // AFTER setDuplicateWarning(dupes); // PCH-02-005: "peach-json" is the legacy PBKDF2-SHA256 envelope; every // other recognised format is plaintext-source (no KDF) or current-format. setLegacyKdfNotice(currentPreview.format === "peach-json"); await props.onImport(); ``` Also reset it where the other notices reset (`handleFileSelect`, alongside `setError(null)` / `setAwaitingReplaceConfirm(false)`): ```typescript setLegacyKdfNotice(false); ``` 2c. Render next to the duplicate-warning block (Import.tsx:~790, same `` pattern and styling class as the sibling notice): ```tsx
This backup was created with Peach's older encryption format (PBKDF2). It imported fine — but we recommend exporting a fresh backup now so your paper/file backups use the current, stronger format.
``` (Class choice: `peach-restore-warning` is the existing soft-warning surface used three blocks above; no new CSS.) ## Tests UI-notice coverage belongs in `src/popup/views/contracts.test.tsx` style; the notice is a pure render-state change. Suggested single contract case (optional, not counted as a security regression test): mock `parseImportSource` to return `format: "peach-json"`, drive `handleImport`, assert the notice text renders. The KDF comment itself needs no test. ## Save message (integration) `fix: PCH-02-005 document legacy PBKDF2 import path + recommend re-export after legacy import`