Commit Graph
10 Commits
Author SHA1 Message Date
ancsemi 6454688837 Give each integration test its own port 2026-10-15 12:38:49 -04:00
ancsemi 70adbee04b Replace em dashes in test comments and names 2026-10-10 16:08:55 -04:00
ancsemi e856d5229c Deleting a group DM as an admin who is not in it also removes its keys and invites (#5733)
Only the last-member-leaves path cleaned up a group's key, epoch, invite
and rewrap rows. That cleanup now lives in the shared DM delete, so every
way a group is deleted removes them.
2026-10-05 18:28:49 -04:00
ancsemi 7327d5627a Group DMs only share the group key with keys this device has pinned, and only accept keys a member signed (#5733)
The server relays every group key, so it could forge a rewrap request and
collect the group key from everyone online, or plant an epoch key of its
own. Now a device wraps the group key only for the encryption key it has
pinned for that person (the same pins 1:1 DMs use), accepts a wrapped key
only from a sender whose pinned key opens it, and requires each epoch to
carry a statement signed by the member who published it, naming the key,
the group, the epoch and exactly who it was shared with. Signing keys are
pinned on first sight too. When someone's keys change, Haven asks before
trusting them, as 1:1 DMs do, and until then the group key is not shared
with the new keys and messages signed with them stay hidden. Groups whose
current key predates this are rotated to a signed one automatically.
2026-10-05 18:28:49 -04:00
ancsemi af3316c327 Group messages stay readable after the sender resets their encryption keys (#5733)
Only the sender's current signing key was tried, so a key reset turned
everything they had sent before into "could not be verified". The server
now keeps every signing key a person has published, and a message shows
when it verifies under any of them. A message that verifies under none of
its author's recorded keys is still hidden.
2026-10-05 18:28:49 -04:00
ancsemi b5341b8772 Group DM signing keys are set up once per connection, so a first-time user no longer ends up with mismatched keys (#5733)
Setup ran twice at once on every connect and each run accepted any
published reply, so a new account could save one key to its backup while
the server kept another, and everyone then saw that person's group
messages as unverifiable. Setup is now shared by every caller, replies
carry a request id, and the key and its backup are stored in one step.
Another device racing on a new account adopts the winner's key.
2026-10-05 18:28:49 -04:00
ancsemi a8972c01b3 Opening a DM with someone no longer reuses a group DM that has shrunk to the two of you (#5733)
Once members left a group, starting a 1:1 DM could land in the group
instead, where 1:1 messages were then refused. Groups are now skipped
when looking for an existing DM.
2026-10-05 18:28:49 -04:00
Amnibro 28f5f0f6b8 Group DMs: create, invite, leave, and end-to-end encrypted messaging in the web client (#5733) 2026-10-05 17:59:39 -04:00
ancsemi c64f2784d3 Tests: a certless start makes its own certificate; plain-HTTP tests say so explicitly 2026-09-07 19:52:23 -04:00
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