Small and portable AES encryption library for Qt
AES-128 · AES-192 · AES-256 | ECB · CBC · CFB · OFB · CTR | PBKDF2 key derivation | Partial AES-NI support
- All AES key sizes — 128, 192, 256 bit
- Five cipher modes — ECB, CBC, CFB, OFB, CTR
- Three padding schemes — ISO (default), PKCS7, ZERO
- PBKDF2-HMAC key derivation (RFC 2898) — no QtNetwork required
- Optional hardware acceleration via AES-NI (ECB and CBC)
- Qt 5 and Qt 6 compatible
- Single dependency:
QtCore
cmake -B build -DCMAKE_PREFIX_PATH=/path/to/Qt -DCMAKE_INSTALL_PREFIX=/path/to/install
cmake --build build
cmake --install buildEnable optional features:
cmake -B build \
-DQTAES_ENABLE_AESNI=ON \ # Hardware AES-NI acceleration (all modes)
-DQTAES_ENABLE_TESTS=ON \ # Build unit tests
-DQTAES_ENABLE_WERROR=ON \ # Treat warnings as errors
-DQTAES_ENABLE_SANITIZERS=ON \ # AddressSanitizer + UBSan (GCC/Clang only)
-DQTAES_ENABLE_FUZZING=ON # libFuzzer fuzz target (Clang only)In your CMakeLists.txt:
find_package(QtAES REQUIRED)
target_link_libraries(your_target PRIVATE QtAES::QtAES)Then include as you would any Qt class header:
#include <QAESEncryption>Pass the install prefix to CMake so find_package can locate the library:
cmake -B build -DCMAKE_PREFIX_PATH=/path/to/installAlternatively, copy the source tree into your project and use add_subdirectory:
add_subdirectory(Qt-AES)
target_link_libraries(your_target PRIVATE QtAES::QtAES)| Method | Description |
|---|---|
encode(rawText, key, iv, ok) |
Encrypt rawText with key. iv required for CBC/CFB/OFB/CTR. Optional bool *ok set to false on invalid key/IV. |
decode(rawText, key, iv, ok) |
Decrypt rawText with key. iv required for CBC/CFB/OFB/CTR. Optional bool *ok set to false on invalid input. |
removePadding(rawText, ok) |
Strip padding. Optional bool *ok set to false if PKCS7 padding is invalid. |
All ok parameters default to nullptr — existing code requires no changes.
| Method | Description |
|---|---|
QAESEncryption::Crypt(..., ok) |
Static encrypt — no instance needed. Optional bool *ok. |
QAESEncryption::Decrypt(..., ok) |
Static decrypt — no instance needed. Optional bool *ok. |
QAESEncryption::RemovePadding(..., ok) |
Static padding removal. Optional bool *ok. |
QAESEncryption::ExpandKey(...) |
Static key expansion — advanced use; encode/decode handle this internally. |
QAESEncryption::generateKey(password, salt, level, algo, iterations) |
Derive an AES-ready key via PBKDF2-HMAC (see below). |
QAESEncryption(Aes level, Mode mode, Padding padding = ISO);Supported values
| Enum | Values |
|---|---|
QAESEncryption::Aes |
AES_128, AES_192, AES_256 |
QAESEncryption::Mode |
ECB, CBC, CFB, OFB, CTR |
QAESEncryption::Padding |
ISO (default), PKCS7, ZERO |
#include "qaesencryption.h"
QAESEncryption encryption(QAESEncryption::AES_128, QAESEncryption::ECB);
QByteArray encoded = encryption.encode(plainText, key);
QByteArray decoded = encryption.decode(encoded, key);Use generateKey() to derive a secure key from a password and a random salt.
Store the salt (and IV) alongside the ciphertext — they are not secret.
#include "qaesencryption.h"
QAESEncryption encryption(QAESEncryption::AES_256, QAESEncryption::CBC, QAESEncryption::PKCS7);
QByteArray salt = /* random 16+ bytes from a CSPRNG */;
QByteArray iv = /* random 16 bytes */;
QByteArray key = QAESEncryption::generateKey(password.toUtf8(), salt,
QAESEncryption::AES_256);
QByteArray cipherText = encryption.encode(plainText.toUtf8(), key, iv);
// Decrypt — re-derive the same key from the stored salt:
QByteArray key2 = QAESEncryption::generateKey(password.toUtf8(), salt,
QAESEncryption::AES_256);
QByteArray decrypted = encryption.removePadding(encryption.decode(cipherText, key2, iv));Note
generateKey() uses Qt's QMessageAuthenticationCode, which is not guaranteed to be
constant-time. For security-critical applications prefer a dedicated library such as
OpenSSL (PKCS5_PBKDF2_HMAC) or libsodium.
#include <QCryptographicHash>
#include "qaesencryption.h"
QAESEncryption encryption(QAESEncryption::AES_256, QAESEncryption::CBC);
QByteArray hashKey = QCryptographicHash::hash(key.toLocal8Bit(), QCryptographicHash::Sha256);
QByteArray hashIV = QCryptographicHash::hash(iv.toLocal8Bit(), QCryptographicHash::Md5);
QByteArray encoded = encryption.encode(inputStr.toLocal8Bit(), hashKey, hashIV);
QByteArray decoded = encryption.decode(encoded, hashKey, hashIV);
QString result = QString(encryption.removePadding(decoded));#include <QCryptographicHash>
#include "qaesencryption.h"
QByteArray hashKey = QCryptographicHash::hash(key.toLocal8Bit(), QCryptographicHash::Sha256);
QByteArray hashIV = QCryptographicHash::hash(iv.toLocal8Bit(), QCryptographicHash::Md5);
QByteArray encrypted = QAESEncryption::Crypt(QAESEncryption::AES_256, QAESEncryption::CBC,
inputStr.toLocal8Bit(), hashKey, hashIV);
QString decrypted = QString(QAESEncryption::RemovePadding(
QAESEncryption::Decrypt(QAESEncryption::AES_256, QAESEncryption::CBC,
encrypted, hashKey, hashIV)));Instances are thread-safe. All mutable state during an encode() / decode() operation is kept on the call stack — no member variables are written after construction. Multiple threads may safely call encode() or decode() on the same instance concurrently without a mutex.
The static methods (Crypt, Decrypt, ExpandKey, RemovePadding, generateKey) are also safe to call concurrently.
On x86/x86-64 CPUs that support the AES-NI instruction set, Qt-AES can use native hardware instructions for a significant throughput improvement over the pure software implementation.
Note
AES-NI is only supported on x86/x86-64. Enabling it on any other architecture will produce a CMake configure error.
Windows / MSVC: MSVC does not require a separate compiler flag to enable AES-NI intrinsics — they are available by default on x64 targets. The -maes flag check in CMakeLists.txt is a no-op under MSVC, which is expected and correct.
| Mode | Encrypt | Decrypt |
|---|---|---|
| ECB | ✅ | ✅ |
| CBC | ✅ | ✅ |
| CTR | ✅ | ✅ |
| CFB | ✅ | ✅ |
| OFB | ✅ | ✅ |
All five modes are hardware-accelerated. CFB and OFB use the forward AES cipher for both encrypt and decrypt (as the standard requires), so they take the encryption key schedule in both directions.
Pass -DQTAES_ENABLE_AESNI=ON at configure time:
cmake -B build \
-DQTAES_ENABLE_AESNI=ON \
-DQTAES_ENABLE_TESTS=ON \
-DCMAKE_PREFIX_PATH=/path/to/Qt
cmake --build build
ctest --test-dir build -VEven with QTAES_ENABLE_AESNI=ON, the library queries the CPU at runtime via CPUID. If the running CPU does not support AES-NI the library silently falls back to the software implementation — no code changes are required.
AES-NI is entirely transparent to the caller. The same encode() / decode() API is used regardless of whether hardware acceleration is active. Ciphertext produced by the hardware path is identical to the software path and fully interoperable.
Test vectors are taken from NIST SP 800-38A for ECB, CBC, OFB, and CTR modes.
generateKey() is validated against five PBKDF2-HMAC-SHA256 vectors cross-checked with Python's hashlib.pbkdf2_hmac.
cmake -B build -DQTAES_ENABLE_TESTS=ON -DCMAKE_PREFIX_PATH=/path/to/Qt
cmake --build build
ctest --test-dir build -VA libFuzzer fuzz target lives in fuzz/fuzz_encrypt.cpp.
It exercises all five cipher modes, all three key sizes, and all three padding schemes against
randomly mutated inputs, and checks two properties on every input:
- Crash freedom — neither
encode()nordecode()ever crashes or triggers memory errors (AddressSanitizer is always active with-fsanitize=fuzzer). - Round-trip correctness — for PKCS7 padding,
removePadding(decode(encode(pt))) == pt; for CTR mode,decode(encode(pt)) == ptdirectly.
cmake -B build-fuzz \
-DQTAES_ENABLE_FUZZING=ON \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_PREFIX_PATH=/path/to/Qt
cmake --build build-fuzz --target fuzz_encrypt
# Run for 60 seconds, seeding from the provided corpus:
./build-fuzz/fuzz_encrypt fuzz/corpus/ -max_total_time=60The fuzz/corpus/ directory contains seed inputs that cover all mode/level/padding
combinations, giving the fuzzer a head start.
This code is not audited or AES-certified by any competent authority. Use it at your own risk.
Released under the Unlicense — public domain, no restrictions.