Files
Haven/package.json
T
AmnibroandCursor daefc658f2 Group DM E2E: crypto core, key distribution, and tests (#5499)
* Group DM E2E: crypto core, key distribution, and tests

Implements the design in docs/group-dm-e2e-plan.md far enough to prove the
properties hold. No UI yet — this is the layer everything else sits on.

public/js/e2e-group.js holds the primitives, deliberately free of storage,
sockets and DOM so the same file runs in the browser and under node --test.
Confidentiality comes from an AES-256 epoch key wrapped per member over the
pairwise ECDH secret that already exists; authenticity from an ECDSA P-256
signature over a digest binding channel, epoch, sender and the previous
message. Verification happens before decryption and a rejected message
returns null plaintext, so a forgery cannot be rendered by mistake.

The signature is what makes one shared group key safe: every member can
decrypt, only a key holder can author.

src/socketHandlers/groupE2E.js distributes wrapped blobs the server cannot
open, while enforcing what no client can check for itself — only a member
may publish an epoch, epochs are strictly sequential, a member reads only
their own blob, and an epoch must cover exactly the current membership.
That last rule stops a member publishing an epoch that silently omits
someone, locking them out of a conversation the UI still shows them in.

Two suites, 28 tests, runnable with npm test. The crypto suite is written
as attacks rather than happy paths: forging as another member, re-attributing
a genuine message, tampering, stripping the signature, replaying into another
channel or epoch, reading an epoch you were not given, and unwrapping a key
not wrapped for you. It also asserts scrollback still decrypts across several
rotations, which is the regression a per-message ratchet would have caused,
and that concurrent sends sharing a prev do not false-alarm.

Confirmed the suite is load-bearing by disabling the signature check: exactly
the five attack tests fail and nothing else.

* Group DMs invite instead of auto-joining, and rewraps require a request.

Addresses review on #5499: drop leftover setup.js, consent plus roster
dedup on start-group-dm, and pin rewraps to a prior request plus the
recipient's TOFU public key.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 22:00:38 -04:00

44 lines
1.0 KiB
JSON

{
"name": "haven",
"version": "4.2.0",
"description": "Haven - self-hosted private chat for your server, your rules",
"license": "AGPL-3.0",
"main": "server.js",
"engines": {
"node": ">=18.0.0 <27.0.0"
},
"scripts": {
"start": "node server.js",
"dev": "node --watch server.js",
"test": "node --test --test-concurrency=1"
},
"dependencies": {
"adm-zip": "^0.6.0",
"archiver": "^7.0.1",
"bcryptjs": "^2.4.3",
"better-sqlite3": "^12.10.0",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"express-rate-limit": "^7.5.1",
"helmet": "^8.1.0",
"jsonwebtoken": "^9.0.2",
"localtunnel": "^2.0.2",
"multer": "^2.3.0",
"music-metadata": "^11.15.0",
"otpauth": "^9.5.0",
"qrcode": "^1.5.4",
"socket.io": "^4.8.1",
"web-push": "^3.6.7",
"ws": "^8.18.3",
"yauzl": "^3.4.0"
},
"overrides": {
"bn.js": ">=5.2.3",
"axios": ">=1.8.2"
},
"devDependencies": {
"innosetup-compiler": "^6.3.1",
"socket.io-client": "^4.8.1"
}
}