Encrypted QR Code Generator

A QR code that keeps its mouth shut.

  • AES-256-GCM sealed
  • Password never leaves
  • Scan and open here
A square ceramic tile with a black and white block mosaic lying inside an open metal cash box with a key in its lock.

The guide

A QR code hides nothing, so the secret has to be sealed before it is drawn

A QR code is plain data in a square. Encrypting first changes what a scanner sees, and moves the whole question of safety onto one thing: the password.

  • 2,953 bytes the most a single QR code holds in byte mode
  • 53 bytes what the sealed container adds to any message
  • 600,000 PBKDF2 iterations for every key, the published figure
  • 60 bits a six-word passphrase from a list of 1,024 words
  • 96 bits the nonce length recommended for Galois/Counter Mode
Read the full guide Fold the guide 7 min · 14 sources

A QR code looks like a secret. It is a square of black and white blocks that no person can read, and that is exactly why people trust it with things they should not. The pattern is not a cipher. It is a way of writing bytes so that a camera can find them, and a camera that can read them is in nearly every pocket. So anything put into a QR code is public to whoever sees it, photographs it or finds the printout in a recycling bin. Content that matters has to be made unreadable before the square is drawn, and one thing decides whether that worked.

What the square really is

The QR code was invented in 1994 to label automobile parts, and it was built for speed and survival on a factory floor, not for privacy. Its international standard, ISO/IEC 18004, defines forty sizes called versions, from 21 modules on a side to 177. The three large squares in the corners tell a camera where the symbol is and which way up.

Inside, the data is protected against dirt and tears, not against readers. The bytes are split into blocks and each block gets extra check bytes computed with Reed-Solomon codes, so that a scanner can rebuild what a scratch destroyed. There are four levels of this protection. The lowest restores about 7 percent of the data bytes and the highest about 30 percent, and a higher level leaves less room for the message.

Room is the hard limit. The largest version at the lowest correction level holds 2,953 bytes in byte mode. That is a few paragraphs, not a document, so a sealed code suits short things.

None of that structure conceals anything. Error correction is the opposite of secrecy: it exists so that the content comes back even when part of the picture is gone.

Sealing before encoding

Encrypting first changes what the scanner gets. The camera still reads every byte perfectly, but the bytes are now the output of a cipher, and without the key they are indistinguishable from noise. The square becomes an envelope that anyone may handle and only one reader can open.

The cipher used here is AES with a 256-bit key. AES is the block cipher standardized by the United States government in FIPS 197, which works on blocks of 128 bits with keys of 128, 192 or 256 bits. A block cipher alone is not enough, though. It needs a mode, a rule for applying it to a message of any length, and the choice of mode decides whether tampering can be noticed.

Plain encryption has a weakness that surprises people. It hides the message, but by itself it gives the reader no way to tell whether the scrambled bytes were changed on the way. A reader would decrypt altered data and never know. Hiding a message and proving it is intact are two separate jobs.

Authenticated encryption does both at once. RFC 5116 describes the interface: a key, a nonce, the plaintext and some associated data go in, and the result either decrypts to the original or fails outright. Galois/Counter Mode, specified in NIST Special Publication 800-38D, is one such scheme. It encrypts with a counter and computes a tag over the ciphertext and over any additional data that should be protected without being hidden.

On opening, the tag is recomputed. If one bit of the ciphertext, the tag or the additional data has changed, the mode returns a failure and no plaintext at all. That is why a well-made tool gives the same answer for a wrong password and for altered data. It cannot tell them apart, and saying more would only help someone who is guessing.

Galois/Counter Mode asks one thing in return: the nonce must never repeat under the same key. The NIST document recommends a nonce of 96 bits and is blunt that reuse can undo the protection almost entirely. The simple way to honour that is to use each key for exactly one message, and to draw the nonce from the device’s cryptographic random generator, which in a browser is the getRandomValues method of the Web Cryptography interface.

The password is the whole strength

A 256-bit key cannot be remembered or said over the phone, so the key is made from a password. This is where the security of the whole arrangement actually lives. An attacker who holds the code does not attack AES. They guess passwords, turn each guess into a key the same way the tool does, and see whether the tag verifies.

That guessing happens on the attacker’s own machines. Nothing rate-limits it, nothing locks after five tries, and nobody is told it is going on. NIST’s guidance on passwords notes that people’s choices are predictable, which is the reason common and expected values are tried first. A short or familiar password protecting a code that someone can keep is not protection at all.

Key stretching makes each guess expensive. PBKDF2, defined in RFC 8018, runs a keyed hash over the password and a random salt again and again, so that deriving one key costs a fixed, large amount of work. NIST Special Publication 800-132 puts the effect plainly: with an iteration count of C, an attack on a password with t bits of unpredictability rises from 2 to the power t operations to C times that.

The salt, which that publication wants to be at least 128 random bits, stops an attacker from doing the work once and reusing it against every code. The count has to be chosen. The same document says to make it as large as users can tolerate, and current published advice for PBKDF2 with HMAC-SHA-256 is 600,000 iterations. That multiplies an attacker’s cost by a factor of about 2 to the power 19.

Nineteen bits is worth having and is not a rescue. It turns a password that would fall in a second into one that falls in a week, and leaves a good one far out of reach. The real gain comes from the password itself. Six words picked at random from a list of 1,024 give 60 bits, because each word is one choice in 1,024, which is 10 bits. Stretching adds its 19 on top.

From a message to a phone

Take the message “The spare key is under the third flowerpot. Back on Friday.” It is 59 bytes. The container that carries it adds exactly 53 bytes: a 4-byte marker, a version byte, the iteration count in 4 bytes, a 16-byte salt, a 12-byte nonce, and after the ciphertext a 16-byte tag. That makes 112 bytes, and the first 37 of them are fed to the cipher as additional data so that none can be altered unnoticed.

Those 112 bytes fit a version 7 symbol at medium error correction, 45 modules on a side. Written as text in the URL-safe base64 alphabet of RFC 4648 they become 150 characters, which need version 8. Placed after the number sign of this page’s address they make a link of 206 bytes and a version 10 code, 57 modules across. The same sealed message, three sizes of square.

The link form exists because of an old and useful rule about addresses. RFC 3986 defines the fragment as the part of a URI after the number sign and says it is separated from the rest before the resource is fetched and handled solely by the user agent. RFC 9110 repeats the point for the web: the target of a request excludes the fragment, and a browser must not include it in a Referer header either. The URL Standard that browsers follow keeps the fragment as a component of its own in a parsed address.

So a phone camera that scans the code opens the page, and the request that reaches the server names the page and nothing more. The sealed data arrives in the browser, where the page reads it from its own address and asks for the password. Decryption happens there, in the Web Cryptography implementation the browser already ships, with the key held where page scripts cannot export it.

The same RFC is careful about the limit of that rule. The fragment is still visible to anything running in the page and to browser extensions, and it stays in the history until removed. What travels in the fragment should therefore be something that is safe to expose, which sealed data is and a password never would be.

What it cannot do

It cannot recover anything. There is no reset link and no master key, and a forgotten password leaves a square of noise that nobody, including the people who wrote the software, can open. That is not a flaw to be engineered away. It is the same property that keeps a stranger out.

It cannot hide that a message exists, or roughly how long it is, since the ciphertext is as long as the plaintext. It cannot protect a message from the person it was meant for, who can copy it once opened. And it cannot make a weak password strong. The code and the password should travel by different routes, because anyone who gets both has everything.

Used within those limits, the arrangement is sound and unusually easy to check. The algorithms are public standards with published test values, RFC 7914 among the places that list them for PBKDF2 with HMAC-SHA-256. The container is a few dozen bytes that can be written on one page. Anyone with a general-purpose cryptography library can open it without this site, given the password and nothing else.

Sources

Every source consulted for this guide. Links checked October 7, 2026.

  1. Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC (NIST Special Publication 800-38D) National Institute of Standards and Technology
  2. Recommendation for Password-Based Key Derivation, Part 1: Storage Applications (NIST Special Publication 800-132) National Institute of Standards and Technology
  3. Advanced Encryption Standard (AES) (FIPS 197) National Institute of Standards and Technology
  4. NIST Special Publication 800-63B National Institute of Standards and Technology
  5. PKCS #5: Password-Based Cryptography Specification Version 2.1 (RFC 8018) Internet Engineering Task Force
  6. An Interface and Algorithms for Authenticated Encryption (RFC 5116) Internet Engineering Task Force
  7. The scrypt Password-Based Key Derivation Function (RFC 7914) Internet Engineering Task Force
  8. Uniform Resource Identifier (URI): Generic Syntax (RFC 3986) Internet Engineering Task Force
  9. HTTP Semantics (RFC 9110) Internet Engineering Task Force
  10. The Base16, Base32, and Base64 Data Encodings (RFC 4648) Internet Engineering Task Force
  11. Web Cryptography Level 2 World Wide Web Consortium
  12. URL Standard WHATWG
  13. Password Storage Cheat Sheet OWASP Cheat Sheet Series
  14. QR code Wikipedia