Skip to content

fix(vscode,pi): correct OSCrypt safeStorage encryption and allow custom deployment IDs for Pi - #14

Closed
shzdehmd wants to merge 2 commits into
fw-ai:mainfrom
shzdehmd:fix/safestorage-and-pi-custom-deployments
Closed

fix(vscode,pi): correct OSCrypt safeStorage encryption and allow custom deployment IDs for Pi#14
shzdehmd wants to merge 2 commits into
fw-ai:mainfrom
shzdehmd:fix/safestorage-and-pi-custom-deployments

Conversation

@shzdehmd

Copy link
Copy Markdown

Summary

Two bug fixes in this PR:

1. VS Code safeStorage encryption (Linux + Windows)

The VS Code harness writes the Fireworks API key as an Electron safeStorage-encrypted blob into state.vscdb. The OSCrypt parameters were wrong on Linux and Windows, so VS Code's safeStorage.decryptString could not decrypt the ciphertext — it silently deleted the secret and the API key was lost on every restart.

Linux: The v10/v11 version prefixes were swapped between the keyring and basic_text branches, and the PBKDF2 iteration count was 1003 (macOS-only) instead of 1. Chromium's os_crypt/async/common/algorithm.mojom confirms kAES128CBC is the "v11 os_crypt_sync encryption on Linux" with 1 iteration.

Windows: The code used raw DPAPI on the whole plaintext (no prefix), but modern Chromium uses v10 + AES-256-GCM with a DPAPI-protected 32-byte key stored in VS Code's Local State file (os_crypt.encrypted_key). Additionally, the DPAPI round-trip corrupted the 32-byte key because windowsUnprotect converted raw bytes through a UTF-8 string (45 bytes instead of 32).

macOS: Unaffected — v10 + AES-128-CBC + 1003 iterations + keychain master password was already correct.

Changes (vscode-safestorage.mjs):

  • Made PBKDF2 iterations platform-aware (1 for Linux, 1003 for macOS)
  • Swapped Linux prefixes: v11 + keyring password (was v10), v10 + "peanuts" basic_text (was v11)
  • Replaced raw-DPAPI-on-plaintext with v10 + AES-256-GCM (v10(3) + nonce(12) + ciphertext + tag(16), tag at end)
  • Added loadWindowsOsCryptKey to read the DPAPI-protected 32-byte key from Local State (os_crypt.encrypted_key → strip 5-byte DPAPI prefix → DPAPI-unprotect)
  • Added buffer-based windowsProtectBuffer/windowsUnprotectBuffer that preserve binary data (no UTF-8 conversion)
  • Added App-Bound Encryption sentinel: fails clearly if the Local State key prefix is not DPAPI
  • Threaded localStatePath through encryptSecret/decryptSecret/isSecretEncryptionAvailable and all call sites in vscode-core.mjs and vscode-firerouter-core.mjs

Verification: Ground-truth probe on Windows (Electron 42) confirmed:

  • fireconnect encryptSecret output decrypts correctly with Electron's real safeStorage.decryptString
  • Electron safeStorage.encryptString output decrypts correctly with fireconnect's decryptSecret
  • A blob written by fireconnect vscode on into the real state.vscdb decrypts correctly with VS Code's own Local State key
  • fireconnect vscode status reads the key back (Key present: yes)

Parameters confirmed against Chromium's os_crypt/async/common/algorithm.mojom and encryptor.cc.

2. Pi custom deployment model IDs

Custom deployment IDs (accounts/<user>/deployments/<id>) worked with all harnesses except Pi. piModelsToRegister gated custom model registration on startsWith("accounts/fireworks/"), excluding user-specific deployment paths. Every other harness (Claude, VS Code, Codex, OpenCode) accepts any accounts/... ID freely.

Fix: Broadened the guard to startsWith("accounts/") in pi-fireworks-models.mjs:216. Custom deployments now flow through buildPiCustomFireworksModelEntry with graceful defaults (128K context, no cost block) — same degradation any unrecognized accounts/fireworks/... router gets.

Test plan

  • 15 new tests in vscode-safestorage.test.mjs: Linux prefix/iteration matrix (proves 1-iter vs 1003-iter are incompatible), Windows AES-256-GCM round-trip/layout/nonce-randomness/tamper-detection, Local State error paths (missing file, missing field, wrong prefix, bad JSON)
  • 2 new tests in pi-fireworks-models.test.mjs: custom deployment ID registration and effective model resolution
  • All existing safestorage + Pi tests pass (no regressions)
  • End-to-end Windows verification on dev-cloudpos: fireconnect vscode on --api-key → blob in state.vscdb decrypts with VS Code's Local State key → fireconnect vscode status reads it back
  • Pi custom deployment tested by user: fireconnect pi on --main accounts/ahmadshahzad/deployments/ub9lvh50 works as expected

shzdehmd added 2 commits July 20, 2026 00:09
…rage

Linux: swap v10/v11 prefixes (v11=keyring, v10=basic_text) and use 1
PBKDF2 iteration instead of 1003 (macOS-only). The old params produced
ciphertext VS Code's safeStorage couldn't decrypt, causing it to silently
delete the secret and lose the API key on every restart.

Windows: replace raw-DPAPI-on-plaintext with v10 + AES-256-GCM using a
DPAPI-protected 32-byte key loaded from VS Code's Local State file
(os_crypt.encrypted_key). Fix the DPAPI round-trip to use buffer-based
functions that preserve binary data (the old UTF-8 string path corrupted
the 32-byte key into 45 bytes).

macOS: unchanged (v10 + AES-128-CBC + 1003 iterations + keychain master
password) — matches Chromium's OSCrypt macOS scheme.

Verified against Chromium os_crypt/async/common/algorithm.mojom and
encryptor.cc, and confirmed the Windows blob layout (v10 + nonce(12) +
ct + tag(16) at end) via a ground-truth probe against Electron
safeStorage on Windows.
…ts/<id>)

piModelsToRegister gated custom model registration on
startsWith('accounts/fireworks/'), excluding custom deployment IDs
(accounts/<user>/deployments/<id>). Broadened to startsWith('accounts/')
so custom deployments flow through buildPiCustomFireworksModelEntry with
graceful defaults (128K context, no cost block) — same as any other
unrecognized accounts/ ID. Other harnesses (Claude, VS Code, Codex,
OpenCode) already accept these IDs freely; Pi was the only one with the
prefix filter.
@deepsimulation

Copy link
Copy Markdown
Collaborator

thank you! fixed in v0.9.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants