File indexing completed on 2025-01-19 03:55:40
0001 /* 0002 Copyright (c) 2011, Andre Somers 0003 All rights reserved. 0004 0005 Redistribution and use in source and binary forms, with or without 0006 modification, are permitted provided that the following conditions are met: 0007 * Redistributions of source code must retain the above copyright 0008 notice, this list of conditions and the following disclaimer. 0009 * Redistributions in binary form must reproduce the above copyright 0010 notice, this list of conditions and the following disclaimer in the 0011 documentation and/or other materials provided with the distribution. 0012 * Neither the name of the Rathenau Instituut, Andre Somers nor the 0013 names of its contributors may be used to endorse or promote products 0014 derived from this software without specific prior written permission. 0015 0016 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 0017 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 0018 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 0019 DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY 0020 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 0021 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 0022 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 0023 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 0024 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 0025 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 0026 */ 0027 #include "o0simplecrypt.h" 0028 #include <QByteArray> 0029 #include <QtDebug> 0030 #include <QtGlobal> 0031 #include <QDateTime> 0032 #include <QCryptographicHash> 0033 #include <QDataStream> 0034 #include <QIODevice> 0035 0036 O0SimpleCrypt::O0SimpleCrypt(): 0037 m_key(0), 0038 m_compressionMode(CompressionAuto), 0039 m_protectionMode(ProtectionChecksum), 0040 m_lastError(ErrorNoError) 0041 { 0042 #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) 0043 qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); 0044 #else 0045 m_rand.seed(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); 0046 #endif 0047 } 0048 0049 O0SimpleCrypt::O0SimpleCrypt(quint64 key): 0050 m_key(key), 0051 m_compressionMode(CompressionAuto), 0052 m_protectionMode(ProtectionChecksum), 0053 m_lastError(ErrorNoError) 0054 { 0055 #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) 0056 qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); 0057 #else 0058 m_rand.seed(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); 0059 #endif 0060 splitKey(); 0061 } 0062 0063 void O0SimpleCrypt::setKey(quint64 key) 0064 { 0065 m_key = key; 0066 splitKey(); 0067 } 0068 0069 void O0SimpleCrypt::splitKey() 0070 { 0071 m_keyParts.clear(); 0072 m_keyParts.resize(8); 0073 for (int i=0;i<8;i++) { 0074 quint64 part = m_key; 0075 for (int j=i; j>0; j--) 0076 part = part >> 8; 0077 part = part & 0xff; 0078 m_keyParts[i] = static_cast<char>(part); 0079 } 0080 } 0081 0082 QByteArray O0SimpleCrypt::encryptToByteArray(const QString& plaintext) 0083 { 0084 QByteArray plaintextArray = plaintext.toUtf8(); 0085 return encryptToByteArray(plaintextArray); 0086 } 0087 0088 QByteArray O0SimpleCrypt::encryptToByteArray(QByteArray plaintext) 0089 { 0090 if (m_keyParts.isEmpty()) { 0091 qWarning() << "No key set."; 0092 m_lastError = ErrorNoKeySet; 0093 return QByteArray(); 0094 } 0095 0096 0097 QByteArray ba = plaintext; 0098 0099 CryptoFlags flags = CryptoFlagNone; 0100 if (m_compressionMode == CompressionAlways) { 0101 ba = qCompress(ba, 9); //maximum compression 0102 flags |= CryptoFlagCompression; 0103 } else if (m_compressionMode == CompressionAuto) { 0104 QByteArray compressed = qCompress(ba, 9); 0105 if (compressed.count() < ba.count()) { 0106 ba = compressed; 0107 flags |= CryptoFlagCompression; 0108 } 0109 } 0110 0111 QByteArray integrityProtection; 0112 if (m_protectionMode == ProtectionChecksum) { 0113 flags |= CryptoFlagChecksum; 0114 QDataStream s(&integrityProtection, QIODevice::WriteOnly); 0115 #if QT_VERSION >= 0x060000 0116 s << qChecksum(QByteArrayView(ba)); 0117 #else 0118 s << qChecksum(ba.constData(), ba.length()); 0119 #endif 0120 } else if (m_protectionMode == ProtectionHash) { 0121 flags |= CryptoFlagHash; 0122 QCryptographicHash hash(QCryptographicHash::Sha1); 0123 hash.addData(ba); 0124 0125 integrityProtection += hash.result(); 0126 } 0127 0128 //prepend a random char to the string 0129 #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) 0130 char randomChar = char(qrand() & 0xFF); 0131 #else 0132 char randomChar = char(m_rand.generate() & 0xFF); 0133 #endif 0134 ba = randomChar + integrityProtection + ba; 0135 0136 int pos(0); 0137 char lastChar(0); 0138 0139 int cnt = ba.count(); 0140 0141 while (pos < cnt) { 0142 ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar; 0143 lastChar = ba.at(pos); 0144 ++pos; 0145 } 0146 0147 QByteArray resultArray; 0148 resultArray.append(char(0x03)); //version for future updates to algorithm 0149 resultArray.append(char(flags)); //encryption flags 0150 resultArray.append(ba); 0151 0152 m_lastError = ErrorNoError; 0153 return resultArray; 0154 } 0155 0156 QString O0SimpleCrypt::encryptToString(const QString& plaintext) 0157 { 0158 QByteArray plaintextArray = plaintext.toUtf8(); 0159 QByteArray cypher = encryptToByteArray(plaintextArray); 0160 QString cypherString = QString::fromLatin1(cypher.toBase64()); 0161 return cypherString; 0162 } 0163 0164 QString O0SimpleCrypt::encryptToString(QByteArray plaintext) 0165 { 0166 QByteArray cypher = encryptToByteArray(plaintext); 0167 QString cypherString = QString::fromLatin1(cypher.toBase64()); 0168 return cypherString; 0169 } 0170 0171 QString O0SimpleCrypt::decryptToString(const QString &cyphertext) 0172 { 0173 QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); 0174 QByteArray plaintextArray = decryptToByteArray(cyphertextArray); 0175 QString plaintext = QString::fromUtf8(plaintextArray, plaintextArray.size()); 0176 0177 return plaintext; 0178 } 0179 0180 QString O0SimpleCrypt::decryptToString(QByteArray cypher) 0181 { 0182 QByteArray ba = decryptToByteArray(cypher); 0183 QString plaintext = QString::fromUtf8(ba, ba.size()); 0184 0185 return plaintext; 0186 } 0187 0188 QByteArray O0SimpleCrypt::decryptToByteArray(const QString& cyphertext) 0189 { 0190 QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); 0191 QByteArray ba = decryptToByteArray(cyphertextArray); 0192 0193 return ba; 0194 } 0195 0196 QByteArray O0SimpleCrypt::decryptToByteArray(QByteArray cypher) 0197 { 0198 if (m_keyParts.isEmpty()) { 0199 qWarning() << "No key set."; 0200 m_lastError = ErrorNoKeySet; 0201 return QByteArray(); 0202 } 0203 0204 if (!cypher.length()) { 0205 m_lastError = ErrorUnknownVersion; 0206 return QByteArray(); 0207 } 0208 0209 QByteArray ba = cypher; 0210 0211 char version = ba.at(0); 0212 0213 if (version !=3) { //we only work with version 3 0214 m_lastError = ErrorUnknownVersion; 0215 qWarning() << "Invalid version or not a cyphertext."; 0216 return QByteArray(); 0217 } 0218 0219 CryptoFlags flags = CryptoFlags(ba.at(1)); 0220 0221 ba = ba.mid(2); 0222 int pos(0); 0223 int cnt(ba.count()); 0224 char lastChar = 0; 0225 0226 while (pos < cnt) { 0227 char currentChar = ba[pos]; 0228 ba[pos] = ba.at(pos) ^ lastChar ^ m_keyParts.at(pos % 8); 0229 lastChar = currentChar; 0230 ++pos; 0231 } 0232 0233 ba = ba.mid(1); //chop off the random number at the start 0234 0235 bool integrityOk(true); 0236 if (flags.testFlag(CryptoFlagChecksum)) { 0237 if (ba.length() < 2) { 0238 m_lastError = ErrorIntegrityFailed; 0239 return QByteArray(); 0240 } 0241 quint16 storedChecksum; 0242 { 0243 QDataStream s(&ba, QIODevice::ReadOnly); 0244 s >> storedChecksum; 0245 } 0246 ba = ba.mid(2); 0247 #if QT_VERSION >= 0x060000 0248 quint16 checksum = qChecksum(QByteArrayView(ba)); 0249 #else 0250 quint16 checksum = qChecksum(ba.constData(), ba.length()); 0251 #endif 0252 integrityOk = (checksum == storedChecksum); 0253 } else if (flags.testFlag(CryptoFlagHash)) { 0254 if (ba.length() < 20) { 0255 m_lastError = ErrorIntegrityFailed; 0256 return QByteArray(); 0257 } 0258 QByteArray storedHash = ba.left(20); 0259 ba = ba.mid(20); 0260 QCryptographicHash hash(QCryptographicHash::Sha1); 0261 hash.addData(ba); 0262 integrityOk = (hash.result() == storedHash); 0263 } 0264 0265 if (!integrityOk) { 0266 m_lastError = ErrorIntegrityFailed; 0267 return QByteArray(); 0268 } 0269 0270 if (flags.testFlag(CryptoFlagCompression)) 0271 ba = qUncompress(ba); 0272 0273 m_lastError = ErrorNoError; 0274 return ba; 0275 }