Some checks failed
Test / test (push) Has been cancelled
Cross-platform vectors / TypeScript vectors (bun) (push) Has been cancelled
Cross-platform vectors / Kotlin vectors (gradle) (push) Has been cancelled
Docker build and publish / docker (push) Has been cancelled
Publish / publish (push) Has been cancelled
V3.1 → V3.12 consolidated and tagged for the first GA release. Wire format unchanged from 0.4.x — 4.0 peers interoperate with 0.4.x peers byte-for-byte. The version bump is semantic: audit-cycle complete, opt-in surface fully exposed, threat model refreshed for every new surface. Highlights: - All 24 @shade/* packages bumped to 4.0.0 in lockstep. - CHANGELOG 4.0.0 section is the canonical manifest of what landed. - THREAT-MODEL extended (§10 fingerprint gates, §11 WebRTC P2P, §12 Web-Worker boundary) + residual-risks table refreshed. - OpenAPI now covers all 27 routes: prekey, transfer, KT, inbox, bridge, observer, /metrics, /healthz, /ready. - MIGRATION 0.3.x → 4.0 documented + smoke-tested against shade migrate-storage on a real SQLite DB. - docs/audit/REVIEW-BUNDLE.md + SCOPE.md ready for external reviewer. - scripts/soak.ts harness for the GA-stable 2-week soak window. - All V*.md plans archived under docs/archive/ with Status: Done. - Voice/Video carved out into V5.0; 4.0 audit focuses on the frozen non-realtime stack. Tests: TS 1000/1000 + Kotlin 11/11 cross-platform vectors green. Docker: gt.zyon.no/stian/shade-prekey:4.0.0 builds and reports version 4.0.0 on /health. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
129 lines
8.9 KiB
Markdown
129 lines
8.9 KiB
Markdown
# Security Policy
|
|
|
|
## Review status
|
|
|
|
| Area | Status | Notes |
|
|
|------|--------|-------|
|
|
| Internal review | **Done** | Every mitigation in `THREAT-MODEL.md` is cross-linked to at least one automated test (see [Threat-/test-matrix](#threat--test-matrix) below). The matrix is enforced by `tests/security/*` + the cross-platform vector suite. |
|
|
| Independent code review | **Pending** | Targeted for **V4.0**. No external review has been completed. |
|
|
| Independent crypto review | **Pending** | Targeted for **V4.0** alongside the audit. |
|
|
| Pen test | **Pending** | Targeted for **V4.0**. |
|
|
|
|
> **Read this:** Shade implements the Signal Protocol primitives
|
|
> (X3DH + Double Ratchet) on top of `@noble/curves` and SubtleCrypto.
|
|
> The protocol is well-studied; the **implementation** has not yet been
|
|
> audited externally. Treat the wire format as stable but the
|
|
> implementation as "production-ready in trusted contexts" until V4.0
|
|
> closes the audit gap. The `THREAT-MODEL.md` cells with no test
|
|
> linkage are documentary, not enforced.
|
|
|
|
## Reporting a Vulnerability
|
|
|
|
If you discover a security vulnerability in Shade, please report it
|
|
privately by emailing the maintainer rather than opening a public
|
|
issue. We take all reports seriously and will respond within 48 hours.
|
|
|
|
### How to report
|
|
|
|
1. **Email:** the maintainer email listed in the package metadata.
|
|
For coordinated disclosure, prefer email over GitHub/Gitea so the
|
|
issue does not become public before a fix ships.
|
|
2. **PGP / age:** if you need encrypted reporting, ask for a key
|
|
over the same email — keys are not bound to the repo to avoid
|
|
key-rotation drift.
|
|
3. **Scope:** CVE-style severity (CVSS v3.1) is appreciated but not
|
|
required. A working reproduction is more valuable than a CVSS
|
|
score.
|
|
|
|
When reporting, please include:
|
|
- A description of the vulnerability
|
|
- Steps to reproduce (a runnable script or test case)
|
|
- Affected versions
|
|
- Potential impact
|
|
- Any suggested mitigation
|
|
|
|
We commit to:
|
|
- Acknowledging receipt within 48 hours.
|
|
- A first-pass triage within 7 days.
|
|
- A coordinated disclosure timeline once severity is agreed; for
|
|
high-severity issues we aim to ship a patched release within 30
|
|
days of triage.
|
|
|
|
## What's in scope
|
|
|
|
Shade aims to provide:
|
|
- Confidentiality of message contents (only sender and intended recipient can read)
|
|
- Forward secrecy (past messages stay safe if a key is compromised later)
|
|
- Post-compromise security (future messages re-secure after compromise)
|
|
- Authentication of identity keys (signed prekey verification, replay protection)
|
|
|
|
Vulnerabilities in any of these guarantees are in scope and high priority.
|
|
|
|
## What's out of scope
|
|
|
|
Shade does NOT protect against:
|
|
- A compromised endpoint (if your device is rooted, the attacker can read messages directly)
|
|
- Metadata leakage (the prekey server sees who fetches whose bundle and when)
|
|
- Traffic analysis (encrypted message sizes and timing are visible)
|
|
- A malicious prekey server distributing fake bundles (mitigation: verify safety numbers out-of-band)
|
|
- Loss of user identity verification (if users don't compare fingerprints, MITM is possible at session establishment)
|
|
|
|
These are documented in [THREAT-MODEL.md](./THREAT-MODEL.md).
|
|
|
|
## Identity verification recommendation
|
|
|
|
When using Shade, you should provide users with a way to compare safety numbers out-of-band (in person, over a video call, or through a separate trusted channel) before treating a session as fully verified. The `getIdentityFingerprint()` API returns a 60-digit number formatted in 12 groups, designed for human comparison.
|
|
|
|
## Cryptographic primitives
|
|
|
|
Shade uses well-established primitives:
|
|
- **X25519** for Diffie-Hellman key agreement (via @noble/curves)
|
|
- **Ed25519** for digital signatures (via @noble/curves)
|
|
- **AES-256-GCM** for symmetric encryption (via Web Crypto SubtleCrypto)
|
|
- **HKDF-SHA256** for key derivation (via Web Crypto SubtleCrypto)
|
|
- **HMAC-SHA256** for chain key advancement (via Web Crypto SubtleCrypto)
|
|
|
|
These match the Signal Protocol specification.
|
|
|
|
---
|
|
|
|
## Threat-/test-matrix
|
|
|
|
This is the consolidated index that backs `THREAT-MODEL.md`. Every
|
|
threat-model row that claims a mitigation must point to at least one
|
|
test file here. Pull requests that add a new mitigation must add a
|
|
matrix row in the same change.
|
|
|
|
| Threat-model row | Mitigation | Test file(s) |
|
|
|------------------|------------|--------------|
|
|
| § 1 Network attacker — signed writes | Ed25519 signature on every write | `packages/shade-server/tests/server.test.ts` |
|
|
| § 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` |
|
|
| § 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` |
|
|
| § 3 Endpoint compromise — forward secrecy | Old keys not recoverable from leak | `packages/shade-core/tests/ratchet.test.ts`, `packages/shade-crypto-web/tests/hardening.test.ts` |
|
|
| § 3 Endpoint compromise — post-compromise security | First DH ratchet evicts leaked state | `packages/shade-core/tests/ratchet.test.ts` (`"alternating messages trigger DH ratchets"`) |
|
|
| § 3 Endpoint compromise — memory zeroization | Buffers wiped after use | `packages/shade-crypto-web/tests/hardening.test.ts` (`"zeroize"`) |
|
|
| § 3 Endpoint compromise — identity-rotation invalidates resume | Device-key bound to signing key | `packages/shade-core/tests/identity-rotation.test.ts`, `packages/shade-transfer/tests/resume.test.ts` |
|
|
| § 4 Compromised device storage — at-rest stream secrets | Resume secret AES-GCM under device-key | `packages/shade-transfer/tests/resume.test.ts` |
|
|
| § 4 Compromised device storage — at-rest session DB | **Pending V3.2** | _none yet_ |
|
|
| § 5 Timing side-channel — constant-time compare | XOR accumulator | `packages/shade-crypto-web/tests/hardening.test.ts` (`"timing variance stays bounded across mismatch positions"`) |
|
|
| § 5 Timing side-channel — primitives | SubtleCrypto + @noble/curves | `packages/shade-crypto-web/tests/provider.test.ts`, `packages/shade-streams/tests/aead.test.ts` |
|
|
| § 6 DoS — per-IP register/bundle rate limit | Token bucket per IP | `packages/shade-server/tests/rate-limit.test.ts` |
|
|
| § 6 DoS — per-identity replenish/delete rate limit | Token bucket per identity | `packages/shade-server/tests/rate-limit.test.ts` |
|
|
| § 6 DoS — body size cap (64 KiB) | Hono middleware | `packages/shade-server/tests/server.test.ts` |
|
|
| § 6 DoS — address validation | Regex + NFKC + length | `packages/shade-server/tests/server.test.ts` |
|
|
| § 6 DoS — per-sender ops/byte quota (`@shade/files`) | RateLimiter token bucket | `packages/shade-files/tests/security/quota.test.ts` |
|
|
| § 6 DoS — replay protection (`@shade/files`) | Idempotency cache | `packages/shade-files/tests/security/replay.test.ts` |
|
|
| § 6 DoS — fingerprint gate (`@shade/files`) | Per-sender trust check | `packages/shade-files/tests/security/fingerprint-gate.test.ts` |
|
|
| § 6 DoS — tampered envelope reject (`@shade/files`) | AEAD reject | `packages/shade-files/tests/security/tampered-envelope.test.ts` |
|
|
| § 8a Recovery — k-1 collusion impossible | Shamir Secret Sharing over GF(2^8) | `packages/shade-recovery/tests/shamir.test.ts`, `packages/shade-recovery/tests/adversarial.test.ts` |
|
|
| § 8b Recovery — forged share rejected | AES-GCM tag on backup blob + subset-search | `packages/shade-recovery/tests/adversarial.test.ts` (`"a corrupted share never authenticates against the backup AEAD tag"`) |
|
|
| § 8c Recovery — guardian OOB-fingerprint gate | Two-checkbox `<RecoveryApprove />` + decline propagation | `packages/shade-recovery/tests/adversarial.test.ts` (`"approve handler that REJECTS a wrong fingerprint never sends a grant"`, `"throwing approve handler counts as decline with descriptive reason"`) |
|
|
| § 9 Cross-sender X3DH state corruption | `initReceiverSession` copies keypair | `packages/shade-core/tests/ratchet.test.ts` (`"does not mutate the caller-provided keypair after a DH ratchet step"`), `packages/shade-recovery/tests/integration.test.ts` |
|
|
|
|
If you add a new mitigation, add a row here in the same PR — the
|
|
threat model is the contract; this matrix is the proof.
|