diff options
Diffstat (limited to 'docs/MESHBAY_DESIGN.md')
| -rw-r--r-- | docs/MESHBAY_DESIGN.md | 256 |
1 files changed, 117 insertions, 139 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md index 3672da2..3ce0a67 100644 --- a/docs/MESHBAY_DESIGN.md +++ b/docs/MESHBAY_DESIGN.md @@ -134,155 +134,67 @@ paths, no filenames**. --- -## 2. Trust model +## 2. Who you trust -### 2.1 Adversaries +MeshBay lets you share things with the people you choose, and keeps everyone else +out. In plain terms, here is who can see your group — and the one caveat worth +knowing. -Every claim below is written against one of these, and they are the only ones the -document uses: +### 2.1 Inside your group -| Adversary | What they can do | -|---|---| -| **Passive hub** | Read everything the hub legitimately stores and relays | -| **Active hub** | Also lie: forge tokens, invent accounts, substitute values it publishes, ship modified client code to a browser | -| **Malicious node operator** | Read and alter everything on their own machine, including the plaintext files they host | -| **Malicious group member** | Everything a member may do, plus anything the protocol fails to refuse | -| **Network attacker** | Observe and tamper with traffic between any two parties | -| **Local attacker** | Reach loopback services and files on a client or node machine | -| **Registered hub user with no membership** | Reach every hub endpoint that does not check membership | -| **Federated peer hub** | Push directory rows and revocations over MHP | - -### 2.2 Security claims — and how to read them +A group lives on a node, run by its operator. Everyone in the group — the operator +and the members — shares its files, its index and its chat. That is what a group +*is*: the people you decided to let in, the same as a shared folder or a team +workspace. They are the people you trust, and they are the only ones who can read +what you put there. -Every collaboration product carries the structural limits this section lists. -Most never write them down. What follows states ours plainly and separates three -things a single ❌ usually hides — a **definition**, a **guarantee** and an -**accepted risk** — and says, where it matters, how a comparable product fares on -the same point. Read it with one rule: **where you see a limit here, assume the -mainstream closed product has it too and does not tell you.** +Two things follow from that by design — they are the shape of a shared space, not +gaps in it: -The verdict first. Against anyone *outside* your group — the hub, the network, -another group's operator — your content is unreadable. *Inside* your group, your -operator and fellow members read it, because that is what a group is. The one -place the protocol cannot protect a browser is **T3** (below); the native client -turns that from an invisible attack into a publicly verifiable one. +- Your operator hosts your files, so they can change what they host. A node's + content is the operator's to keep and to serve (§7). +- Leaving a group removes you on the hub, but copies already on a node stay with + its operator (§7.7) — as with anything you have handed to someone in person. -#### What a group inherently means +### 2.2 Everyone else sees nothing -These are not defeats of the design; they are what *a shared, hosted group* is. -Each would read as ❌ in any honest table, for any product in this class: +Outside your group nobody can read what you share — not the hub that connects you, +not anyone watching the network, not the operator of any *other* group: -- Your **operator and fellow members read the group's content** — files, index - and chat. You chose them when you joined; they are your trust boundary, not the - hub's. -- Your **operator hosts the files, so they can alter what they host.** Authority - over a node's content is the operator's by design (§2.4, §7). -- **Leaving clears you hub-side; copies on a node you were hosted on stay** with - that operator (§7.7). - -On the one point every product in this class shares — who can read your content — -stated the way an honest table would: - -| Who can read your content | MeshBay | A mainstream E2EE messenger | A mainstream cloud suite | -|---|---|---|---| -| The people in your group | Yes — operator + members | Yes — every member | Yes — members + workspace admin | -| The server / host itself | No (native) · T3 (browser) | No — but a web build has the same client-code exposure, unstated | Yes, routinely | -| Is this limit written down for you? | Here, explicitly | Rarely | Almost never | +- Your content never passes through the hub in the clear. The hub introduces nodes + to one another and relays sealed traffic; it does not hold what you share (§5). +- The hub holds no key of yours. It cannot read your group, add a device to your + account, or act as you — and it cannot hand your group's key to anyone, save + where you ask it to (an open-join group, or an invitation you asked it to mail; + §7.3, §3.4). +- Each node carries its own identity keys, so a key that somehow leaked would open + that one node and no other (§3), and a copy of a node's storage without its + unlock key reveals none of its chat (§4.5). -#### What MeshBay guarantees +### 2.3 Your operator hosts you, but cannot become you -Stated as guarantees, not as the absence of failure, against the adversaries in -§2.1: +Keeping your content is the deal you made with your operator. Turning that into +*being* you is the line they cannot cross. The identity bundle stored on their +node opens only with your passphrase and a secret the hub releases to no one but a +session that has already proved the passphrase or a device key; the only attempts +left to an operator are ordinary sign-ins, which the hub counts and locks out. And +reading what *they* host never reaches what *other* operators host (§3.2). -- Your **content never transits the hub** — the hub relays, it does not hold your - plaintext (§5). -- **No party outside your group reads your files, index or chat**: not the hub, - not the network, not the operator of any *other* group (§3.2, §4). -- The **hub holds no user key and cannot countersign**: it cannot add a device to - your account, become *you*, or obtain your group key — except where you ask it - to (open-join, mailed invitations; §7.3, §3.4). -- A **node cannot be impersonated**, and content one node holds cannot be forged - into another's (§5). -- Your **identity keys are per-node**: a bundle that leaks opens that one node's - bundle and no other (§3, §2.4). -- **Chat is unreadable from a copy of a node's storage** that lacks its unlock - key (§4.5). - -#### Residual risks, and how they are checked - -Two risks are real, named and accepted — each with the control that bounds it: - -- **T3 — a compromised hub and the browser.** The hub serves the browser SPA, so - an active hub can ship modified code to a browser user and lift their keys. No - protocol prevents this; it is accepted for browsers (§2.3). The **native client - removes it**: its code ships in the package, not from the hub, so a malicious - hub cannot alter it **without the change being publicly verifiable** — anyone - can rebuild from source and compare hashes (reproducible builds). The attack - moves from invisible-and-per-user to an artifact the whole community can check. - This is *verifiable*, not merely *detectable*: integrity someone actively - confirms, not something a victim might happen to notice. -- **C4 — keypair bundles.** For an account with browser access, each node holds - the bundle, sealed under the passphrase *and* a pepper the hub alone holds: no - operator can search it offline, only an active hub can — which is T3's adversary - already. For an account the desktop app keeps (browser access off, §3.7), - nothing of it sits on any node. An account is only as strong as its weakest - client. - -#### The full claim matrix - -For auditors: the complete claim-by-adversary matrix. The sections above are the -reading; this is the reference. - -| Claim | Passive hub | Active hub | Malicious node operator | Malicious member | Network attacker | -|---|---|---|---|---|---| -| Data never transits the hub | ✅ | ✅ | — | — | ✅ | -| File content is unreadable | ✅ | ❌ **T3** (browser) · ✅ native | ❌ by design — the operator hosts the files | ❌ members share the group key | ✅ | -| The file index is unreadable | ✅ | ❌ T3 · ✅ native | ❌ | ❌ | ✅ | -| Chat content is unreadable | ✅ | ❌ T3 · ✅ native | ❌ — the operator is a member | ❌ | ✅ | -| Chat is unreadable **from a copy of the node's storage that lacks its unlock key** — not from a whole disk by default (§4.5) | ✅ | ✅ | — the operator holds the unlock key | ✅ | ✅ | -| Content cannot be modified | ✅ | ✅ | ❌ by design | ✅ | ✅ | -| The node cannot be impersonated | ✅ | ✅ | — | ✅ | ✅ | -| Client code integrity | ❌ **T3, accepted** (browser) · ✅ ships in the package (native) | ❌ T3 · ⚠️ native: **publicly verifiable, not prevented** | ✅ | ✅ | ✅ | -| The hub cannot obtain the group key | ✅ | ✅ **except** in an open-join group, where it can join legitimately (§7.3), and for an invitation the inviter asked the hub to mail, whose code it then holds (§3.4) | — | — | ✅ | -| Node content authority | ✅ | ✅ | ✅ sovereign | ✅ | ✅ | -| Devices cannot be added by the hub | ✅ | ✅ — the hub holds no user key and cannot countersign | ⚠️ a node adds a device only to itself, where it already reads everything | ✅ | ✅ | -| Chat senders are authenticated to each other | ✅ | ✅ | ⚠️ only for accounts the reader has already seen (§3.3) | ✅ | ✅ | -| Keypair bundles (**C4**) | ✅ it holds the pepper and no bundle | ⚠️ it can fetch a bundle with a token it mints and holds the pepper: an offline passphrase search, as T3 already concedes for browsers · none to fetch for an account without browser access | ✅ no offline search: the bundle does not open without the hub's pepper — only sign-in attempts, bounded and audited · none on disk for an account without browser access | — | — | -| Deleting your account erases you | ✅ hub-side | ✅ hub-side | ❌ files, pinned identity and bundle stay on the node (§7.7) | — | — | -| Your identity keys stay yours | ✅ | ⚠️ as the row above | ✅ the bundle they hold does not open without the pepper, and a leaked bundle key opens **that node's** bundle only | ✅ | ✅ | - -### 2.3 What the project must not claim - -Three sentences are forbidden, each for a deliberate reason: - -- **"Everything is encrypted and unreadable by other parties, even the hub."** - A hub that ships the code can lift keys from the page regardless of protocol - design (**T3**). That is an artifact-level attack, not a silent directory lie, - and it is removed for native clients — not for browsers. -- **"A native client makes the hub untrusted."** It converts an undetectable, - per-request, per-user attack into a persistent artifact that can be hashed and - compared. That value is realised by reproducible builds and published hashes, - not by the packaging format. A build signed with a key the hub operator holds - *relocates* trust; it does not remove it. -- **"C4 is closed."** It is closed for an account whose identities the desktop - application keeps (browser access off, §3.7): nothing of them is on any node. - For an account with browser access the bundle is still on each node, sealed - under the passphrase **and** a pepper the hub holds — no operator can search it - offline, but an active hub can, and that is T3's adversary already. **An account - is only as strong as its weakest client.** +### 2.4 The one honest caveat: the browser -"End-to-end" here describes **client ↔ node**, never client ↔ client. Members and -the operator read everything in their group; that is what a group is. +Used in a web browser, MeshBay is a page the hub sends you on each visit — so a hub +that had been taken over could send an altered page and lift your keys from it. +This is true of **every** web application; most simply never say so, and no +protocol can prevent it, because the attack is in the code itself rather than in +the messages. So we never claim your content is unreadable *even by the hub* when +you use a browser. -### 2.4 One boundary worth naming - -An operator hosts your content by design. They should not be able to become -*you*. The bundle on their disk does not let them try: it opens only with the -passphrase and a pepper the hub hands to nobody but a session that proved the -passphrase or a device key, so the only guesses left to them are sign-ins, which -the hub counts and locks out. And were a bundle key to leak all the same, it -opens **the bundle on that node** and no other. Reading what they host is by -design; reading what *other* operators host is not, and does not follow (§3.2). +The desktop and mobile apps close this. Their code is installed once, from a +package anyone can rebuild from source and check against a published hash, so a +tampered build is caught instead of silently trusted. For the strongest assurance, +use the app. This is the project's single standing limit for browser use — tracked +as **T3** — and the full, adversary-by-adversary analysis behind every statement +in this section is in §13.9 for readers who want it. --- @@ -2043,7 +1955,7 @@ an operator-signed op. Hub minimisation was considered and **deferred, and may be dropped** (decision D4). The hub keeps serving the web UI and remains in the trusted path by choice. That is a legitimate product call; what follows from it is carried deliberately rather than -by accident (§2.3). +by accident (§2.4). **Stores:** accounts (username, encrypted email, status, role), the group registry and membership, IP logs (one year, legal retention), node registrations, refresh @@ -3632,7 +3544,7 @@ Two structural recommendations from that review stand as rules: |---|---| | **T1** | **The password split.** The hub never sees a passphrase; it holds a verifier for a client-derived `auth_key`. The passphrase floor can therefore only be enforced client-side (§3.1) | | **T2** | The hub was the key directory. **Closed** by admission redesign, not by safety numbers: the invite path reads no directory at all (§3.4). Reclassified as **H3** | -| **T3** | **The hub serves the SPA. Accepted permanently for browser users.** It is the only remaining way an active hub reads content, it is an artifact-level attack rather than a silent lie, and it does not exist for a native client — whose value is realised by reproducible builds, not by packaging (§2.3, §8.2) | +| **T3** | **The hub serves the SPA. Accepted permanently for browser users.** It is the only remaining way an active hub reads content, it is an artifact-level attack rather than a silent lie, and it does not exist for a native client — whose value is realised by reproducible builds, not by packaging (§13.9, §8.2) | ### 13.5b Availability between members (`AV`) @@ -3765,6 +3677,72 @@ had already been asked. | **O13** | **Hub identity pinning.** The client points at a hub by URL and nothing pins that hub's identity. Bounded, because a substituted hub can neither read content nor ship the code to a native client — worth doing all the same | | **V1–V13**, **P1–P5** | Per-application open items: wording of a disabled-service state, whether artwork reuses the chunk path, cache TTL, multi-track surfacing, HEIC/RAW support, a fuller EXIF panel, lightbox preloading, album-boundary behaviour, cover selection | +### 13.9 Formal adversary model + +§2 states the trust model for a human reader. This restates the same ground +formally, for auditors and implementers: the adversaries every claim is written +against, the claim-by-adversary matrix, and the over-claims the project refuses to +make. Nothing here is new — it is §2 with the proofs shown. + +"End-to-end" throughout describes **client ↔ node**, never client ↔ client. The +operator and the members read everything in their group; that is what a group is. + +#### Adversaries + +Every claim is written against one of these, and they are the only ones the +document uses: + +| Adversary | What they can do | +|---|---| +| **Passive hub** | Read everything the hub legitimately stores and relays | +| **Active hub** | Also lie: forge tokens, invent accounts, substitute values it publishes, ship modified client code to a browser | +| **Malicious node operator** | Read and alter everything on their own machine, including the plaintext files they host | +| **Malicious group member** | Everything a member may do, plus anything the protocol fails to refuse | +| **Network attacker** | Observe and tamper with traffic between any two parties | +| **Local attacker** | Reach loopback services and files on a client or node machine | +| **Registered hub user with no membership** | Reach every hub endpoint that does not check membership | +| **Federated peer hub** | Push directory rows and revocations over MHP | + +#### Claim matrix + +| Claim | Passive hub | Active hub | Malicious node operator | Malicious member | Network attacker | +|---|---|---|---|---|---| +| Data never transits the hub | ✅ | ✅ | — | — | ✅ | +| File content is unreadable | ✅ | ❌ **T3** (browser) · ✅ native | ❌ by design — the operator hosts the files | ❌ members share the group key | ✅ | +| The file index is unreadable | ✅ | ❌ T3 · ✅ native | ❌ | ❌ | ✅ | +| Chat content is unreadable | ✅ | ❌ T3 · ✅ native | ❌ — the operator is a member | ❌ | ✅ | +| Chat is unreadable **from a copy of the node's storage that lacks its unlock key** — not from a whole disk by default (§4.5) | ✅ | ✅ | — the operator holds the unlock key | ✅ | ✅ | +| Content cannot be modified | ✅ | ✅ | ❌ by design | ✅ | ✅ | +| The node cannot be impersonated | ✅ | ✅ | — | ✅ | ✅ | +| Client code integrity | ❌ **T3, accepted** (browser) · ✅ ships in the package (native) | ❌ T3 · ⚠️ native: **publicly verifiable, not prevented** | ✅ | ✅ | ✅ | +| The hub cannot obtain the group key | ✅ | ✅ **except** in an open-join group, where it can join legitimately (§7.3), and for an invitation the inviter asked the hub to mail, whose code it then holds (§3.4) | — | — | ✅ | +| Node content authority | ✅ | ✅ | ✅ sovereign | ✅ | ✅ | +| Devices cannot be added by the hub | ✅ | ✅ — the hub holds no user key and cannot countersign | ⚠️ a node adds a device only to itself, where it already reads everything | ✅ | ✅ | +| Chat senders are authenticated to each other | ✅ | ✅ | ⚠️ only for accounts the reader has already seen (§3.3) | ✅ | ✅ | +| Keypair bundles (**C4**) | ✅ it holds the pepper and no bundle | ⚠️ it can fetch a bundle with a token it mints and holds the pepper: an offline passphrase search, as T3 already concedes for browsers · none to fetch for an account without browser access | ✅ no offline search: the bundle does not open without the hub's pepper — only sign-in attempts, bounded and audited · none on disk for an account without browser access | — | — | +| Deleting your account erases you | ✅ hub-side | ✅ hub-side | ❌ files, pinned identity and bundle stay on the node (§7.7) | — | — | +| Your identity keys stay yours | ✅ | ⚠️ as the row above | ✅ the bundle they hold does not open without the pepper, and a leaked bundle key opens **that node's** bundle only | ✅ | ✅ | + +#### Over-claims the project refuses to make + +Three sentences are forbidden, each for a deliberate reason: + +- **"Everything is encrypted and unreadable by other parties, even the hub."** + A hub that ships the code can lift keys from the page regardless of protocol + design (**T3**). That is an artifact-level attack, not a silent directory lie, + and it is removed for native clients — not for browsers. +- **"A native client makes the hub untrusted."** It converts an undetectable, + per-request, per-user attack into a persistent artifact that can be hashed and + compared. That value is realised by reproducible builds and published hashes, + not by the packaging format. A build signed with a key the hub operator holds + *relocates* trust; it does not remove it. +- **"C4 is closed."** It is closed for an account whose identities the desktop + application keeps (browser access off, §3.7): nothing of them is on any node. + For an account with browser access the bundle is still on each node, sealed + under the passphrase **and** a pepper the hub holds — no operator can search it + offline, but an active hub can, and that is T3's adversary already. **An account + is only as strong as its weakest client.** + --- ## 14. Decisions that are not revisited @@ -3909,7 +3887,7 @@ superseded document kept under `docs/`, a note somebody holds elsewhere. | Cited as | Read | |---|---| -| `draft-v5 §2`, `draft-v6 §4` — security claims | §2.2 | +| `draft-v5 §2`, `draft-v6 §4` — security claims | §13.9 | | `draft-v5 §3` — transport, NAT traversal | §5.1 | | `draft-v5 §4`, §4.1–4.4 — handshake, transcript, channel binding, mutual auth | §5.2 | | `draft-v5 §5.1`, `draft-v6 §2.3`, `§2.4b` — privileged operations, key activation, authorship | §5.4 | |