mirror of
https://github.com/ancsemi/Haven.git
synced 2026-10-30 18:45:20 +00:00
* Design: end-to-end encrypted group DMs Group DMs do not exist yet — start-dm takes a single targetUserId — so this is a greenfield design rather than a retrofit. Proposes an epoch group key: one random AES-256 key per group per epoch, wrapped to each member over the pairwise ECDH channel that already exists, rotated on every membership change. Messages and attachments are encrypted once regardless of group size. Records two properties of the current 1:1 scheme that the design turns on, both of which are easy to miss: there is no forward secrecy, and there is no cryptographic sender authentication — the pairwise key is symmetric, so either party can already produce a ciphertext the other accepts, and sender identity is a server assertion. The chosen scheme therefore widens the set who could forge from one to N-1 rather than introducing a new weakness, and the doc says so rather than glossing it. Rejects pairwise fan-out (multiplies every message and attachment by member count), sender keys (do not authenticate without a signing key Haven lacks), and MLS (no vetted stack across JS, Kotlin and Swift; disproportionate for 3-10 person groups). Notes the upgrade path in dependency order, starting with an ECDSA signing key that would close the same authentication gap 1:1 DMs already have. * Group DM E2E: sign messages rather than settling for a shared-key trade Revises the design to do sender authentication properly instead of noting the gap and moving on. Adds an ECDSA P-256 signing identity key as the foundation: wrapped in the same password-derived blob, published through the same TOFU-pinned flow. Every message is signed over a digest binding channel, epoch, sender and ciphertext, so a valid ciphertext cannot be replayed into another group or epoch, or re-attributed. Confidentiality comes from the epoch key and authenticity from the signature, which is what makes one shared group key safe: everyone can decrypt, only a key holder can author. Applies the same signature to 1:1 DMs, which have the identical forge gap today. P-256 over Ed25519 for reach only — WebCrypto, JCA and CryptoKit all cover it with no polyfill, and this has to work identically across three independent client implementations. Documents why a per-message ratchet is the wrong answer here despite being the textbook one. Haven decrypts history on demand from server-held ciphertext on every scroll-back, so deleting old message keys would make re-fetched history undecryptable. A ratchet buys forward secrecy only by breaking scrollback, or by persisting every message key forever — more key material at rest than the single epoch key it replaces. Forward secrecy is therefore epoch-granular with rotation policy as the knob. Also records that key presence is effectively universal (publish-public-key fires on every init) while key availability is not, since a fresh device without a password lands in the existing ghost state. Only the recovery path needs to care. * Group DM E2E: detect a tampered channel by verifying the conversation Adds transcript chaining and roster gossip, and records why network identifiers were rejected for the same job. Each message carries prev, the hash of the previous envelope its sender had seen, inside the signed digest. Signatures alone authenticate each message but say nothing about the set of them, so a hostile server can still drop, withhold or reorder and every remaining signature still verifies. Chaining turns the history into an append-only structure the server cannot rewrite: a withheld message leaves the next prev dangling, reordering fails to link, and a split view makes two members' chains diverge. Concurrent sends share a prev legitimately, so clients treat it as a DAG and alarm only on a prev that never arrives. Roster gossip has each member publish a signed digest over the sorted group key list. Matching digests mean nobody has been fed a substituted key; a lone mismatch names the disagreement. A 1:1 conversation cannot do this because there is no third party to disagree with — the group is its own redundancy, so substitution becomes detectable without reading safety numbers aloud. The existing pairwise safety number stays as the manual escalation. MAC and IP were considered for this and do not work: a browser cannot read its own MAC, MAC does not survive the first router hop so it could only be self-reported, self-reported values are attacker-controlled, a server-observed IP is circular when the server is the untrusted party, both are deliberately unstable now so they would emit false alerts that train people to dismiss warnings, storing them is the tracking surface this project avoids, and neither says anything about whether a fetched public key is genuine. * Point the constraint note at the tamper-detection section too