cfab3ab543a3a8daf29f873e8de673077c7e0ad5
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]>
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
unsafeoutside generated FFI glue. - Secrets live in
Zeroizing<_>. Never log,Debug-print orformat!a secret. - Any change to formats needs a spec change first and new vectors in
kestrove-spec/vectors. cargo deny checkmust pass. There are no GPL dependencies (App Store).
Languages
Rust
98.1%
JavaScript
1.3%
Shell
0.6%