Files
Shade/SECURITY.md
Sterister e6fdf31b49
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
release(v4.0.0): Shade GA — V3.x consolidation + audit prep
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>
2026-05-03 18:35:35 +02:00

8.9 KiB

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 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.

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.