From f8cd1d65ef6ca9d441aab2a098ca36e936b9e313 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Sun, 11 Feb 2018 04:37:18 +0100 Subject: [PATCH] Ref T249, added encryption and obfudcation base class (3rd party) --- src/blackmisc/blackmisc.pro | 2 + src/blackmisc/obfuscation.cpp | 39 ++++ src/blackmisc/obfuscation.h | 41 ++++ src/blackmisc/thirdparty/simplecrypt.cpp | 278 +++++++++++++++++++++++ src/blackmisc/thirdparty/simplecrypt.h | 234 +++++++++++++++++++ 5 files changed, 594 insertions(+) create mode 100644 src/blackmisc/obfuscation.cpp create mode 100644 src/blackmisc/obfuscation.h create mode 100644 src/blackmisc/thirdparty/simplecrypt.cpp create mode 100644 src/blackmisc/thirdparty/simplecrypt.h diff --git a/src/blackmisc/blackmisc.pro b/src/blackmisc/blackmisc.pro index 0a3471b74..9c4050681 100644 --- a/src/blackmisc/blackmisc.pro +++ b/src/blackmisc/blackmisc.pro @@ -40,6 +40,7 @@ HEADERS += *.h \ $$PWD/simulation/fscommon/*.h \ $$PWD/simulation/fsx/*.h \ $$PWD/simulation/xplane/*.h \ + $$PWD/thirdparty/*.h \ $$PWD/test/*.h \ $$PWD/weather/*.h @@ -58,6 +59,7 @@ SOURCES += *.cpp \ $$PWD/simulation/fscommon/*.cpp \ $$PWD/simulation/fsx/*.cpp \ $$PWD/simulation/xplane/*.cpp \ + $$PWD/thirdparty/*.cpp \ $$PWD/test/*.cpp \ $$PWD/weather/*.cpp diff --git a/src/blackmisc/obfuscation.cpp b/src/blackmisc/obfuscation.cpp new file mode 100644 index 000000000..beeb1a20f --- /dev/null +++ b/src/blackmisc/obfuscation.cpp @@ -0,0 +1,39 @@ +/* Copyright (C) 2018 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "obfuscation.h" +#include "blackmisc/thirdparty/simplecrypt.h" +#include + +using namespace BlackMisc::ThirdParty; + +namespace BlackMisc +{ + QString CObfuscation::decode(const QString &inString, bool trimmed) + { + if (!inString.startsWith(prefix())) { return trimmed ? inString.trimmed() : inString; } + if (inString.length() == prefix().length()) { return QString(); } + SimpleCrypt simpleCrypt(Key); + const QString decoded = simpleCrypt.decryptToString(inString.mid(prefix().length())); + return trimmed ? decoded.trimmed() : decoded; + } + + QString CObfuscation::encode(const QString &inString, bool trimmed) + { + SimpleCrypt simpleCrypt(Key); + const QString encrypted = simpleCrypt.encryptToString(trimmed ? inString.trimmed() : inString); + return prefix() % encrypted; + } + + const QString &CObfuscation::prefix() + { + static const QString obfuscated("OBF:"); + return obfuscated; + } +} // ns diff --git a/src/blackmisc/obfuscation.h b/src/blackmisc/obfuscation.h new file mode 100644 index 000000000..41b9ab5ea --- /dev/null +++ b/src/blackmisc/obfuscation.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2018 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_OBFUSCATION_H +#define BLACKMISC_OBFUSCATION_H + +#include "blackmiscexport.h" +#include + +namespace BlackMisc +{ + //! Utility class to obfuscate strings in the source code to make them unreadable. + //! \remark this is no real security, but as the name says just obfuscation. The sole purpose of this class is to remove potentially strings from the source code + class BLACKMISC_EXPORT CObfuscation + { + protected: + //! Constructor + CObfuscation() {} + + //! Decode string if it has the prefix, otherwise do nothing with it + static QString decode(const QString &inString, bool trimmed = true); + + //! Encode string and return with prefix + static QString encode(const QString &inString, bool trimmed = true); + + //! Prefix to be used with obfuscated string + static const QString &prefix(); + + private: + static constexpr quint64 Key = 7234623562; + }; +} +#endif // guard diff --git a/src/blackmisc/thirdparty/simplecrypt.cpp b/src/blackmisc/thirdparty/simplecrypt.cpp new file mode 100644 index 000000000..a7af37cae --- /dev/null +++ b/src/blackmisc/thirdparty/simplecrypt.cpp @@ -0,0 +1,278 @@ +/* +Copyright (c) 2011, Andre Somers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Rathenau Instituut, Andre Somers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "simplecrypt.h" +#include +#include +#include +#include +#include +#include + +namespace BlackMisc +{ + namespace ThirdParty + { + SimpleCrypt::SimpleCrypt(): + m_key(0), + m_compressionMode(CompressionAuto), + m_protectionMode(ProtectionChecksum), + m_lastError(ErrorNoError) + { + qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); + } + + SimpleCrypt::SimpleCrypt(quint64 key): + m_key(key), + m_compressionMode(CompressionAuto), + m_protectionMode(ProtectionChecksum), + m_lastError(ErrorNoError) + { + qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); + splitKey(); + } + + void SimpleCrypt::setKey(quint64 key) + { + m_key = key; + splitKey(); + } + + void SimpleCrypt::splitKey() + { + m_keyParts.clear(); + m_keyParts.resize(8); + for (int i = 0; i < 8; i++) + { + quint64 part = m_key; + for (int j = i; j > 0; j--) + part = part >> 8; + part = part & 0xff; + m_keyParts[i] = static_cast(part); + } + } + + QByteArray SimpleCrypt::encryptToByteArray(const QString &plaintext) + { + QByteArray plaintextArray = plaintext.toUtf8(); + return encryptToByteArray(plaintextArray); + } + + QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext) + { + if (m_keyParts.isEmpty()) + { + qWarning() << "No key set."; + m_lastError = ErrorNoKeySet; + return QByteArray(); + } + + + QByteArray ba = plaintext; + + CryptoFlags flags = CryptoFlagNone; + if (m_compressionMode == CompressionAlways) + { + ba = qCompress(ba, 9); //maximum compression + flags |= CryptoFlagCompression; + } + else if (m_compressionMode == CompressionAuto) + { + QByteArray compressed = qCompress(ba, 9); + if (compressed.count() < ba.count()) + { + ba = compressed; + flags |= CryptoFlagCompression; + } + } + + QByteArray integrityProtection; + if (m_protectionMode == ProtectionChecksum) + { + flags |= CryptoFlagChecksum; + QDataStream s(&integrityProtection, QIODevice::WriteOnly); + s << qChecksum(ba.constData(), ba.length()); + } + else if (m_protectionMode == ProtectionHash) + { + flags |= CryptoFlagHash; + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(ba); + + integrityProtection += hash.result(); + } + + //prepend a random char to the string + char randomChar = char(qrand() & 0xFF); + ba = randomChar + integrityProtection + ba; + + int pos(0); + char lastChar(0); + + int cnt = ba.count(); + + while (pos < cnt) + { + ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar; + lastChar = ba.at(pos); + ++pos; + } + + QByteArray resultArray; + resultArray.append(char(0x03)); //version for future updates to algorithm + resultArray.append(char(flags)); //encryption flags + resultArray.append(ba); + + m_lastError = ErrorNoError; + return resultArray; + } + + QString SimpleCrypt::encryptToString(const QString &plaintext) + { + QByteArray plaintextArray = plaintext.toUtf8(); + QByteArray cypher = encryptToByteArray(plaintextArray); + QString cypherString = QString::fromLatin1(cypher.toBase64()); + return cypherString; + } + + QString SimpleCrypt::encryptToString(QByteArray plaintext) + { + QByteArray cypher = encryptToByteArray(plaintext); + QString cypherString = QString::fromLatin1(cypher.toBase64()); + return cypherString; + } + + QString SimpleCrypt::decryptToString(const QString &cyphertext) + { + QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); + QByteArray plaintextArray = decryptToByteArray(cyphertextArray); + QString plaintext = QString::fromUtf8(plaintextArray, plaintextArray.size()); + + return plaintext; + } + + QString SimpleCrypt::decryptToString(QByteArray cypher) + { + QByteArray ba = decryptToByteArray(cypher); + QString plaintext = QString::fromUtf8(ba, ba.size()); + + return plaintext; + } + + QByteArray SimpleCrypt::decryptToByteArray(const QString &cyphertext) + { + QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); + QByteArray ba = decryptToByteArray(cyphertextArray); + + return ba; + } + + QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher) + { + if (m_keyParts.isEmpty()) + { + qWarning() << "No key set."; + m_lastError = ErrorNoKeySet; + return QByteArray(); + } + + QByteArray ba = cypher; + + if (cypher.count() < 3) + return QByteArray(); + + char version = ba.at(0); + + if (version != 3) //we only work with version 3 + { + m_lastError = ErrorUnknownVersion; + qWarning() << "Invalid version or not a cyphertext."; + return QByteArray(); + } + + CryptoFlags flags = CryptoFlags(ba.at(1)); + + ba = ba.mid(2); + int pos(0); + int cnt(ba.count()); + char lastChar = 0; + + while (pos < cnt) + { + char currentChar = ba[pos]; + ba[pos] = ba.at(pos) ^ lastChar ^ m_keyParts.at(pos % 8); + lastChar = currentChar; + ++pos; + } + + ba = ba.mid(1); //chop off the random number at the start + + bool integrityOk(true); + if (flags.testFlag(CryptoFlagChecksum)) + { + if (ba.length() < 2) + { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + quint16 storedChecksum; + { + QDataStream s(&ba, QIODevice::ReadOnly); + s >> storedChecksum; + } + ba = ba.mid(2); + quint16 checksum = qChecksum(ba.constData(), ba.length()); + integrityOk = (checksum == storedChecksum); + } + else if (flags.testFlag(CryptoFlagHash)) + { + if (ba.length() < 20) + { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + QByteArray storedHash = ba.left(20); + ba = ba.mid(20); + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(ba); + integrityOk = (hash.result() == storedHash); + } + + if (!integrityOk) + { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + + if (flags.testFlag(CryptoFlagCompression)) + ba = qUncompress(ba); + + m_lastError = ErrorNoError; + return ba; + } + } +} diff --git a/src/blackmisc/thirdparty/simplecrypt.h b/src/blackmisc/thirdparty/simplecrypt.h new file mode 100644 index 000000000..756c88628 --- /dev/null +++ b/src/blackmisc/thirdparty/simplecrypt.h @@ -0,0 +1,234 @@ +/* +Copyright (c) 2011, Andre Somers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Rathenau Instituut, Andre Somers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef BLACKMISC_THIRDPARTY_SIMPLECRYPT_H +#define BLACKMISC_THIRDPARTY_SIMPLECRYPT_H + +#include "blackmisc/blackmiscexport.h" +#include +#include +#include + +namespace BlackMisc +{ + namespace ThirdParty + { + /** + @short Simple encryption and decryption of strings and byte arrays + + This class provides a simple implementation of encryption and decryption + of strings and byte arrays. + + @warning The encryption provided by this class is NOT strong encryption. It may + help to shield things from curious eyes, but it will NOT stand up to someone + determined to break the encryption. Don't say you were not warned. + + The class uses a 64 bit key. Simply create an instance of the class, set the key, + and use the encryptToString() method to calculate an encrypted version of the input string. + To decrypt that string again, use an instance of SimpleCrypt initialized with + the same key, and call the decryptToString() method with the encrypted string. If the key + matches, the decrypted version of the string will be returned again. + + If you do not provide a key, or if something else is wrong, the encryption and + decryption function will return an empty string or will return a string containing nonsense. + lastError() will return a value indicating if the method was succesful, and if not, why not. + + SimpleCrypt is prepared for the case that the encryption and decryption + algorithm is changed in a later version, by prepending a version identifier to the cypertext. + */ + class BLACKMISC_EXPORT SimpleCrypt + { + public: + /** + CompressionMode describes if compression will be applied to the data to be + encrypted. + */ + enum CompressionMode + { + CompressionAuto, /*!< Only apply compression if that results in a shorter plaintext. */ + CompressionAlways, /*!< Always apply compression. Note that for short inputs, a compression may result in longer data */ + CompressionNever /*!< Never apply compression. */ + }; + + /** + IntegrityProtectionMode describes measures taken to make it possible to detect problems with the data + or wrong decryption keys. + + Measures involve adding a checksum or a cryptograhpic hash to the data to be encrypted. This + increases the length of the resulting cypertext, but makes it possible to check if the plaintext + appears to be valid after decryption. + */ + enum IntegrityProtectionMode + { + ProtectionNone, /*!< The integerity of the encrypted data is not protected. It is not really possible to detect a wrong key, for instance. */ + ProtectionChecksum,/*!< A simple checksum is used to verify that the data is in order. If not, an empty string is returned. */ + ProtectionHash /*!< A cryptographic hash is used to verify the integrity of the data. This method produces a much stronger, but longer check */ + }; + + //! Error describes the type of error that occured. + enum Error + { + ErrorNoError, /*!< No error occurred. */ + ErrorNoKeySet, /*!< No key was set. You can not encrypt or decrypt without a valid key. */ + ErrorUnknownVersion, /*!< The version of this data is unknown, or the data is otherwise not valid. */ + ErrorIntegrityFailed, /*!< The integrity check of the data failed. Perhaps the wrong key was used. */ + }; + + //! Constructs a SimpleCrypt instance without a valid key set on it. + SimpleCrypt(); + + //! Constructs a SimpleCrypt instance and initializes it with the given @arg key. + explicit SimpleCrypt(quint64 key); + + //! (Re-) initializes the key with the given @arg key. + void setKey(quint64 key); + + //! Returns true if SimpleCrypt has been initialized with a key. + bool hasKey() const {return !m_keyParts.isEmpty();} + + /** + Sets the compression mode to use when encrypting data. The default mode is Auto. + + Note that decryption is not influenced by this mode, as the decryption recognizes + what mode was used when encrypting. + */ + void setCompressionMode(CompressionMode mode) {m_compressionMode = mode;} + + //! Returns the CompressionMode that is currently in use. + CompressionMode compressionMode() const {return m_compressionMode;} + + /** + Sets the integrity mode to use when encrypting data. The default mode is Checksum. + + Note that decryption is not influenced by this mode, as the decryption recognizes + what mode was used when encrypting. + */ + void setIntegrityProtectionMode(IntegrityProtectionMode mode) {m_protectionMode = mode;} + + //! Returns the IntegrityProtectionMode that is currently in use. + IntegrityProtectionMode integrityProtectionMode() const {return m_protectionMode;} + + //! Returns the last error that occurred. + Error lastError() const {return m_lastError;} + + /** + Encrypts the @arg plaintext string with the key the class was initialized with, and returns + a cyphertext the result. The result is a base64 encoded version of the binary array that is the + actual result of the string, so it can be stored easily in a text format. + */ + QString encryptToString(const QString &plaintext) ; + + /** + Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns + a cyphertext the result. The result is a base64 encoded version of the binary array that is the + actual result of the encryption, so it can be stored easily in a text format. + */ + QString encryptToString(QByteArray plaintext) ; + + /** + Encrypts the @arg plaintext string with the key the class was initialized with, and returns + a binary cyphertext in a QByteArray the result. + + This method returns a byte array, that is useable for storing a binary format. If you need + a string you can store in a text file, use encryptToString() instead. + */ + QByteArray encryptToByteArray(const QString &plaintext) ; + + /** + Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns + a binary cyphertext in a QByteArray the result. + + This method returns a byte array, that is useable for storing a binary format. If you need + a string you can store in a text file, use encryptToString() instead. + */ + QByteArray encryptToByteArray(QByteArray plaintext) ; + + /** + Decrypts a cyphertext string encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QString decryptToString(const QString &cyphertext) ; + + /** + Decrypts a cyphertext string encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QByteArray decryptToByteArray(const QString &cyphertext) ; + + /** + Decrypts a cyphertext binary encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QString decryptToString(QByteArray cypher) ; + + /** + Decrypts a cyphertext binary encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QByteArray decryptToByteArray(QByteArray cypher) ; + + /** + Enum to describe options that have been used for the encryption. Currently only one, but + that only leaves room for future extensions like adding a cryptographic hash. + */ + enum CryptoFlag + { + CryptoFlagNone = 0, //!< None + CryptoFlagCompression = 0x01, //!< Compression + CryptoFlagChecksum = 0x02, //!< Checksum + CryptoFlagHash = 0x04 //!< Hash + }; + Q_DECLARE_FLAGS(CryptoFlags, CryptoFlag); //!< For future usage + + private: + //! Split key + void splitKey(); + + quint64 m_key; + QVector m_keyParts; + CompressionMode m_compressionMode; + IntegrityProtectionMode m_protectionMode; + Error m_lastError; + }; + } +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(BlackMisc::ThirdParty::SimpleCrypt::CryptoFlags) + +#endif // guard