#include "EncryptedSettings.h" #include "platform/Platform.h" EncryptedSettings::EncryptedSettings(QString file) : QSettings(file, QSettings::IniFormat) { #ifdef _WIN32 // On Win, LocalStorageKey can change after an OS update, so don't fetch it every time from the OS. // Use the cached one if available, and only get it from the OS if not. QString keyTag = QString::fromUtf8("LocalStorageKey"); encryptionKey = QByteArray::fromHex(value(keyTag).toByteArray()); if (!encryptionKey.isEmpty()) return; #endif // Get LocalStorageKey from the OS QByteArray fixedSeed("$JY/X?o=h·&%v/M("); QByteArray localKey = Platform::getInstance()->getLocalStorageKey(); QByteArray xLocalKey = XOR(fixedSeed, localKey); QByteArray hLocalKey = QCryptographicHash::hash(xLocalKey, QCryptographicHash::Sha1); encryptionKey = hLocalKey; #ifdef _WIN32 // Cache LocalStorageKey auto bkp = encryptionKey; encryptionKey.clear(); // switch to no key internally when caching the OS one setValue(keyTag, bkp.toHex()); encryptionKey = bkp; // switch back to the real encryptionKey #endif } void EncryptedSettings::setValue(const QString &key, const QVariant &value) { QSettings::setValue(hash(key), encrypt(key, value.toString())); } QVariant EncryptedSettings::value(const QString &key, const QVariant &defaultValue) { return QVariant(decrypt(key, QSettings::value(hash(key), encrypt(key, defaultValue.toString())).toString())); } void EncryptedSettings::beginGroup(const QString &prefix) { QSettings::beginGroup(hash(prefix)); } void EncryptedSettings::beginGroup(int numGroup) { QSettings::beginGroup(QSettings::childGroups().at(numGroup)); } void EncryptedSettings::endGroup() { QSettings::endGroup(); } int EncryptedSettings::numChildGroups() { return QSettings::childGroups().size(); } bool EncryptedSettings::containsGroup(QString groupName) { return QSettings::childGroups().contains(hash(groupName)); } bool EncryptedSettings::isGroupEmpty() { return QSettings::group().isEmpty(); } void EncryptedSettings::remove(const QString &key) { if (!key.length()) { QSettings::remove(QString::fromAscii("")); } else { QSettings::remove(hash(key)); } } void EncryptedSettings::clear() { QSettings::clear(); } void EncryptedSettings::sync() { if (mDeferSyncEnableCount > 0) { mSyncDeferred = true; } else { QSettings::sync(); mSyncDeferred = false; QFile::remove(this->fileName().append(QString::fromUtf8(".bak"))); QFile::copy(this->fileName(), this->fileName().append(QString::fromUtf8(".bak"))); } } void EncryptedSettings::deferSyncs(bool b) { if (b) { mDeferSyncEnableCount += 1; } else { mDeferSyncEnableCount -= 1; } } bool EncryptedSettings::needsDeferredSync() { return mSyncDeferred; } //Simplified XOR fun QByteArray EncryptedSettings::XOR(const QByteArray& key, const QByteArray& data) const { int keyLen = key.length(); if (!keyLen) { return data; } QByteArray result; int rotation = abs(key[keyLen/3]*key[keyLen/5])%keyLen; int increment = abs(key[keyLen/2]*key[keyLen/7])%keyLen; for (int i = 0, j = rotation; i < data.length(); i++, j -= increment) { if (j < 0) { j += keyLen; } result.append(data[i] ^ key[j]); } return result; } QString EncryptedSettings::encrypt(const QString key, const QString value) const { if (value.isEmpty()) { return value; } QByteArray k = hash(key).toAscii(); QByteArray xValue = XOR(k, value.toUtf8()); QByteArray xKey = XOR(k, group().toAscii()); QByteArray xEncrypted = XOR(k, Platform::getInstance()->encrypt(xValue, xKey)); return QString::fromAscii(xEncrypted.toBase64()); } QString EncryptedSettings::decrypt(const QString key, const QString value) const { if (value.isEmpty()) { return value; } QByteArray k = hash(key).toAscii(); QByteArray xValue = XOR(k, QByteArray::fromBase64(value.toAscii())); QByteArray xKey = XOR(k, group().toAscii()); QByteArray xDecrypted = XOR(k, Platform::getInstance()->decrypt(xValue, xKey)); return QString::fromUtf8(xDecrypted); } QString EncryptedSettings::hash(const QString key) const { QByteArray xPath = XOR(encryptionKey, (key+group()).toUtf8()); QByteArray keyHash = QCryptographicHash::hash(xPath, QCryptographicHash::Sha1); QByteArray xKeyHash = XOR(key.toUtf8(), keyHash); return QString::fromAscii(xKeyHash.toHex()); }