release(v4.11.0): streaming Double-Ratchet sub-sessions (ShadeStream)
Some checks failed
Cross-platform vectors / TypeScript vectors (bun) (push) Has been cancelled
Cross-platform vectors / Kotlin vectors (gradle) (push) Has been cancelled
Test / test (push) Has been cancelled
Docker build and publish / docker (push) Has been cancelled
Publish / publish (push) Has been cancelled

Answers Vyvern FR shade-ws-streaming-ratchet.md with a first-class
streaming-session API rather than the documented-contract fallback.
The Double-Ratchet crypto was already safe for high-frequency
one-directional use; the send/receive wrapper was not (per-frame
saveSession keystore write; shared per-peer mutex + single stored
session row coupling reuse to the HTTP path).

- @shade/core: stream.ts — identity-bound 3-DH seeding (X3DH-minus-
  prekeys, no prekey-server round trip, mutually authenticated against
  the parent session's pinned identities), bootstrapStreamSession
  reusing init{Sender,Receiver}Session verbatim, in-memory-only
  StreamRatchet (own op-mutex, never persisted, zeroized on close).
  beginStream/acceptStream on ShadeSessionManager; Stream{Closed,
  Handshake}Error; stream.opened/closed events.
- @shade/proto: STREAM_OPEN/OPEN_ACK/FRAME wire (0x31/0x32/0x33),
  additive; inspectEnvelopeType extended.
- @shade/sdk: Shade.openStream/acceptStream → ShadeStream
  (handshakeFrame/handleHandshake/seal/open/close), transport-
  agnostic, independent of encrypt/decrypt queues + parent session,
  identical server (sqlite:) and browser (IndexedDB) — touches no
  storage.
- Tests: 5000-frame one-directional burst (bounded skipped keys + FS
  zeroize), parent-session independence, replay/rewind rejection,
  mutual-auth, proto wire round-trips. Full suite green (1159 pass).
- docs/streaming-sessions.md (R1–R7 contract); SECURITY.md matrix rows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-15 11:29:09 +02:00
parent 188c3db56a
commit 037f994572
39 changed files with 1241 additions and 31 deletions

View File

@@ -100,6 +100,9 @@ matrix row in the same change.
| § 1 Network attacker — replay window | ±5 min `signedAt` enforcement | `packages/shade-server/tests/server.test.ts` (`"rejects registration with stale signedAt"`) |
| § 1 Network attacker — header AAD | Ratchet headers bound to ciphertext | `packages/shade-core/tests/ratchet.test.ts`, `packages/shade-streams/tests/tamper.test.ts`, `packages/shade-streams/tests/aead.test.ts` |
| § 1 Network attacker — forward secrecy | DH ratchet step + chain-key zeroize | `packages/shade-core/tests/ratchet.test.ts`, `packages/shade-crypto-web/tests/hardening.test.ts` |
| § 1 Network attacker — streaming sub-session FS/replay (V4.11) | Per-frame Double-Ratchet `seal`/`open`; counter-rewind & replay rejected; in-memory-only (never persisted) | `packages/shade-core/tests/stream.test.ts` (`"R1: replayed / rewound frame is rejected"`, `"R2/R3: long one-directional burst stays correct and memory-bounded"`) |
| § 1 Network attacker — streaming handshake auth (V4.11) | Identity-bound 3-DH against parent-session-pinned identities | `packages/shade-core/tests/stream.test.ts` (`"handshake is mutually authenticated against pinned identities"`) |
| § 3 Endpoint compromise — streaming sub-session isolation (V4.11) | Stream ratchet derived without touching the stored parent session; zeroized on close | `packages/shade-core/tests/stream.test.ts` (`"R5: opening/using/closing a stream never touches the parent session"`, `"close() zeroizes and blocks further use; idempotent"`) |
| § 2 Compromised prekey server — public-only storage | Prekey store never accepts a private key | `packages/shade-server/tests/server.test.ts`, `packages/shade-storage-sqlite/tests/sqlite-prekey-store.test.ts` |
| § 2 Compromised prekey server — signed replenish/delete | Per-identity Ed25519 signature | `packages/shade-server/tests/server.test.ts` |
| § 2 Compromised prekey server — fake-bundle detection | Out-of-band fingerprint comparison | `packages/shade-core/tests/fingerprint-session.test.ts` |