├── .gitignore ├── LICENSE ├── QTinyAesTest ├── QTinyAesTest.pro └── tst_qtinyaestest.cpp ├── README.md ├── qtinyaes.cpp ├── qtinyaes.h └── qtinyaes.pri /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | 3 | *.slo 4 | *.lo 5 | *.o 6 | *.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.dll 11 | *.dylib 12 | 13 | # Qt-es 14 | 15 | /.qmake.cache 16 | /.qmake.stash 17 | *.pro.user 18 | *.pro.user.* 19 | *.qbs.user 20 | *.qbs.user.* 21 | *.moc 22 | moc_*.cpp 23 | qrc_*.cpp 24 | ui_*.h 25 | Makefile* 26 | *build-* 27 | 28 | # QtCreator 29 | 30 | *.autosave 31 | 32 | #QtCtreator Qml 33 | *.qmlproject.user 34 | *.qmlproject.user.* 35 | 36 | # ========================= 37 | # Operating System Files 38 | # ========================= 39 | 40 | # OSX 41 | # ========================= 42 | 43 | .DS_Store 44 | .AppleDouble 45 | .LSOverride 46 | 47 | # Thumbnails 48 | ._* 49 | 50 | # Files that might appear in the root of a volume 51 | .DocumentRevisions-V100 52 | .fseventsd 53 | .Spotlight-V100 54 | .TemporaryItems 55 | .Trashes 56 | .VolumeIcon.icns 57 | 58 | # Directories potentially created on remote AFP share 59 | .AppleDB 60 | .AppleDesktop 61 | Network Trash Folder 62 | Temporary Items 63 | .apdisk 64 | 65 | # Windows 66 | # ========================= 67 | 68 | # Windows image file caches 69 | Thumbs.db 70 | ehthumbs.db 71 | 72 | # Folder config file 73 | Desktop.ini 74 | 75 | # Recycle Bin used on file shares 76 | $RECYCLE.BIN/ 77 | 78 | # Windows Installer files 79 | *.cab 80 | *.msi 81 | *.msm 82 | *.msp 83 | 84 | # Windows shortcuts 85 | *.lnk 86 | 87 | # qpm 88 | vendor -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Felix Barz 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of QTinyAes nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /QTinyAesTest/QTinyAesTest.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | QT += testlib 4 | QT -= gui 5 | 6 | CONFIG += console 7 | CONFIG -= app_bundle 8 | 9 | TARGET = tst_qtinyaestest 10 | 11 | SOURCES += tst_qtinyaestest.cpp 12 | DEFINES += SRCDIR=\\\"$$PWD/\\\" 13 | 14 | QDEP_DEPENDS += $$fromfile(../qtinyaes.pri, QDEP_DEPENDS) 15 | 16 | !load(qdep):error("Failed to load qdep feature! Run 'qdep.py prfgen --qmake $$QMAKE_QMAKE' to create it.") 17 | 18 | include(../qtinyaes.pri) 19 | -------------------------------------------------------------------------------- /QTinyAesTest/tst_qtinyaestest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define TEST_ROUNDS 128 6 | 7 | class QTinyAesTest : public QObject 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | QTinyAesTest(); 13 | 14 | private Q_SLOTS: 15 | void initTestCase(); 16 | 17 | void testGenerateKey(); 18 | 19 | void testCTR_data(); 20 | void testCTR(); 21 | void testCBC_data(); 22 | void testCBC(); 23 | void testECB_data(); 24 | void testECB(); 25 | 26 | private: 27 | QByteArray generateData(int size); 28 | 29 | void data(); 30 | void test(QTinyAes::CipherMode mode); 31 | 32 | QTinyAes aes; 33 | }; 34 | 35 | QTinyAesTest::QTinyAesTest(){} 36 | 37 | void QTinyAesTest::initTestCase() 38 | { 39 | qsrand(QDateTime::currentMSecsSinceEpoch()); 40 | } 41 | 42 | void QTinyAesTest::testGenerateKey() 43 | { 44 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) 45 | QTinyAes aes(QTinyAes::CTR, 46 | QTinyAes::generateKey(), 47 | generateData(QTinyAes::BlockSize)); 48 | 49 | auto plain = generateData(42); 50 | auto cipher = aes.encrypt(plain); 51 | auto result = aes.decrypt(cipher); 52 | QCOMPARE(plain, result); 53 | #else 54 | qDebug("Not implemented before Qt 5.10"); 55 | #endif 56 | } 57 | 58 | void QTinyAesTest::testCTR_data() 59 | { 60 | data(); 61 | } 62 | 63 | void QTinyAesTest::testCTR() 64 | { 65 | test(QTinyAes::CTR); 66 | } 67 | 68 | void QTinyAesTest::testCBC_data() 69 | { 70 | data(); 71 | } 72 | 73 | void QTinyAesTest::testCBC() 74 | { 75 | test(QTinyAes::CBC); 76 | } 77 | 78 | void QTinyAesTest::testECB_data() 79 | { 80 | data(); 81 | } 82 | 83 | void QTinyAesTest::testECB() 84 | { 85 | test(QTinyAes::ECB); 86 | } 87 | 88 | QByteArray QTinyAesTest::generateData(int size) 89 | { 90 | QByteArray data(size, Qt::Uninitialized); 91 | for(int i = 0; i < size; i++) 92 | data[i] = (char)qrand(); 93 | return data; 94 | } 95 | 96 | void QTinyAesTest::data() 97 | { 98 | QTest::addColumn("plain"); 99 | QTest::addColumn("key"); 100 | QTest::addColumn("iv"); 101 | 102 | for(int i = 0; i < TEST_ROUNDS; i++) { 103 | QTest::newRow(qPrintable(QStringLiteral("size_%1").arg(i))) 104 | << generateData(i * i) 105 | << generateData(QTinyAes::KeySize) 106 | << generateData(QTinyAes::BlockSize); 107 | } 108 | } 109 | 110 | void QTinyAesTest::test(QTinyAes::CipherMode mode) 111 | { 112 | QFETCH(QByteArray, plain); 113 | QFETCH(QByteArray, key); 114 | QFETCH(QByteArray, iv); 115 | 116 | this->aes.setMode(mode); 117 | this->aes.setKey(key); 118 | this->aes.setIv(iv); 119 | 120 | auto cipher = aes.encrypt(plain); 121 | auto result = aes.decrypt(cipher); 122 | 123 | QCOMPARE(plain, result); 124 | } 125 | 126 | QTEST_APPLESS_MAIN(QTinyAesTest) 127 | 128 | #include "tst_qtinyaestest.moc" 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QTinyAes 2 | A Qt-Wrapper for the AES-implementation kokke/tiny-AES-C (supports AES128/192/256) 3 | 4 | This class is simply a wrapper for https://github.com/kokke/tiny-AES-C. It allows to use the simple AES-implementation inside Qt and with Qt's `QByteArray` class. Thanks to recent updates, allowing keys of size 128, 192 and 256. 5 | 6 | ## Features 7 | - It's a C++-class instead of just C-functions 8 | - Easy integration with Qt-Projects thanks to the use of QByteArray 9 | - Allows plain-texts of any size - [PKCS#7 Padding](https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7) is added automatically 10 | - Supports all common AES keysizes (compile-time switch) 11 | - Currently supports CTR, CBC and ECB as modes 12 | 13 | ## Installation 14 | The package is provided via qdep, as `Skycoder42/QTinyAes`. To use it simply: 15 | 16 | 1. Install and enable qdep (See [qdep - Installing](https://github.com/Skycoder42/qdep#installation) 17 | 2. Add the following to your pro file: 18 | ```qmake 19 | QDEP_DEPENDS += Skycoder42/QTinyAes 20 | !load(qdep):error("Failed to load qdep feature! Run 'qdep.py prfgen --qmake $$QMAKE_QMAKE' to create it.") 21 | ``` 22 | 23 | ## Example 24 | ```cpp 25 | QTinyAes aes; 26 | 27 | aes.setMode(QTinyAes::CTR); 28 | aes.setKey("randomkey_256bit");// QTinyAes::KeySize (256 bit key by default) 29 | //or with Qt 5.10 30 | aes.setKey(QTinyAes::generateKey()); 31 | aes.setIv("random_iv_128bit");// QTinyAes::BlockSize (128 iv vector) 32 | 33 | QByteArray plain = "Hello World"; 34 | qDebug() << "plain:" << plain 35 | QByteArray cipher = aes.encrypt(plain); 36 | qDebug() << "cipher:" << cipher; 37 | QByteArray result = aes.decrypt(cipher); 38 | qDebug() << "result:" << result; 39 | ``` 40 | 41 | ## Changing the key size 42 | By default, your keys must be 256 bit keys. However, you can change this size to 192 or 128 if you need to. This can be done via a qmake variable, as the keysize as a **compile time** switch (due to limitations of kokke/tiny-AES-C). 43 | 44 | To change the size, set the `TINYAES_KEYSIZE` qmake variable to the desired keysize *before* loading the qdep feature 45 | 46 | ```pro 47 | TINYAES_KEYSIZE = 128 # or 192 or the default, 256 48 | 49 | QDEP_DEPENDS += Skycoder42/QTinyAes 50 | !load(qdep):error("Failed to load qdep feature! Run 'qdep.py prfgen --qmake $$QMAKE_QMAKE' to create it.") 51 | ``` 52 | -------------------------------------------------------------------------------- /qtinyaes.cpp: -------------------------------------------------------------------------------- 1 | #include "qtinyaes.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) 7 | #include 8 | #endif 9 | 10 | #include 11 | #undef CBC 12 | #undef ECB 13 | #undef CTR 14 | 15 | const int QTinyAes::BlockSize(AES_BLOCKLEN); 16 | const int QTinyAes::KeySize(AES_KEYLEN); 17 | 18 | class QTinyAesPrivate 19 | { 20 | public: 21 | QTinyAesPrivate(); 22 | 23 | QTinyAes::CipherMode mode; 24 | QByteArray key; 25 | QByteArray iv; 26 | }; 27 | 28 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) 29 | QByteArray QTinyAes::generateKey() 30 | { 31 | static_assert(KeySize >= sizeof(quint64), "Invalid aes keysize?!?"); 32 | QByteArray key(KeySize, Qt::Uninitialized); 33 | auto rng = QRandomGenerator64::system(); 34 | rng->fillRange((quint64*)key.data(), key.size()/sizeof(quint64)); 35 | return key; 36 | } 37 | #endif 38 | 39 | QTinyAes::QTinyAes(QObject *parent) : 40 | QObject(parent), 41 | d(new QTinyAesPrivate()) 42 | {} 43 | 44 | QTinyAes::QTinyAes(QTinyAes::CipherMode mode, const QByteArray &key, const QByteArray &iv, QObject *parent) : 45 | QTinyAes(parent) 46 | { 47 | d->mode = mode; 48 | d->key = key; 49 | d->iv = iv; 50 | } 51 | 52 | QTinyAes::~QTinyAes() 53 | { 54 | memset(d->key.data(), 0, d->key.size()); 55 | memset(d->iv.data(), 0, d->iv.size()); 56 | } 57 | 58 | QTinyAes::CipherMode QTinyAes::mode() const 59 | { 60 | return d->mode; 61 | } 62 | 63 | QByteArray QTinyAes::key() const 64 | { 65 | return d->key; 66 | } 67 | 68 | QByteArray QTinyAes::iv() const 69 | { 70 | return d->iv; 71 | } 72 | 73 | void QTinyAes::setMode(QTinyAes::CipherMode mode) 74 | { 75 | d->mode = mode; 76 | } 77 | 78 | void QTinyAes::setKey(const QByteArray &key) 79 | { 80 | resetKey(); 81 | Q_ASSERT_X(key.size() == KeySize, Q_FUNC_INFO, "The Key-Length is not a valid length! (Check QTinyAes::KEYSIZE)"); 82 | d->key = key; 83 | } 84 | 85 | void QTinyAes::resetKey() 86 | { 87 | memset(d->key.data(), 0, d->key.size()); 88 | d->key.clear(); 89 | } 90 | 91 | void QTinyAes::setIv(const QByteArray &iv) 92 | { 93 | resetIv(); 94 | if(!iv.isEmpty()) { 95 | Q_ASSERT_X(iv.size() >= BlockSize, Q_FUNC_INFO, "The initialisation vector must be at least QTinyAes::BLOCKSIZE bytes long (or empty)"); 96 | if(iv.size() > BlockSize) 97 | qWarning() << "IV is longer then QTinyAes::BLOCKSIZE - the rest will be truncated"; 98 | d->iv = iv.mid(0, BlockSize); 99 | } 100 | } 101 | 102 | void QTinyAes::resetIv() 103 | { 104 | memset(d->iv.data(), 0, d->iv.size()); 105 | d->iv.clear(); 106 | } 107 | 108 | QByteArray QTinyAes::encrypt(const QByteArray &plain) const 109 | { 110 | auto buffer = plain; 111 | preparePlainText(buffer); 112 | return encryptRaw(buffer); 113 | } 114 | 115 | QByteArray QTinyAes::decrypt(const QByteArray &cipher) const 116 | { 117 | auto buffer = decryptRaw(cipher); 118 | restorePlainText(buffer); 119 | return buffer; 120 | } 121 | 122 | QByteArray QTinyAes::encryptRaw(const QByteArray &plain) const 123 | { 124 | Q_ASSERT_X(!d->key.isEmpty(), Q_FUNC_INFO, "The key must not be empty to encrypt data"); 125 | Q_ASSERT_X(plain.size() % BlockSize == 0, Q_FUNC_INFO, "plain must be a multiple of QTinyAes::BlockSize"); 126 | auto buffer = plain; 127 | 128 | AES_ctx ctx; 129 | if(d->iv.isNull()) 130 | AES_init_ctx(&ctx, (uint8_t*)d->key.constData()); 131 | else 132 | AES_init_ctx_iv(&ctx, (uint8_t*)d->key.constData(), (uint8_t*)d->iv.constData()); 133 | 134 | switch(d->mode) { 135 | case CTR: 136 | AES_CTR_xcrypt_buffer(&ctx, (uint8_t*)buffer.data(), (uint32_t)buffer.size()); 137 | break; 138 | case CBC: 139 | AES_CBC_encrypt_buffer(&ctx, (uint8_t*)buffer.data(), (uint32_t)buffer.size()); 140 | break; 141 | case ECB: 142 | for(auto i = 0; i < buffer.size(); i += BlockSize) { 143 | auto ctxCopy = ctx; 144 | AES_ECB_encrypt(&ctxCopy, (uint8_t*)(buffer.data() + i)); 145 | } 146 | break; 147 | default: 148 | Q_UNREACHABLE(); 149 | break; 150 | } 151 | 152 | memset(&ctx, 0, sizeof(AES_ctx)); 153 | 154 | return buffer; 155 | 156 | } 157 | 158 | QByteArray QTinyAes::decryptRaw(const QByteArray &cipher) const 159 | { 160 | Q_ASSERT_X(!d->key.isEmpty(), Q_FUNC_INFO, "The key must not be empty to decrypt data"); 161 | Q_ASSERT_X(cipher.size() % BlockSize == 0, Q_FUNC_INFO, "cipher must be a multiple of QTinyAes::BlockSize"); 162 | auto buffer = cipher; 163 | 164 | AES_ctx ctx; 165 | if(d->iv.isNull()) 166 | AES_init_ctx(&ctx, (uint8_t*)d->key.constData()); 167 | else 168 | AES_init_ctx_iv(&ctx, (uint8_t*)d->key.constData(), (uint8_t*)d->iv.constData()); 169 | 170 | switch(d->mode) { 171 | case CTR: 172 | AES_CTR_xcrypt_buffer(&ctx, 173 | (uint8_t*)buffer.data(), 174 | (uint32_t)buffer.size()); 175 | break; 176 | case CBC: 177 | AES_CBC_decrypt_buffer(&ctx, 178 | (uint8_t*)buffer.data(), 179 | (uint32_t)buffer.size()); 180 | break; 181 | case ECB: 182 | for(auto i = 0; i < buffer.size(); i += BlockSize) { 183 | auto ctxCopy = ctx; 184 | AES_ECB_decrypt(&ctxCopy, (uint8_t*)(buffer.data() + i)); 185 | } 186 | break; 187 | default: 188 | Q_UNREACHABLE(); 189 | break; 190 | } 191 | 192 | return buffer; 193 | } 194 | 195 | QByteArray QTinyAes::ctrEncrypt(const QByteArray &key, const QByteArray &iv, const QByteArray &plain) 196 | { 197 | return QTinyAes(QTinyAes::CTR, key, iv).encrypt(plain); 198 | } 199 | 200 | QByteArray QTinyAes::ctrDecrypt(const QByteArray &key, const QByteArray &iv, const QByteArray &cipher) 201 | { 202 | return QTinyAes(QTinyAes::CTR, key, iv).decrypt(cipher); 203 | } 204 | 205 | QByteArray QTinyAes::cbcEncrypt(const QByteArray &key, const QByteArray &iv, const QByteArray &plain) 206 | { 207 | return QTinyAes(QTinyAes::CBC, key, iv).encrypt(plain); 208 | } 209 | 210 | QByteArray QTinyAes::cbcDecrypt(const QByteArray &key, const QByteArray &iv, const QByteArray &cipher) 211 | { 212 | return QTinyAes(QTinyAes::CBC, key, iv).decrypt(cipher); 213 | } 214 | 215 | QByteArray QTinyAes::ecbEncrypt(const QByteArray &key, const QByteArray &plain) 216 | { 217 | return QTinyAes(QTinyAes::ECB, key).encrypt(plain); 218 | } 219 | 220 | QByteArray QTinyAes::ecbDecrypt(const QByteArray &key, const QByteArray &cipher) 221 | { 222 | return QTinyAes(QTinyAes::ECB, key).decrypt(cipher); 223 | } 224 | 225 | void QTinyAes::preparePlainText(QByteArray &data) 226 | { 227 | auto padding = (BlockSize - (data.size() % BlockSize)); 228 | data.append(QByteArray(padding, (char)padding)); 229 | } 230 | 231 | void QTinyAes::restorePlainText(QByteArray &data) 232 | { 233 | Q_ASSERT_X(data.size() >= BlockSize, Q_FUNC_INFO, "Invalid data. Must be at least one block"); 234 | auto padding = data.at(data.size() - 1); 235 | data.chop((int)padding); 236 | } 237 | 238 | 239 | 240 | QTinyAesPrivate::QTinyAesPrivate() : 241 | mode(QTinyAes::CTR), 242 | key(), 243 | iv() 244 | {} 245 | -------------------------------------------------------------------------------- /qtinyaes.h: -------------------------------------------------------------------------------- 1 | #ifndef QTINYAES 2 | #define QTINYAES 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class QTinyAesPrivate; 9 | class QTinyAes : public QObject 10 | { 11 | Q_OBJECT 12 | 13 | Q_PROPERTY(CipherMode mode READ mode WRITE setMode) 14 | Q_PROPERTY(QByteArray key READ key WRITE setKey RESET resetKey) 15 | Q_PROPERTY(QByteArray iv READ iv WRITE setIv RESET resetIv) 16 | 17 | public: 18 | enum CipherMode { 19 | CTR, 20 | CBC, 21 | ECB 22 | }; 23 | Q_ENUM(CipherMode) 24 | 25 | static const int BlockSize; 26 | static const int KeySize; 27 | 28 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) 29 | static QByteArray generateKey(); 30 | #endif 31 | 32 | QTinyAes(QObject *parent = nullptr); 33 | QTinyAes(CipherMode mode, const QByteArray &key, const QByteArray &iv = QByteArray(), QObject *parent = nullptr); 34 | ~QTinyAes(); 35 | 36 | CipherMode mode() const; 37 | QByteArray key() const; 38 | QByteArray iv() const; 39 | 40 | Q_INVOKABLE QByteArray encrypt(const QByteArray &plain) const; 41 | Q_INVOKABLE QByteArray decrypt(const QByteArray &cipher) const; 42 | 43 | Q_INVOKABLE QByteArray encryptRaw(const QByteArray &plain) const; 44 | Q_INVOKABLE QByteArray decryptRaw(const QByteArray &cipher) const; 45 | 46 | static QByteArray ctrEncrypt(const QByteArray &key, const QByteArray &iv, const QByteArray &plain); 47 | static QByteArray ctrDecrypt(const QByteArray &key, const QByteArray &iv, const QByteArray &cipher); 48 | static QByteArray cbcEncrypt(const QByteArray &key, const QByteArray &iv, const QByteArray &plain); 49 | static QByteArray cbcDecrypt(const QByteArray &key, const QByteArray &iv, const QByteArray &cipher); 50 | static QByteArray ecbEncrypt(const QByteArray &key, const QByteArray &plain); 51 | static QByteArray ecbDecrypt(const QByteArray &key, const QByteArray &cipher); 52 | 53 | public slots: 54 | void setMode(CipherMode mode); 55 | void setKey(const QByteArray &key); 56 | void resetKey(); 57 | void setIv(const QByteArray &iv); 58 | void resetIv(); 59 | 60 | private: 61 | QScopedPointer d; 62 | 63 | static void preparePlainText(QByteArray &data); 64 | static void restorePlainText(QByteArray &data); 65 | }; 66 | 67 | #endif // QTINYAES 68 | 69 | -------------------------------------------------------------------------------- /qtinyaes.pri: -------------------------------------------------------------------------------- 1 | HEADERS += \ 2 | $$PWD/qtinyaes.h 3 | 4 | SOURCES += \ 5 | $$PWD/qtinyaes.cpp 6 | 7 | INCLUDEPATH += $$PWD 8 | 9 | QDEP_DEPENDS += Skycoder42/tiny-AES-C 10 | 11 | !qdep_build { 12 | isEmpty(TINYAES_KEYSIZE): TINYAES_KEYSIZE = 256 13 | DEFINES += AES$${TINYAES_KEYSIZE}=1 14 | } 15 | --------------------------------------------------------------------------------