Commit Graph
5 Commits
Author SHA1 Message Date
samiandClaude Opus 5.5 c84015f67b Multi-user: email sign-in, invite/open registration, admin calls
- kv-proto: email on setup, AccountInfo.email/is_admin, ServerInfo.registration, admin DTOs.
- kv-sync: prelogin by email or account id (setup QR / Emergency Kit), register with email,
  set_email, /v1/admin/* client calls.
- kv-ffi: register_account(code, email), login(.., email), account_details, set_account_email,
  admin_create_invite. kv-wasm: the same plus the full admin API for the web vault.
- Live test: two users on one server stay isolated; a new device signs in by email.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 07:35:23 +07:00
samiandClaude Opus 5.5 83d56d43e2 Removed devices: DeviceRevoked, identity reset and one retry with the ticket
kv-sync: SyncError::DeviceRevoked for 403 device_revoked (drops the session);
the test server answers it after OPAQUE (before the ticket) and for valid
signatures by a revoked device's key.

kv-ffi: CoreError::DeviceRevoked; the foreign DeviceSigner gains reset() (new key
and id). On device_revoked the core resets the identity, forgets the session
and the kept OPAQUE input (no silent re-enrolment) and keeps the vault data; a
login carrying a ticket retries once as the new device. reset_device_identity()
runs the reset on demand. Signed calls (sync, devices, tickets, join) reset too.

kv-wasm: DeviceRevoked; the browser binding replaces the WebCrypto key and id in
IndexedDB (same name and platform), loginWithSetup retries once with a ticket;
resetDeviceIdentity().

Tests: end to end revoked -> reset -> re-enrol with the same ticket (kv-ffi and
web core), no-ticket path, in-browser IndexedDB reset.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-20 22:25:03 +09:00
samiandClaude Opus 5.5 cfab3ab543 kv-sync: authenticated tombstones (sealed {id, seq, deleted: true})
Tombstones carried no ciphertext, so the server could forge a delete (hiding
an item), and a device that pulled one accepted the next live version without
a seq comparison (delete-then-replay rollback).

* A delete is sealed like an item (same AAD and padding) with plaintext
  {id, seq, deleted: true}; seq = working/base seq + 1. DELETE carries it as
  the ItemPut body; the test server stores and serves it.
* replay: Base is None | LegacyTombstone | Sealed(plaintext). check_pulled
  refuses a record without ciphertext, a deleted flag that disagrees with the
  plaintext, or a tombstone naming another item, then requires seq to grow
  for tombstones too. The `restores` exception is gone: edit beats delete
  re-seals with max(edit, tombstone) + 1. next_version returns just seq.
* Legacy: a pulled tombstone without ciphertext is an integrity alarm (cannot
  be told apart from a forgery). A pending legacy delete is sealed on push; a
  legacy tombstone already held as base is accepted as unknown seq.
* engine: open_sealed, seal_tombstone; next_local_version -> u64. Merkle leaf
  hashes a sealed tombstone's envelope like a live record's.
* kv-model Item: `restores` removed. kv-ffi delete_item seals the tombstone;
  FFI surface unchanged.
* Tests: forged (unsealed and relabelled) tombstones raise an integrity alarm
  (kv-sync and kv-ffi), replay after a tombstone, replays of the tombstone and
  of the restore, legacy pending delete, shared item_replay.json table; the
  live test now deletes an item through the real server.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 11:34:08 +09:00
samiandClaude Opus 5.5 abcb758b67 kv-sync: refuse per-item replays (seq/restores in the item plaintext)
A malicious server could re-serve an old but valid envelope of an item under
a new, higher revision: the AAD does not bind the revision and the Merkle
root was only checked when head_revision == cursor. The client took it as
the latest version, rolled the item back and signed over it in its next
manifest.

* kv-model Item: `seq: u64` (serde default 0) and `restores: Option<u64>`.
* kv-sync replay module: pure decision rules (check_successor,
  next_version, stamp, same_content). Every pulled live record must
  authenticate and have seq > the base's seq, unless it carries
  restores = t with base_revision < t < revision (edit beat a delete this
  device never saw) or is identical to the base. Otherwise: Integrity alarm.
  Tombstones and items without a base pass.
* engine: check on every pulled record; edit-beats-delete re-seals with
  restores = tombstone revision; merges write seq = max + 1 and adopt the
  remote version when only seq/restores differ. Public
  engine::next_local_version gives the seq/restores for a local save
  (used by kv-ffi save_item and the tests).
* verify_manifest checks the Merkle root whenever no held item is newer
  than head_revision (previously only at head == cursor).
* Tests: replay alarm (clean and pending receivers, plus kv-ffi), harmless
  replay of the current version, tampered envelope, concurrent equal-seq
  edits across three devices, edit-beats-delete with a device that skipped
  the tombstone and a later replay of the restore, legacy items without seq,
  hidden item with cursor past the manifest head, and the shared
  item_replay.json decision table.
* gen-vectors: envelope vector item payload now includes seq.

FFI surface unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 11:08:42 +09:00
samiandClaude Opus 5.5 2be9a3baad M1: persistent vault, local unlock key, sync client, test vectors
kv-store: SQLite (bundled) and in-memory Store holding only ciphertext
envelopes, with base copies for three-way merges and a 10-version history.

kv-ffi: VaultCore::open(path); persisted account record; local unlock key
(enable/unlock_with_local_key/disable); async sync API (configure_server,
set_device_signer, register_account, login, sync_now, logout, devices);
DeviceSigner foreign trait; IntegrityAlarm/PinMismatch/... error variants.

kv-sync: reqwest + rustls (TLS 1.3 only, ring) with SPKI pinning; OPAQUE
client on kv-proto's OpaqueSuite; RFC 9421 signing per kv_proto::httpsig;
pull/push with If-Match, field-level three-way merge, edit-beats-delete;
signed manifest (canonical CBOR, Merkle root, prev chain, Ed25519) with
rollback/fork/Merkle checks as hard integrity alarms. Tested against a
wiremock fake server, plus an #[ignore] test against kestrove-server.

kv-crypto: gen-vectors example and tests reproducing kestrove-spec/vectors.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 21:52:14 +09:00