Direct answer
Safe Send encrypts your file with a key derived from a passphrase before the first QR frame is drawn, so the optical stream carries AES-GCM ciphertext: a photograph of the screen captures scrambled bytes. The receiver enters the same passphrase to decrypt, and that passphrase must reach them over a different channel.
Key takeaways
- Safe Send encrypts the file on the sending device before encoding, so every frame of the QR stream carries ciphertext rather than file bytes.
- Encryption is AES-GCM through the browser's Web Crypto API: authenticated encryption, so an altered stream fails to decrypt instead of producing plausible rubbish.
- It protects confidentiality against anyone watching the optical channel, and integrity against tampering in flight.
- It does not protect a weak passphrase, a compromised endpoint, or a passphrase shared over the same channel as the screen.
- Key derivation is the soft spot in any scheme like this. QR Send uses PBKDF2-HMAC-SHA-256 at 100,000 iterations, which makes each guess cost real CPU time — and that is exactly why a long passphrase beats a clever short one.
- A forgotten passphrase is unrecoverable by design: nothing is stored that could reconstruct the key.
What "encrypted before encoding" means for the stream
A normal send hands the raw file bytes to the QRTP fountain encoder, which chops them into packets and streams them as QR frames. Safe Send inserts one stage in front of that pipeline: the whole file is encrypted first, and the ciphertext becomes the payload.
QRTP does not care what the bytes mean. To the protocol the payload is an opaque blob, so the frame layout, the 368-byte large packets, the 40-byte small packets, and the fountain block size are all unchanged. Only the contents of each packet differ. That is the whole trick: there is no plaintext of your document anywhere in the stream, so there is nothing on the screen to photograph.
contract.pdf
│ passphrase ──► derived key
▼
AES-GCM ciphertext ──► QRTP fountain encoder ──► QR frame, QR frame, QR frame, …
│ (camera)
▼
ciphertext ──► same passphrase ──► contract.pdf
The passphrase never travels in the stream. If it did, an observer who captured the frames would hold both halves of the secret, and the encryption would buy nothing.
What Safe Send protects — and what it does not
Safe Send is a confidentiality and integrity control for the optical hop. It is not endpoint security, and it is not a substitute for a strong passphrase.
| Situation | Protected by Safe Send? | Why |
|---|---|---|
| Someone photographs the sending screen | Yes | the frames carry ciphertext; without the passphrase the bytes are opaque |
| Someone modifies or injects frames mid-stream | Yes, and detected | AES-GCM authentication rejects altered bytes instead of decoding them |
| A bystander points a camera at the stream without the passphrase | Yes | the key comes from the passphrase, not from the stream |
| Someone guesses a weak passphrase offline | No | the ciphertext is public on screen; only the cost of key derivation slows guessing |
| Malware on either endpoint | No | the file is plaintext on the sender before encryption and on the receiver after decryption |
| Passphrase typed next to the QR screen, or sent in the same message thread | No | an observer gets the key and the ciphertext together |
If your threat model is a listener on a radio link rather than a camera in the room, the comparison in optical vs radio transfer security covers why a unidirectional light path is a different attack surface from Bluetooth or Wi-Fi Direct in the first place.
The workflow, end to end
- Choose Safe Send. On the send page, pick the Safe Send path rather than the standard file send. This is the flow that asks for a password before it ever touches your file.
- Set the password. Use a long passphrase — four or five unrelated words is stronger and easier to dictate than a short password with substitutions. There is no server-side password record and no reset.
- Pick the file. Encryption happens on your device immediately, and from that point the payload handed to the QR encoder is ciphertext.
- Send the passphrase over a different channel. Say it across the room, call, or use a separate messaging thread. Never type it on, or beside, the screen that is showing the QR stream.
- Have the receiver enter the same passphrase. The receive page prompts for the password, and only then is the transfer decoded and the file shown. Until the passphrase matches, the receiver holds ciphertext and nothing else.
The transport tuning is unaffected — the encryption stage sits before the encoder, so throughput and frame density behave exactly as on a standard send. If you drive the optical link from a terminal instead of a browser, the flags that govern the stream are the ones in the QR Send CLI guide:
./qrsend --fps 15 --cols 12 --rows 8 --packets-per-qr 4 --label contract.pdf ./contract.pdf
In a room where one screen serves many receivers — a shared display, a kiosk, a workshop bench — assume several phones are pointed at it at once; the office QR kiosk pattern is exactly the case where you want the passphrase travelling by a route that has nothing to do with the screen.
Why the passphrase is the weak point
Everything else in this scheme is standard, well-reviewed cryptography: AES-GCM for encryption and Web Crypto for the implementation. The part with fewer bits is you.
The ciphertext is public in the most literal sense — it is painted on a screen and can be captured by any camera in the room. An attacker with a recording can therefore test candidate passphrases offline, at whatever rate their hardware allows, with no attempt limit and no lockout. A six-character password that would survive a login form because of rate limiting is worthless here.
That asymmetry is why passphrase-based key derivation gets hardened with a deliberately slow function. QR Send derives the key with PBKDF2-HMAC-SHA-256 at 100,000 iterations; making each guess expensive is the only lever available once the ciphertext is in the attacker's hands. It also explains the practical advice: length beats cleverness. Substituting 0 for o adds almost nothing against a machine, while adding two random words adds a large multiple of guessing work.
Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Receiver reports an incorrect password | Typo, wrong capitalisation, a trailing space, or a different passphrase from the one used to encrypt | Retype it carefully; the derivation has to match exactly, so a single character difference fails. The receiver can be prompted again without rescanning |
| Decryption fails after the scan looked mostly successful | Frames were altered or replaced in flight — glare, moiré, or a stray frame from another transfer | Reposition the camera and rescan. Fountain coding recovers missing frames, but authenticated encryption refuses modified ones, so this failure is the protection working |
| The password is forgotten | The key exists only as a derivation of the passphrase, and nothing is stored that could reconstruct it | Unrecoverable by design. Start a new transfer with a fresh passphrase and store it in a password manager before you send |
| A very large file streams slowly | The payload is the whole file plus a small encryption overhead, so there is more ciphertext than plaintext and the scan runs longer | Raise frame density (--fps, --cols, --rows, --packets-per-qr), keep both devices steady, use a larger display, or let the transparent WebRTC upgrade take over when both ends have a network |
FAQ
Is a QR code file transfer encrypted?
Only if you ask it to be. A standard send streams the raw file bytes into the QR stream; Safe Send encrypts the file first, so the frames carry ciphertext. If confidentiality matters, use Safe Send — the optical channel itself does not add encryption.
Can someone photograph my screen and read the file?
No. They capture AES-GCM ciphertext, which is useless without the passphrase. The same applies to a recording: the stream is a valid sequence of ciphertext, not a set of images of your document.
What happens if the receiver types the wrong password?
Decryption fails and nothing is displayed. Because AES-GCM is authenticated encryption, a wrong key does not produce a partially correct or corrupted file — it produces nothing. The receiver can retype and try again without repeating the scan.
Can a forgotten password be recovered?
No, and that is deliberate. The key is derived from the passphrase and no copy of the passphrase or the file is kept anywhere, so there is no recovery path, no reset link, and no support override. Write the passphrase down or store it in a password manager before sending.
Does encryption slow the transfer down?
Not meaningfully. The encrypted payload is only slightly larger than the file, and the frames themselves are the same size, so the receiver needs roughly the same number of QR captures as an unprotected send of the same data.
Related reading
- Optical vs radio file transfer: the attack surface — Radio links radiate in all directions and are observable at a distance. An optical link sends bytes only where the camera can see.
- QR Send CLI guide for power users — The qrsend binary renders QR streams straight in your terminal, with flags for frame rate, grid size, error correction, colours and auditing.
- One screen, many receivers: QR Send in an office — Leave a QR stream running on a screen and let anyone in the room pull the file over WiFi or optically, whenever they arrive.