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

kestrove-core

Rust workspace that holds all Kestrove crypto and vault logic. Apple, Android and desktop apps call it through UniFFI. The server uses only kv-proto.

Crate Status Purpose
kv-proto ✅ M0 Wire types (Envelope, KDF params, API bodies). No secrets.
kv-crypto ✅ M0 2SKD (Argon2id + Secret Key), XChaCha20-Poly1305 envelopes, HPKE vault-key sealing, keyset, Ed25519, Secret Key format
kv-model ✅ M0 Items, URL matching (PSL), TOTP (RFC 6238), password generator
kv-ffi ✅ M1 The exported API (VaultCore with SQLite storage, local unlock key, sync; totp_code, match_url, generate_password)
kv-store ✅ M1 SQLite replica (ciphertext only), base copies for merges, 10-version history
kv-sync ✅ M1 TLS 1.3 + SPKI pins, OPAQUE login, RFC 9421 signing, pull/push/merge, signed manifest

Spec: ../kestrove-spec/docs/02-cryptography.md and 03-sync-protocol.md.

Setup

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh   # once
cargo test --workspace

Bindings

./scripts/build-apple.sh     # → ../kestrove-apple/Packages/KestroveCore (XCFramework + Swift)
./scripts/build-android.sh   # → ../kestrove-android/core-ffi (jniLibs + Kotlin); needs cargo-ndk

The desktop (Tauri) app depends on kv-ffi directly as a Rust crate.

Test vectors and the live-server test

cargo run -p kv-crypto --example gen-vectors   # writes crates/kv-crypto/tests/vectors and ../kestrove-spec/vectors
cargo test -p kv-ffi --test live_server -- --ignored   # against a fresh local kestrove-server; see the test's docs

Rules

  • No unsafe outside generated FFI glue.
  • Secrets live in Zeroizing<_>. Never log, Debug-print or format! a secret.
  • Any change to formats needs a spec change first and new vectors in kestrove-spec/vectors.
  • cargo deny check must pass. There are no GPL dependencies (App Store).
S
Description
Rust core: crypto, vault, sync, passkeys, import
Readme
1.1 MiB
Languages
Rust 98.1%
JavaScript 1.3%
Shell 0.6%