Commit Graph

17 Commits

Author SHA1 Message Date
adlee-was-taken
92febf38f1 refactor(core,wasm): zeroized ed25519 seed decode in core; org_unwrap_key reuses it
Review fixes for Task 1 (org_unwrap_key). Public signature unchanged.

- Add device::extract_ed25519_seed -> Zeroizing<[u8;32]>: length-checks the
  slice then copy_from_slice straight into the Zeroizing buffer, so the raw
  seed never lands in a plain [u8;32] (Important #1).
- Refactor device::sign() to call the new helper (pure refactor; device/sign
  roundtrip tests unchanged and green) (Important #2).
- org_unwrap_key now calls relicario_core::device::extract_ed25519_seed
  directly; drop the duplicated local decode_device_seed helper and remove the
  redundant ssh-key dep from relicario-wasm (core already pins it) (Important #2).
- Device-key-form finding (OpenSSH PEM, not base64; spec name matches reality)
  moved onto the core helper, condensed copy kept on org_unwrap_key.
- Test helper keeps the private key as Zeroizing<String> and passes &priv
  (derefs to &str) — no plain-String copy (Minor #3).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014s527M917W47LDrfQ4t47g
2026-06-25 21:13:49 -04:00
adlee-was-taken
d55861ffd8 feat(wasm): org_unwrap_key — ECIES unwrap into a session handle
Expose org_unwrap_key(keys_blob, device_private_openssh) to let the
extension SW unwrap a member's ECIES-wrapped org master key into an
ordinary Zeroizing session handle. The returned handle is key-agnostic
and works with the existing item_decrypt/manifest_decrypt AEAD.

Key-form finding: device private keys are OpenSSH PEM blobs produced by
relicario_core::device::generate_keypair(), not base64 raw bytes. The
plan's "device_private_key_base64" parameter name was misleading; all
code and the .d.ts declaration use "device_private_openssh" to match
the spec and reality.

Adds ssh-key dep to relicario-wasm for PEM parsing in decode_device_seed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014s527M917W47LDrfQ4t47g
2026-06-25 21:02:54 -04:00
adlee-was-taken
fc9264e9ae feat(wasm): add parse_month_year, base32_decode_lenient, guess_mime exports
Plan B Phase 8 — three #[wasm_bindgen] exports for the parsers migrated
in Phase 7, mirrored in extension/src/wasm.d.ts under "Pure parsers
(no session needed)". snake_case JS naming consistent with every
existing export; SessionHandle not required.

- parse_month_year(s) → { month, year } via js_value_for
- base32_decode_lenient(s) → Uint8Array
- guess_mime(filename) → string

Tests in session_tests mod cover the OK paths; error-path / JsValue
serialization can't be tested natively (JsError construction panics
off-wasm) and is covered in core (time::tests + base32::tests).

Plan C will wire SW message handlers consuming these exports in a
future round; this commit delivers only the seam.

Includes simplify-feedback fixes:
- relicario-core lib.rs module-list mentions base32 and mime
- item_types/totp.rs neighbour comment unified to ///-style block

cargo test --workspace: green
cargo clippy --workspace: silent
cargo build -p relicario-wasm --target wasm32-unknown-unknown: clean
cd extension && npm run test: 17 pre-existing failures only (baseline)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 11:33:40 -04:00
adlee-was-taken
1e858e1d1f fix(wasm): impl Drop for SessionHandle clears registry entry
Closes the P1.1 defense-in-depth gap: wasm-bindgen's auto-generated
.free() previously dropped the SessionHandle wrapper (a u32) without
removing the SESSIONS HashMap entry, leaving the master key and
image_secret in WASM linear memory until JS explicitly called
lock(handle). Drop now wires .free() to session::remove, and the
new native test pins the contract.

Refs: docs/superpowers/specs/2026-05-04-security-polish-design.md (Phase 1)
Refs: docs/superpowers/reviews/2026-05-04-architecture-review.md (P1.1)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 01:52:24 -04:00
adlee-was-taken
f17944a404 fix(core,wasm): correct QR version comment, expect msg, zeroize image_secret in closure 2026-05-03 21:09:02 -04:00
adlee-was-taken
ada00895d4 feat(wasm): expose generate_recovery_qr and unwrap_recovery_qr bindings 2026-05-03 20:57:55 -04:00
adlee-was-taken
42b746f9af feat(wasm): session stores image_secret for recovery QR generation 2026-05-03 20:56:39 -04:00
adlee-was-taken
fb1f28161c feat(wasm): secure device API (private keys never cross to JS)
- register_device() generates signing + deploy keypairs via core device
  module, stores them in DEVICE_STATE (once_cell Lazy<Mutex>), and
  returns only public keys to JS
- sign_for_git() signs data using the internal signing key
- get_device_info() returns name and public keys; returns null if not
  registered
- clear_device() zeroes and drops device state (logout / re-registration)
- Removed generate_device_keypair() which exposed raw private key bytes

Fixes audit I5: private key material no longer crosses the WASM boundary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-02 12:27:50 -04:00
adlee-was-taken
1f764a4639 feat(wasm): parse_lastpass_csv_json bridge
Returns { items: [Item], warnings: [ImportWarning] } as a JSON
string. The items already have fresh IDs + timestamps; the SW
caller encrypts and writes them through the existing
item_encrypt + manifest_encrypt bridges.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 23:25:25 -04:00
adlee-was-taken
7407fe512f feat(wasm): pack_backup_json / unpack_backup_json
JSON bridge for the SW. Binary fields are base64 in the JSON wrapper;
core gets borrowed byte slices.
2026-04-28 19:52:36 -04:00
adlee-was-taken
3aa17e6be2 feat(wasm): default_vault_settings_json() for wizard parity with CLI init
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 18:27:07 -04:00
adlee-was-taken
caebe9f97e feat(wasm): add generate_device_keypair + get_field_history bindings
generate_device_keypair returns an ed25519 keypair as JSON with hex pubkey
and base64 private key. get_field_history extracts tracked field history
from a decrypted item for the popup's history view.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-26 15:44:04 -04:00
adlee-was-taken
b8afec3560 feat(wasm): configure serde_wasm_bindgen for plain-object HashMap
Maps serialize as JS objects, not Maps — what the extension popup
expects. Also ships hand-written TS declarations for the bridge
(consumed by Plan 1C).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 17:41:41 -04:00
adlee-was-taken
92b9e64ef9 feat(wasm): attachment / generator / totp / imgsecret / id bridges
Also ports TOTP RFC 6238 compute to relicario-core::item_types::totp
so native + CLI + WASM share one implementation (audit H5: CSPRNG
via core's Uniform-sampling generator).

Adds hmac = "0.12" and sha1 = "0.10" to relicario-core deps to support
HOTP/TOTP HMAC with Sha1/Sha256/Sha512. RFC 6238 test vector (t=59,
SHA-1, 8 digits) passes: "94287082".
2026-04-20 17:39:45 -04:00
adlee-was-taken
fac2e49cf1 feat(wasm): manifest / item / settings encrypt+decrypt via SessionHandle
Adds six #[wasm_bindgen] functions (manifest_encrypt/decrypt,
item_encrypt/decrypt, settings_encrypt/decrypt) plus a native
round-trip test that verifies encrypt→core_decrypt and nonce
uniqueness without calling js-sys (serde_wasm_bindgen::from_value
is wasm32-only; documented in test comment).
2026-04-20 17:37:50 -04:00
adlee-was-taken
f3ce76d9fb feat(wasm): opaque SessionHandle bridge with unlock/lock
Master key never leaves WASM linear memory. Held in Zeroizing<[u8;32]>
inside a thread_local HashMap keyed by u32. lock() removes + zeroizes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 17:34:50 -04:00
adlee-was-taken
519a6f0e36 chore: rename project from idfoto to relicario
Sweeping rename across crates, CLI binary, WASM bindings, extension, docs,
and vault metadata paths. Git remote updated to relicario.git.

- crates/idfoto-{core,cli,wasm} -> crates/relicario-{core,cli,wasm}
- IdfotoError -> RelicarioError
- IDFOTO_IMAGE env var -> RELICARIO_IMAGE
- ~/.config/idfoto -> ~/.config/relicario
- .idfoto/ vault metadata dir -> .relicario/ (breaking; pre-release)
- Binary name idfoto -> relicario
- Extension wasm module idfoto_wasm -> relicario_wasm
- Storage key idfotoSettings -> relicarioSettings
- All doc filenames and content references updated

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:47:02 -04:00