MSC4382: Peppered hash verification for E2EE content moderation#4382
MSC4382: Peppered hash verification for E2EE content moderation#4382seuros wants to merge 7 commits intomatrix-org:mainfrom
Conversation
There was a problem hiding this comment.
Implementation requirements:
- Client
- Server
- Use ciphertext as pepper instead of hashes.sha256 - Move verification_hash into content (clients can set it) - Specify base64 encoding for hash output - Specify canonical JSON encoding for plaintext - Fix size calculation: 43 bytes base64 + overhead - Wrap lines to 80 characters
| ### Verification Process | ||
|
|
||
| When a user reports encrypted content, the server verifies: | ||
|
|
||
| ```python | ||
| plaintext_event = canonical_json(report['plaintext']) | ||
| ciphertext = event['content']['ciphertext'] | ||
|
|
||
| computed = base64(sha256(plaintext_event + ciphertext)) | ||
|
|
||
| if computed == event['content']['verification_hash']: | ||
| # Report verified - plaintext is authentic | ||
| else: | ||
| # Report is false - reporter is lying | ||
| ``` |
There was a problem hiding this comment.
At the moment this ciphertext "pepper" is not helping and is salt because it is public and we can reproduce the hash and start making guesses.
Instead a secret pepper should be embedded in the plaintext event content, and that way it can only be accessed when you forward the entire plaintext event in a report (or decrypt the event).
There was a problem hiding this comment.
The Problem I'm Solving:
I run servers where users get brigaded, 99 reports against one person based on their identity or something they said in a private room. As a server admin, I face an impossible choice:
- Believe the mass (potentially unjust)
- Ask the reported user for their private keys (unacceptable)
- Ignore reports entirely (enables actual abuse)
Simple hash(plaintext) enables rainbow tables.
With unique ciphertext per message, checking if "buy crypto" appears in 1 million messages requires 1 million fresh hash computations. This doesn't scale for surveillance but works perfectly for verification when I receive a report with claimed plaintext.
When I receive a report, I have:
- event_id (which message is being reported)
- claimed_plaintext (what reporter says it says)
I fetch from my database:
- ciphertext (already stored in the encrypted event)
- verification_hash (already stored in the encrypted event)
Then I compute: hash(claimed_plaintext || ciphertext)
If it matches the stored verification_hash, the report is authentic.
Alternative i considered we could pepper with user_id: hash(plaintext || user_id)
Your secret pepper approach is more robust cryptographically, but we're not protecting against post-quantum super computers.
We are solving a practical moderation problem: verifying report authenticity without breaking E2EE.
I'm open to changing the design if there is a use case or security concern that the current approach doesn't address.
What specific attack scenario are you protecting against that per-message public pepper doesn't prevent?
8efdf32 to
8a835ab
Compare
Rendered
This proposal adds a
verification_hashfield to encrypted events to enable cryptographic verification of reported content without breaking E2EE.Uses SHA-256(plaintext || ciphertext_hash) construction where the ciphertext hash acts as a unique per-message pepper, preventing rainbow tables and mass surveillance while enabling moderation of reported messages.