This document provides a technical overview of the cryptographic components used by the Secure QR Code Tool. The goal is to make it straightforward for auditors and integrators to understand the primitives that underpin the application and how they interact with each other.
- Default primitive – Argon2id with parameters sourced from
AppConfig.argon2_time_cost– default 3 iterations.argon2_memory_cost_kib– default 128 MiB of memory.argon2_parallelism– default parallelism of 2 lanes.
- Compatibility primitive – PBKDF2-HMAC-SHA256 remains available for
environments that lack Argon2 or that need deterministic parameters for
legacy payloads. The iteration count is still configurable through
AppConfig.pbkdf2_iterations(default 600,000). - Salt – Random 128-bit value generated with
os.urandomfor every encryption operation. - Output size – 256-bit key used directly by the AES-256-GCM cipher.
Key derivation happens inside CryptoManager._derive_key. Argon2id is used for
new payloads, while the decryptor recognises and transparently handles older
PBKDF2-based payloads. The derived key material never leaves the scope of the
encrypt/decrypt helpers.
- Cipher – AES in Galois/Counter Mode (AES-256-GCM) via
cryptography.hazmat.primitives.ciphers.aead.AESGCM. - Nonce – Random 96-bit value generated using
os.urandom. - Associated data – A deterministic JSON document binding the cipher, protocol version and selected KDF. Any modification to these fields causes the authentication check to fail, preventing downgrade and mix-and-match attacks.
- Output payload – JSON-serialisable dictionary containing base64-encoded salt, nonce and ciphertext as well as the application version. The same components are also packed into a compact binary structure for QR codes (see QR Code Payloads and Hashing below).
AES-GCM provides confidentiality and integrity. The authentication tag is
embedded in the ciphertext returned by AESGCM.encrypt, ensuring tamper
detection during decryption.
- Mnemonic standard – BIP-39 via the
mnemoniclibrary. - Strength – Configurable (default 256 bits, producing 24-word phrases).
- Validation – Delegated to
Mnemonic.check. - Checksum helper – The
MnemonicManager.checksummethod computes the first six hexadecimal characters of the SHA-256 digest of the mnemonic. The checksum is intended to help users visually detect transcription errors when recording phrases offline.
QR codes are generated with the optional segno
package. The QRCodeManager.save_png helper serialises encryption payloads to
PNG images. After encoding the payload it returns a checksum generated by
QRCodeManager.payload_digest:
- Serializer –
secure_qr_tool.payload.encode_componentspacks the format version, KDF selection, and raw salt/nonce/ciphertext segments using big-endian length prefixes. QR images therefore contain raw bytes instead of textual JSON while remaining backwards compatible with legacy payloads. - Hash function – SHA-256 applied to the binary QR payload.
- Digest format – Lowercase hexadecimal string.
The checksum can be stored alongside the QR image or displayed to the user.
When a QR code is scanned, recomputing the digest on the decoded binary payload
and comparing it against the recorded value provides a simple integrity check
before attempting decryption. When exporting payloads as .json files the
tool still emits the JSON dictionary to preserve interoperability with older
installations.