Security whitepaper
Version 1.0 · 28 July 2026 · CC-BY 4.0
Version 1.0 · 2026-07-28 · CC-BY 4.0
Abstract
OpaqueShare is a file-transfer service where the operator provably cannot read the content it stores or forwards. Files are encrypted on the sender's device with a per-transfer key sealed to the recipient's long-lived identity key (app mode) or shared out-of-band via a URL fragment (link mode). The server sees ciphertext, a byte-count, and a hash; it never learns filename, MIME type, plaintext length, or a way to construct the decryption key. The client verifies content integrity via a rolling SHA-256 and sender authenticity via an Ed25519 signature over that hash, both bound to the recipient's declared trust in the sender's public keys. This paper describes the protocol, its threat model, and its explicit non-goals. It is written for readers who want to verify the cryptographic claims rather than take them on trust; client-side design decisions cite the corresponding architectural-decision records (ADRs), which are published alongside the client source.
1. Introduction and motivation
The word "encrypted" has been stretched thin in the file-transfer
market. When WeTransfer or Dropbox advertise encryption, they mean
their servers use disk-at-rest encryption and TLS on the wire - a
provider running SELECT against a customer database can still read
every file. In regulated contexts - legal discovery, medical
records, journalism, incident response - that guarantee is often
insufficient. What is needed is end-to-end: the file is unreadable
to any party the sender did not intend to reach, including the
operator, including under compelled disclosure.
End-to-end encryption is well understood for real-time messaging, where Signal's Double Ratchet [1] has become the reference design. It maps awkwardly to file transfer for three reasons. First, files are one-shot rather than continuous — forward secrecy per-message is not naturally per-file. Second, recipients frequently have no existing account with the sender's provider — link-mode delivery is a first-class use case, not an edge case. Third, files are larger than messages by orders of magnitude — streaming, integrity, and resumability need to work on multi-gigabyte payloads on consumer-grade devices.
Firefox Send addressed some of these (link-mode) with in-browser decryption via URL fragments and closed in 2020 after abuse-report handling proved too costly to sustain [2]. The lesson is not that end-to-end file transfer is unworkable; it is that abuse handling and content integrity are load-bearing pieces of the design that must be built in, not bolted on.
OpaqueShare is a zero-knowledge file-transfer protocol with app-mode and link-mode delivery, streaming send and receive, per-transfer ciphertext hashing, sender-signature verification, and a cooperative abuse-report mechanism that operates over ciphertext hashes without compromising the encryption of unrelated transfers. The design principle it optimises for is minimum server knowledge: every decision is measured against what it forces the server to learn.
This paper covers the v1 protocol. §2 states the threat model. §3 describes the cryptographic architecture. §4 walks the send and receive flows. §5 covers the trust model. §6 enumerates what the server can and cannot see. §7 describes the data lifecycle. §8 covers the additional protections. §9 compares the design to related work. §10 lists explicit non-goals and the v2 roadmap. §11 collects references.
2. Threat model
The adversaries below are listed in decreasing order of resources we expect the design to defeat.
2.1 Honest-but-curious server operator
Someone with legitimate root access to the server host, a sysadmin, a database administrator, a stack operator, who wants to read stored files or reconstruct who-sent-what-to-whom without overtly breaking rules. This is the primary adversary the protocol is designed against. We defeat this adversary by ensuring the server never possesses the material required to decrypt.
2.2 Compromised server or compelled disclosure
An attacker who has either taken control of the server host or compelled the operator to hand over everything the server has lawful access to. This is the same adversary as §2.1 with the gloves off. Our defence is the same: absence of decryption material in the server. What such an attacker gains is metadata — sender and recipient account identifiers, byte counts, timestamps, and ciphertext-object references. They do not gain the ability to decrypt in-flight or archived transfers.
2.3 Passive network adversary
An attacker who can observe TLS-encrypted traffic to and from the server but cannot decrypt it. This adversary sees connection timing, packet sizes, and hostnames via SNI. We do not defend against traffic-analysis attacks in v1 — size padding was considered and deferred. The threat model acknowledges this honestly: an attacker who logs enough sender ⇄ recipient pairs and file sizes can infer relationships that the plaintext would confirm.
2.4 Active network adversary and TLS MITM
An attacker who can intercept and alter TLS-encrypted traffic. In
practice this requires either a rogue certificate authority in the
user's trust store or a compromised endpoint. Our defence layers on
top of standard TLS: certificate pinning at the client is planned
(v2 roadmap) but not shipped in v1. The client does verify the
sender's identity public key via an out-of-band fingerprint
mechanism (§5); an active MITM cannot forge a valid sender signature
over a substituted blob_sha256 without also holding the sender's
Ed25519 signing key, which never leaves the sender's device.
2.5 Malicious sender or malicious recipient
A user who is themselves inside the cryptographic envelope. This is the weakest adversary and the one the design explicitly does not try to defeat: a recipient who receives a file can obviously read it, screenshot it, and share it further. A sender can send whatever they choose to send. What the protocol does provide, even here, is non-repudiation: the sender's Ed25519 signature over the ciphertext hash proves to the recipient which key material was used, and via the sender-public-key mechanism (§5) the recipient can bind that to a durable identity.
2.6 Endpoint compromise
Explicitly out of scope. If the sender's or recipient's device is compromised (hostile OS, keylogger with root, malicious browser extension) the file is compromised. The design does not attempt to defend the plaintext once it has left the sodium primitive on either end. This is documented as a limitation, not a defence-in-depth opportunity to be hardened around: attempting to would inflate complexity without meaningfully changing the adversary's economics.
2.7 Store-now-decrypt-later (quantum)
A resourced attacker who records ciphertext today with the intent of decrypting once large-scale fault-tolerant quantum computers become available. Curve25519 and Ed25519 are both broken by a sufficiently capable quantum adversary via Shor's algorithm. This risk is acknowledged: the v2 roadmap discusses the migration path to a hybrid classical-plus-post-quantum construction. v1 ships without post-quantum protection because the primitives available in stable libsodium do not yet include a settled Kyber/Dilithium implementation, and shipping a home-grown one would be worse than the status quo.
3. Cryptographic architecture
The protocol composes three layers of key material: (1) long-lived per-user identities; (2) per-transfer symmetric keys, and (3) per-chunk AEAD tags. Each of these addresses a distinct property.
3.1 Per-user long-lived keys
Every registered user has two keypairs generated on their device at first registration:
- Identity keypair (X25519). Used with libsodium's
crypto_box_sealto receive per-transfer symmetric keys. The public half is uploaded to the server; the private half never leaves the device. - Signing keypair (Ed25519). Used to sign the SHA-256 hash of the ciphertext container so recipients can verify authenticity. Same asymmetry; public half on the server, private half on the device only.
Storage on the device is platform-native: Keychain on iOS,
Keystore-backed Encrypted SharedPreferences on Android, IndexedDB
wrapped by an origin-scoped AES key held in SubtleCrypto on web.
Each installed device slot is per-user: a device with multiple
accounts holds separate keypairs under separate slots (client
ADR-0011) rather than a single overwritable slot.
Key rotation is not automatic in v1. A device that loses its identity keypair loses the ability to decrypt any transfer sealed to it; there is no cloud backup. The client warns loudly on the verify-contact flow when a contact's public key differs from what was previously verified out-of-band (§5).
3.2 Per-transfer symmetric keys
Every transfer generates a fresh 32-byte key — K_file — via
libsodium's crypto_secretstream_keygen. This key is used exactly
once, for one transfer, and is discarded on the sender's device
after the send completes.
In app mode, K_file is sealed to the recipient's identity public
key via crypto_box_seal and travels through the server as the
wrapped_key field on the transfer envelope. The server sees the
sealed bytes; only the recipient's identity private key can unseal
them.
In link mode, K_file is base64url-encoded and appended to the
share URL as a fragment: https://opaqueshare.com/r/<id>#<K_file>.
Browsers do not send URL fragments in HTTP requests, so the server
never sees K_file for link-mode transfers. The recipient's browser
extracts the fragment client-side and passes it into the decrypt
routine locally.
Neither mode grants the server the material to decrypt.
3.3 Content encryption — the OS4S container
File bytes are encrypted with libsodium's
crypto_secretstream_xchacha20poly1305 [3], which provides a
streaming AEAD with a randomly chosen per-encryption 24-byte header
and 17-byte AEAD tags on each 64 KiB chunk. The wire format is:
[4-byte magic prefix: 'O','S','4','S']
[24-byte secretstream header]
[chunk_1: up to 64 KiB plaintext + 17-byte AEAD tag]
[chunk_2: ...]
...
[chunk_N: last chunk, tag = FINAL]
Every chunk is authenticated with a Poly1305 tag over the ciphertext and its position in the stream, so truncation, reordering, and byte-level tampering are all detected. The FINAL tag on the last chunk defends against truncation attacks that would otherwise be undetectable at the AEAD layer.
The 64 KiB plaintext-chunk size (client ADR-0003) is chosen for two reasons. It is small enough that memory usage during encryption and decryption stays proportional to one chunk regardless of file size - multi-gigabyte transfers work on 4 GB mobile devices. It is large enough that per-chunk overhead (17 bytes) remains under 0.03% of plaintext even on maximally chunked payloads.
3.4 The envelope
Each transfer carries a small AEAD-encrypted metadata blob, the
envelope, or enc_header in code, containing:
filename(UTF-8, sanitised at receive)mime(best-effort, nullable)plaintext_length(int, cross-checked at decrypt)blob_sha256(hex of the ciphertext container's SHA-256)
The envelope is encrypted with K_file using the same AEAD primitive. The server sees the envelope only as an opaque byte string. The recipient decrypts it after unsealing K_file (or extracting it from the URL fragment in link mode), and uses the fields to name the saved file, verify the plaintext-length invariant, and cross-check the hash the server reported.
Alongside the envelope, the sender emits an Ed25519 signature over
the ciphertext hash. That is, Sign(signing_priv, blob_sha256).
The recipient verifies this against the sender's signing public key
(which arrives on the download response, alongside the sender's
identity public key, see §5). A recipient who trusts the sender's
public keys can conclude both that the ciphertext bytes are the
ones the sender emitted (authenticity) and that no intermediary
substituted them (integrity).
3.5 Mode split summary
| Property | App mode | Link mode |
|---|---|---|
| Recipient has account | Yes | No |
| K_file delivery | Sealed to recipient identity_pub via crypto_box_seal, through server |
URL fragment, out-of-band |
| Recipient identity trust | Enforced via fingerprint (§5) | None — anyone with the URL can decrypt |
| Optional password gate | No (recipient is authenticated) | Yes — argon2id-hashed on server |
| Sender signature verification | Yes | Skipped — link-mode sender identity is not exposed [ADR-0010] |
The link-mode password does not weaken the encryption; K_file stays in the URL fragment either way. It is a rate-limited server-side check that fires before the presigned download URL is issued, defeating opportunistic link sharing without granting the server decryption capability.
4. Protocol flow
The send and receive paths are described below. The server never touches file bytes. It issues time-bounded presigned URLs against the cloud storage, and files stream directly from client to the storage provider.
4.1 Send
{ mode, byte_count, recipient_id | link_password_hash } API-->>S: { transfer_id, upload_url(s) } Note over S: Encrypt-stream(source, K_file):
emits ciphertext chunks loop for each part S->>OS: PUT part (presigned URL) OS-->>S: 200 + ETag end Note over S: On stream close:
compute blob_sha256,
build enc_header,
sign blob_sha256 S->>API: POST /transfers/{id}/commit
{ blob_sha256, enc_header, signature,
wrapped_key?, parts? } API->>OS: HEAD storage_key
(verify byte_count) API->>API: Check blocklist by blob_sha256 API-->>S: 200 + { status: uploaded } API->>API: Enqueue recipient notification
Two aspects are worth calling out. First, the per-transfer crypto
metadata (blob_sha256, enc_header, signature, wrapped_key)
lands at /commit, not at /initiate (client ADR-0014 amends
client ADR-0013 on this point). This is because the streaming pipeline
does not know the ciphertext hash until the stream drains, and
forcing the client to buffer the whole file to pre-compute it would
defeat streaming's memory guarantees.
Second, the ciphertext-hash blocklist (see §8.2) fires at
/commit rather than at /initiate for the same reason. The
trade-off is that a banned re-upload wastes storage briefly
before rejection; the TTL sweeper (§7) reclaims it. Blocklist hits
are rare in practice.
4.2 Receive
enc_header, signature, blob_sha256,
sender_identity_pub, sender_signing_pub } R->>OS: GET download_url R-->>R: Stream ciphertext,
compute rolling SHA-256 Note over R: Verify: computed hash == server-reported blob_sha256
(truncation / substitution defence) Note over R: Verify: Ed25519(sender_signing_pub, blob_sha256, signature)
(authenticity — skipped in link mode) Note over R: Unseal K_file via crypto_box_seal_open(wrapped_key,
own identity_priv, own identity_pub)
OR extract from URL fragment (link mode) Note over R: Decrypt enc_header with K_file → filename, mime, plaintext_length Note over R: Stream-decrypt ciphertext with K_file → plaintext
Verify emitted length == plaintext_length R->>API: POST /transfers/{id}/ack API->>API: Enqueue burn (see §7.1) API-->>R: { status: deleted }
Every verification step is client-side. The server never learns
whether hash-verification succeeded or failed — it sees only whether
/ack was called. This is deliberate: a failed verification is a
security event on the recipient's device, not a coordination
problem the server needs to manage.
5. Trust model and verification
Cryptography verifies that a message came from some key. It does not by itself verify that the key belongs to the person the recipient thinks they are talking to. That binding is the recipient's responsibility, and OpaqueShare provides three mechanisms for it.
5.1 Fingerprints
The server surfaces the sender's identity and signing public keys on the download response. The recipient's client recomputes a fingerprint (a Signal-style five-group base-10 digest of both public keys concatenated) and displays it in the receive UI. The sender sees the same fingerprint in their own profile.
Two users who trust that they are talking to each other can compare these fingerprints out-of-band, for example by voice call, video, in person, and record a durable trust decision on their local devices. This is the same TOFU-plus-verification model Signal's Safety Numbers use; the mathematics are identical, only the display format differs.
5.2 Trust on first use, with a hard block on change
When a sender first sends to a recipient (or looks up a recipient by email or handle), the client soft-warns that the fingerprint has not been verified. The user may proceed. On any subsequent send, the client checks whether the recipient's current fingerprint matches what was previously used. A mismatch is a hard block. The user must go through the verify-contact flow again before the send proceeds.
The rationale is asymmetric: sending to a wrong key is silent (the intended recipient never receives; an attacker who owns the wrong key does). Detecting key change proactively is the only way the user learns something is off before content leaves their device.
5.3 Link mode has no fingerprint
Because link-mode transfers have no on-platform recipient identity and the server does not expose the sender's public keys on the link-mode download endpoint, the signature-verification step is skipped for link mode (client ADR-0010). The web decrypt page displays a "sender not verified" banner explicitly. Users who need sender authentication should use app mode.
6. What the server can and cannot see
The following is exhaustive as of v1. New endpoints or storage columns will require this section to be revisited.
6.1 What the server sees
- Sender user id, recipient user id, and mode (app or link).
- Storage key — a random object-store key; not derived from content.
- Byte count — the ciphertext container's total size, as measured by the object store's HEAD after upload.
blob_sha256— the ciphertext container's SHA-256, sent at commit and used for the abuse blocklist.- Timestamps: created, first-downloaded, acked, expired.
enc_headerandsignatureas opaque byte strings.wrapped_key(app mode only) as an opaque byte string sealed and unopenable without the recipient's identity private key.- User account metadata — email and handle, both application-layer encrypted at rest with a per-column AEAD key above database-at-rest encryption. A blind index (HMAC of the plaintext email under a separate key) supports login lookups without plaintext storage.
- Access patterns, which recipient downloaded which transfer at which time.
6.2 What the server does not see
- File content. Encrypted with K_file, which the server never possesses.
- Filename, MIME type, plaintext length. Encrypted inside
enc_headerunder K_file. - Sender or recipient private keys. Generated on-device, never transmitted.
- Link-mode K_file. Lives in the URL fragment; browsers do not transmit fragments in HTTP requests.
- Whether a recipient's device successfully decrypted a given
transfer. The server sees
/ackbut not verification outcomes.
6.3 What the server emits about itself
Three transparency mechanisms make server behaviour verifiable to external observers:
- Admin audit log. Every admin action — takedown, blocklist change, user action, DSAR export is written to an append-only, hash-chained table. Each row contains the SHA-256 hash of the previous row's payload plus its own. Tampering with the log breaks the chain.
- Transparency canary. A periodic operator-signed statement of "we have not received a gag order or compelled-disclosure request in this window" is published to a public log. Absence of a canary in a given window is itself the signal.
- External anchor. The root of the audit-log hash chain is periodically anchored to an external timestamping service. This prevents retroactive log rewriting even by an operator with root.
None of these prove that the server behaves correctly — a compromised operator can lie about anything they emit. What they do provide is detectable misbehaviour: any user who cares can pull the audit log and verify its hash chain against the anchored root, and any observer can note a missing canary.
7. Data lifecycle
Files exist on the object store from /commit (successful
upload) through /ack (recipient signals success) or TTL expiry,
whichever comes first.
7.1 Burn after read
On /ack, the server appends the transfer's storage key to a queue. A dedicated worker process
drains the queue and issues DELETE calls against the object store. The queue is
idempotent in that a repeated ack does not re-enqueue.
The worker is a separate container from the API service so that API request-serving latency is unaffected by burn-queue backpressure.
7.2 TTL sweeper
A second worker runs periodically and reaps two categories of lingering state:
- Transfers in UPLOADED state past their expiry window
(
transfer_ttl_days, default six hours) object-store DELETE, mark EXPIRED. - Multipart uploads that received
/initiatebut never/commitor/abortpastmultipart_orphan_grace_seconds— AbortMultipartUpload on the pending session.
The TTL sweeper is the backstop: the burn worker handles the common case (recipient ack), the sweeper handles crashes, abandonment, and users who close the tab without acking.
7.3 Client-side plaintext handling
The client is responsible for its own plaintext lifecycle.
- On mobile, plaintext lands in a temp file under
getTemporaryDirectory()and is deleted after save-to-user-chosen location or ack, whichever comes first. - On web, plaintext lives in an in-memory Blob and is released on
save (via File System Access API on Chromium browsers, or
<a download>on Firefox and Safari). The browser reclaims the Blob on tab close.
Neither platform holds plaintext on disk longer than the receive session.
8. Additional protections
8.1 Rate limiting
Both per-user and per-IP sliding windows (server
ADR-0019). Unauthenticated endpoints (login, register,
password-reset) are IP-limited; authenticated endpoints are
user-limited. Limits are documented per-endpoint in the API surface
and returned to the client via Retry-After on 429 responses.
8.2 Abuse handling
Reports are first-class: any authenticated recipient can report a
transfer. Admins review reports and can take down a transfer,
which additionally offers to add its blob_sha256 to the
ciphertext blocklist. Blocked hashes reject
future re-uploads of identical ciphertext at /commit, regardless
of which account submits them.
The design constraint is that abuse handling must operate over information the server has such as timestamps, storage keys, hashes, report submissions. The server does not gain the ability to inspect content in the process. Blocklist decisions are made on user reports and human review, not on server-side content inspection.
8.3 Log hygiene
Application logs never contain plaintext PII. Structured logging redacts email, handle, and any other application-layer-encrypted field at the emit point. A dedicated test suite exercises the receive path with fixture PII and asserts none of the fixture strings appear in captured log output.
8.4 GDPR and DSAR
Right-to-erasure requests trigger a tombstone-plus-scrub flow. The user's account row is retained (foreign keys from historical transfers require it) but every PII field is overwritten with a per-row nonce. A signed erasure receipt is returned so the user has evidence the request was honoured.
9. Comparison to related work
The following rubric compares OpaqueShare's design position against existing options. Ratings reflect the systems' stated designs, not audits.
| System | E2E for file bytes | Open protocol | Files-first UX | Recipient identity trust model |
|---|---|---|---|---|
| OpaqueShare | Yes | Yes (this paper) | Yes | TOFU + fingerprint (§5) |
| Signal | Yes | Yes | No (chat-first; file transfer is a Signal message) | Safety numbers |
| WeTransfer / Dropbox Transfer | No | No | Yes | N/A (provider sees plaintext) |
| Firefox Send (2017-2020) | Yes | Yes | Yes | None — link-only |
| Tresorit / SpiderOak | Yes (claimed) | No | Yes | Proprietary |
| MEGA | Yes | Partial | Yes | Account-scoped |
| ProtonDrive share | Yes | Partial | Yes | Account-scoped |
"Open protocol" here means the wire protocol and cryptographic construction are publicly specified — enough for a third party to build a compatible implementation or audit the design. Separately, OpaqueShare publishes its client source code; the server code is not open. Under a zero-knowledge design the client is where the audit-relevant work happens: the server is not trusted with content by construction, and its correctness is instead constrained by the transparency mechanisms in §6.3.
9.1 Signal
The design borrows Signal's Safety Number pattern for fingerprint display and the underlying primitives (X25519, Ed25519, XChaCha20 via secretstream). What OpaqueShare adds is files-first UX: per-transfer keys rather than session-persistent state, link-mode delivery for recipients without accounts, and integrity metadata sized for multi-gigabyte files. What it lacks compared to Signal is forward secrecy at the message level — each K_file is one-shot but identity keys are long-lived.
9.2 Firefox Send (2020, closed)
Firefox Send pioneered the K_file-in-URL-fragment pattern that link-mode inherits. Its closure was driven by abuse handling: the combination of easy sharing, provider-blind content, and no per-user rate limiting made it a preferred vector for malware distribution [2]. OpaqueShare's abuse-report and ciphertext-hash blocklist mechanisms (§8.2) are direct responses to this failure mode. Blocklist decisions are made on user reports, not server-side content inspection, so the zero-knowledge property is preserved.
9.3 Tresorit / SpiderOak
Both make strong end-to-end claims but ship closed-source clients, so users cannot independently verify that the code doing the encryption on their device matches the vendor's stated protocol. Under a zero-knowledge design, the client is the load-bearing surface for that claim, the server, by construction, is not trusted with content, so its source-code status is a lesser concern for confidentiality. OpaqueShare publishes its client source, so a user or auditor can read the exact encryption path that runs on their device against this paper's specification. The server is not open source; its role in the security argument is limited to what §6 enumerates, and the transparency mechanisms in §6.3 exist precisely to make server behaviour auditable without requiring source access.
9.4 MEGA
Backendal, Haller, and Paterson (2022) published a series of
practical cryptanalytic attacks against MEGA's client-side crypto
[4], including a malleability attack that could be turned into a
plaintext-recovery attack under specific server behaviour.
OpaqueShare's design differs in ways relevant to those attacks: it
uses libsodium primitives rather than custom AES-CCM code, it
signs blob_sha256 explicitly (not a derived value that admits
malleability), and it does not depend on server-side unwrapping of
per-file keys (crypto_box_seal is unseal-only on the recipient
side).
The comparison is not "OpaqueShare cannot be broken." It is "OpaqueShare's construction avoids the specific classes of attack that broke MEGA in 2022, and its ADR trail makes future audit against similar attack classes possible."
9.5 ProtonDrive share
Proton's file-sharing product uses OpenPGP-based end-to-end encryption with account-scoped recipient identities. It is a credible alternative for users already inside the Proton ecosystem. OpaqueShare's differences are the streaming primitive (secretstream vs. per-message OpenPGP), link-mode support with no account required, and the open-protocol commitment (this paper).
10. Non-goals and future work
The following are explicit non-goals in v1, listed so users can make informed decisions:
- No forward secrecy per-transfer beyond one-shot K_file deletion.
Identity keys are long-lived; an attacker who compromises both a
recipient's identity_priv and the server's stored
wrapped_keycopies can decrypt historical transfers. Cross-device identity-key rotation is the intended v2 remediation. - No post-quantum crypto. Discussed in §2.7. The v2 target is a hybrid X25519 + Kyber KEM for K_file wrap and hybrid Ed25519 + Dilithium for the signature, once stable libsodium bindings ship.
- No group or broadcast transfers. One sender, one recipient (app mode) or one URL (link mode). Multi-recipient app-mode transfers are on the v2 roadmap; the current sealed-box construction extends naturally by sealing K_file separately to each recipient.
- No cross-device automatic key sync. First login on a new device is a fresh device from the identity-key perspective. This implies that the user must re-register or accept that received transfers cannot be decrypted on that device. QR-code pairing between devices is planned (v2 roadmap; client ADR-0011 covers the initial per-device key architecture).
- No external cryptographic audit yet. Server ADR-0004 documents the deferral criteria and the intended engagement model. This paper is the intermediate artifact: a specification against which an audit can be scoped.
- No metadata-privacy protections beyond TLS. Size padding and cover traffic are v2+ considerations. In v1, an adversary who observes the network can learn transfer timings and sizes; the protocol prevents them from learning content.
11. References
Cryptographic primitives:
- Bernstein, D. J. (2006). Curve25519: New Diffie-Hellman Speed Records. Public Key Cryptography — PKC 2006.
- Bernstein, D. J., Duif, N., Lange, T., Schwabe, P., & Yang, B.-Y. (2012). High-speed high-security signatures. Journal of Cryptographic Engineering.
- RFC 7748 — Elliptic Curves for Security (X25519, X448).
- RFC 8032 — Edwards-Curve Digital Signature Algorithm (EdDSA).
- RFC 9106 — Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications.
- Bernstein, D. J. (2008). ChaCha, a variant of Salsa20.
- Bernstein, D. J. (2013). Extending the Salsa20 nonce. (XChaCha20 rationale.)
- Denis, F. (2013–2026). libsodium. https://libsodium.gitbook.io/
Prior art:
- [1] Marlinspike, M., & Perrin, T. (2013). The Double Ratchet Algorithm. Open Whisper Systems.
- [2] Mozilla (2020). Update on Firefox Send and Firefox Notes. Blog post announcing discontinuation.
- [3] Denis, F. crypto_secretstream_xchacha20poly1305. libsodium documentation.
- [4] Backendal, M., Haller, M., & Paterson, K. G. (2022). MEGA: Malleable Encryption Goes Awry. IEEE Symposium on Security and Privacy.
OpaqueShare ADRs cited in this paper:
Client repo (public):
- ADR-0003 — OS4S ciphertext container.
- ADR-0004 — Streaming send.
- ADR-0006 — Streaming receive.
- ADR-0010 — In-app link decrypt (link-mode receive on the Flutter app).
- ADR-0011 — Per-user secure storage.
- ADR-0013 — Web send/receive streaming.
- ADR-0014 — Metadata deferred to
/commit.
Server-side design records are internal to the project. The security-relevant behaviour of the server — what it sees, what it does not see, the transparency mechanisms that make its actions auditable — is fully described in §6 of this paper; a reader who wants to verify a claim can do so against the paper itself. Access to the internal design records is available under NDA on request; contact the project.
License
This paper is licensed under Creative Commons Attribution 4.0 International. You may share and adapt it — including commercially — with attribution.
Changelog
- v1.0 · 2026-07-28 — Initial publication covering the v1 protocol. where