Password-protected transfers with Safe Send

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

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.

SituationProtected by Safe Send?Why
Someone photographs the sending screenYesthe frames carry ciphertext; without the passphrase the bytes are opaque
Someone modifies or injects frames mid-streamYes, and detectedAES-GCM authentication rejects altered bytes instead of decoding them
A bystander points a camera at the stream without the passphraseYesthe key comes from the passphrase, not from the stream
Someone guesses a weak passphrase offlineNothe ciphertext is public on screen; only the cost of key derivation slows guessing
Malware on either endpointNothe 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 threadNoan 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

  1. 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.
  2. 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.
  3. Pick the file. Encryption happens on your device immediately, and from that point the payload handed to the QR encoder is ciphertext.
  4. 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.
  5. 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

SymptomLikely causeWhat to do
Receiver reports an incorrect passwordTypo, wrong capitalisation, a trailing space, or a different passphrase from the one used to encryptRetype 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 successfulFrames were altered or replaced in flight — glare, moiré, or a stray frame from another transferReposition 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 forgottenThe key exists only as a derivation of the passphrase, and nothing is stored that could reconstruct itUnrecoverable 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 slowlyThe payload is the whole file plus a small encryption overhead, so there is more ciphertext than plaintext and the scan runs longerRaise 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

References