Commit Graph
10 Commits
Author SHA1 Message Date
samiandClaude Opus 5.5 8c40b6cccc kv-ffi: passkey API (create_passkey, passkeys_for_rp, get_assertion, list_passkeys, delete_passkey, kestrove_aaguid)
New module crates/kv-ffi/src/passkeys.rs; lib.rs only gains the mod/pub use lines.
Tests verify registrations and assertions like an RP (ciborium + p256), check RP isolation,
reopening the vault and use on a second device after a sync.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-17 00:28:55 +09:00
samiandClaude Opus 5.5 13eb5a34ee kv-model: passkey module (ES256, none attestation, AAGUID, KAT + independent verifier tests)
WIP: FFI surface and spec docs not yet added.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 22:59:02 +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 9da4472ad8 Untrack .DS_Store; compile rustls without TLS 1.2
The transport already restricts to TLS 1.3 (docs/02); dropping the tls12
feature keeps TLS 1.2 code out of the binary entirely.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 10:55:29 +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
samiandClaude Opus 5.5 cbef689903 kv-proto: add PreloginResponse for GET /v1/auth/prelogin
A new device needs account_id and the KDF params to derive AUTH_INPUT
before it can build KE1, so they cannot come only from login/start.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 21:31:46 +09:00
samiandClaude Opus 5.5 48c7e32d33 kv-proto: OPAQUE suite, RFC 9421 signing profile and M1 auth wire types
Adds the OpaqueSuite (Ristretto255 OPRF, TripleDH, SHA-512, identity KSF)
shared by client and server, the httpsig signature-base builders, and the
setup/credentials/account/device/SSE types for milestone M1.
Cargo.lock changes only for kv-proto's new dependencies.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 21:22:03 +09:00
samiandClaude Opus 5.5 4a152bde11 Fix Secret Key decode rejecting valid keys; verify build
The first base32 symbol carries 3 data bits (26 symbols = 130 bits, 2 spare),
but decode only accepted values 0-3, so ~half of generated keys failed to parse.
Adds an edge-case test, rustfmt config, Cargo.lock, and on-demand cross targets
instead of installing all seven in rust-toolchain.toml.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 21:14:15 +09:00
samiandClaude Opus 5.5 462fbbc523 Initial scaffold for kestrove-core
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:30:14 +09:00