safePasswords #36
74
CHANGELOG.md
Normal file
74
CHANGELOG.md
Normal file
@ -0,0 +1,74 @@
|
||||
# Changelog
|
||||
|
||||
## Squawk 0.1.4 (UNRELEASED)
|
||||
------------------------------
|
||||
### New features
|
||||
- several ways to manage your account password:
|
||||
- store it in plain text with the config (like it always was)
|
||||
- store it in config jammed (local hashing with the constant seed, not secure at all but might look like it is)
|
||||
- ask the account password on each program launch
|
||||
|
||||
### Bug fixes
|
||||
- never updating MUC avatars now update
|
||||
- going offline related segfault fix
|
||||
|
||||
|
||||
## Squawk 0.1.3 (Mar 31, 2020)
|
||||
------------------------------
|
||||
### New features
|
||||
- delivery states for the messages
|
||||
- delivery receipts now work for real
|
||||
- avatars in conferences
|
||||
- edited messages now display correctly
|
||||
- restyling to get better look with different desktop themes
|
||||
|
||||
### Bug fixes
|
||||
- clickable links now detects better
|
||||
- fixed lost messages that came with no ID
|
||||
- avatar related fixes
|
||||
- message carbons now get turned on only if the server supports them
|
||||
- progress spinner fix
|
||||
- files in dialog now have better comment
|
||||
|
||||
|
||||
## Squawk 0.1.2 (Dec 25, 2019)
|
||||
------------------------------
|
||||
### New features
|
||||
- icons in roster for conferences
|
||||
- pal avatar in dialog window
|
||||
- avatars next to every message in dialog windows (not in conferences yet)
|
||||
- roster window position and size now are stored in config
|
||||
- expanded accounts and groups are stored in config
|
||||
- availability (from top combobox) now is stored in config
|
||||
|
||||
### Bug fixes
|
||||
- segfault when sending more then one attached file
|
||||
- wrong path and name of saving file
|
||||
- wrong message syntax when attaching file and writing text in the save message
|
||||
- problem with links highlighting in dialog
|
||||
- project building fixes
|
||||
|
||||
|
||||
## Squawk 0.1.1 (Nov 16, 2019)
|
||||
------------------------------
|
||||
A lot of bug fixes, memory leaks fixes
|
||||
### New features
|
||||
- exchange files via HTTP File Upload
|
||||
- download VCards of your contacts
|
||||
- upload your VCard with information about your contact phones email addresses, names, career information and avatar
|
||||
- avatars of your contacts in roster and in notifications
|
||||
|
||||
|
||||
## Squawk 0.0.5 (Oct 10, 2019)
|
||||
------------------------------
|
||||
### Features
|
||||
- chat directly
|
||||
- have multiple accounts
|
||||
- add contacts
|
||||
- remove contacts
|
||||
- assign contact to different groups
|
||||
- chat in MUCs
|
||||
- join MUCs, leave them, keep them subscribed or unsubscribed
|
||||
- download attachmets
|
||||
- local history
|
||||
- desktop notifications of new messages
|
@ -74,6 +74,8 @@ endif()
|
||||
add_subdirectory(ui)
|
||||
add_subdirectory(core)
|
||||
|
||||
add_subdirectory(external/simpleCrypt)
|
||||
|
||||
target_link_libraries(squawk squawkUI)
|
||||
target_link_libraries(squawk squawkCORE)
|
||||
target_link_libraries(squawk uuid)
|
||||
|
@ -37,3 +37,4 @@ target_link_libraries(squawkCORE Qt5::Gui)
|
||||
target_link_libraries(squawkCORE Qt5::Xml)
|
||||
target_link_libraries(squawkCORE qxmpp)
|
||||
target_link_libraries(squawkCORE lmdb)
|
||||
target_link_libraries(squawkCORE simpleCrypt)
|
||||
|
@ -52,14 +52,31 @@ void Core::Squawk::stop()
|
||||
QSettings settings;
|
||||
settings.beginGroup("core");
|
||||
settings.beginWriteArray("accounts");
|
||||
SimpleCrypt crypto(passwordHash);
|
||||
for (std::deque<Account*>::size_type i = 0; i < accounts.size(); ++i) {
|
||||
settings.setArrayIndex(i);
|
||||
Account* acc = accounts[i];
|
||||
|
||||
Shared::AccountPassword ap = acc->getPasswordType();
|
||||
QString password;
|
||||
|
||||
switch (ap) {
|
||||
case Shared::AccountPassword::plain:
|
||||
password = acc->getPassword();
|
||||
break;
|
||||
case Shared::AccountPassword::jammed:
|
||||
password = crypto.encryptToString(acc->getPassword());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
settings.setValue("name", acc->getName());
|
||||
settings.setValue("server", acc->getServer());
|
||||
settings.setValue("login", acc->getLogin());
|
||||
settings.setValue("password", acc->getPassword());
|
||||
settings.setValue("password", password);
|
||||
settings.setValue("resource", acc->getResource());
|
||||
settings.setValue("passwordType", static_cast<int>(ap));
|
||||
}
|
||||
settings.endArray();
|
||||
settings.endGroup();
|
||||
@ -318,12 +335,37 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
|
||||
|
||||
Core::Account* acc = itr->second;
|
||||
Shared::ConnectionState st = acc->getState();
|
||||
QMap<QString, QVariant>::const_iterator mItr;
|
||||
bool needToReconnect = false;
|
||||
|
||||
if (st != Shared::ConnectionState::disconnected) {
|
||||
mItr = map.find("login");
|
||||
if (mItr != map.end()) {
|
||||
needToReconnect = acc->getLogin() != mItr->toString();
|
||||
}
|
||||
|
||||
if (!needToReconnect) {
|
||||
mItr = map.find("password");
|
||||
if (mItr != map.end()) {
|
||||
needToReconnect = acc->getPassword() != mItr->toString();
|
||||
}
|
||||
}
|
||||
if (!needToReconnect) {
|
||||
mItr = map.find("server");
|
||||
if (mItr != map.end()) {
|
||||
needToReconnect = acc->getServer() != mItr->toString();
|
||||
}
|
||||
}
|
||||
if (!needToReconnect) {
|
||||
mItr = map.find("resource");
|
||||
if (mItr != map.end()) {
|
||||
needToReconnect = acc->getResource() != mItr->toString();
|
||||
}
|
||||
}
|
||||
|
||||
if (needToReconnect && st != Shared::ConnectionState::disconnected) {
|
||||
acc->reconnect();
|
||||
}
|
||||
|
||||
QMap<QString, QVariant>::const_iterator mItr;
|
||||
mItr = map.find("login");
|
||||
if (mItr != map.end()) {
|
||||
acc->setLogin(mItr->toString());
|
||||
@ -344,6 +386,11 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
|
||||
acc->setServer(mItr->toString());
|
||||
}
|
||||
|
||||
mItr = map.find("passwordType");
|
||||
if (mItr != map.end()) {
|
||||
acc->setPasswordType(Shared::Global::fromInt<Shared::AccountPassword>(mItr->toInt()));
|
||||
}
|
||||
|
||||
emit changeAccount(name, map);
|
||||
}
|
||||
|
||||
@ -570,6 +617,17 @@ void Core::Squawk::uploadVCard(const QString& account, const Shared::VCard& card
|
||||
itr->second->uploadVCard(card);
|
||||
}
|
||||
|
||||
void Core::Squawk::responsePassword(const QString& account, const QString& password)
|
||||
{
|
||||
AccountsMap::const_iterator itr = amap.find(account);
|
||||
if (itr == amap.end()) {
|
||||
qDebug() << "An attempt to set password to non existing account" << account << ", skipping";
|
||||
return;
|
||||
}
|
||||
itr->second->setPassword(password);
|
||||
accountReady();
|
||||
}
|
||||
|
||||
void Core::Squawk::readSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
@ -614,5 +672,16 @@ void Core::Squawk::parseAccount(
|
||||
addAccount(login, server, password, name, resource, passwordType);
|
||||
accountReady();
|
||||
break;
|
||||
case Shared::AccountPassword::jammed: {
|
||||
SimpleCrypt crypto(passwordHash);
|
||||
QString decrypted = crypto.decryptToString(password);
|
||||
addAccount(login, server, decrypted, name, resource, passwordType);
|
||||
accountReady();
|
||||
}
|
||||
break;
|
||||
case Shared::AccountPassword::alwaysAsk:
|
||||
addAccount(login, server, QString(), name, resource, passwordType);
|
||||
emit requestPassword(name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "shared/message.h"
|
||||
#include "shared/global.h"
|
||||
#include "networkaccess.h"
|
||||
#include "external/simpleCrypt/simplecrypt.h"
|
||||
|
||||
namespace Core
|
||||
{
|
||||
@ -73,6 +74,7 @@ signals:
|
||||
void uploadFileProgress(const QString& messageId, qreal value);
|
||||
void responseVCard(const QString& jid, const Shared::VCard& card);
|
||||
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
||||
void requestPassword(const QString& account);
|
||||
|
||||
public slots:
|
||||
void start();
|
||||
@ -101,6 +103,7 @@ public slots:
|
||||
void downloadFileRequest(const QString& messageId, const QString& url);
|
||||
void requestVCard(const QString& account, const QString& jid);
|
||||
void uploadVCard(const QString& account, const Shared::VCard& card);
|
||||
void responsePassword(const QString& account, const QString& password);
|
||||
|
||||
private:
|
||||
typedef std::deque<Account*> Accounts;
|
||||
@ -155,6 +158,8 @@ private:
|
||||
const QString& resource,
|
||||
Shared::AccountPassword passwordType
|
||||
);
|
||||
|
||||
static const quint64 passwordHash = 0x08d054225ac4871d;
|
||||
};
|
||||
|
||||
}
|
||||
|
16
external/simpleCrypt/CMakeLists.txt
vendored
Normal file
16
external/simpleCrypt/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(simplecrypt)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
find_package(Qt5Core CONFIG REQUIRED)
|
||||
|
||||
set(simplecrypt_SRC
|
||||
simplecrypt.cpp
|
||||
)
|
||||
|
||||
# Tell CMake to create the helloworld executable
|
||||
add_library(simpleCrypt ${simplecrypt_SRC})
|
||||
|
||||
# Use the Widgets module from Qt 5.
|
||||
target_link_libraries(simpleCrypt Qt5::Core)
|
252
external/simpleCrypt/simplecrypt.cpp
vendored
Normal file
252
external/simpleCrypt/simplecrypt.cpp
vendored
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
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 <QByteArray>
|
||||
#include <QtDebug>
|
||||
#include <QtGlobal>
|
||||
#include <QDateTime>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDataStream>
|
||||
|
||||
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<char>(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;
|
||||
}
|
225
external/simpleCrypt/simplecrypt.h
vendored
Normal file
225
external/simpleCrypt/simplecrypt.h
vendored
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
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 SIMPLECRYPT_H
|
||||
#define SIMPLECRYPT_H
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QFlags>
|
||||
|
||||
/**
|
||||
@ short Simple encrypt*ion 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 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 t*he 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. */
|
||||
};
|
||||
|
||||
/**
|
||||
Constructor. *
|
||||
|
||||
Constructs a SimpleCrypt instance without a valid key set on it.
|
||||
*/
|
||||
SimpleCrypt();
|
||||
/**
|
||||
Constructor. *
|
||||
|
||||
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 compress*ion 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 cypher*text 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 cypher*text 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 cypher*text 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 cypher*text 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,
|
||||
CryptoFlagCompression = 0x01,
|
||||
CryptoFlagChecksum = 0x02,
|
||||
CryptoFlagHash = 0x04
|
||||
};
|
||||
Q_DECLARE_FLAGS(CryptoFlags, CryptoFlag);
|
||||
private:
|
||||
|
||||
void splitKey();
|
||||
|
||||
quint64 m_key;
|
||||
QVector<char> m_keyParts;
|
||||
CompressionMode m_compressionMode;
|
||||
IntegrityProtectionMode m_protectionMode;
|
||||
Error m_lastError;
|
||||
};
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(SimpleCrypt::CryptoFlags)
|
||||
|
||||
#endif // SimpleCrypt_H
|
2
main.cpp
2
main.cpp
@ -116,6 +116,7 @@ int main(int argc, char *argv[])
|
||||
QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest);
|
||||
QObject::connect(&w, &Squawk::requestVCard, squawk, &Core::Squawk::requestVCard);
|
||||
QObject::connect(&w, &Squawk::uploadVCard, squawk, &Core::Squawk::uploadVCard);
|
||||
QObject::connect(&w, &Squawk::responsePassword, squawk, &Core::Squawk::responsePassword);
|
||||
|
||||
QObject::connect(squawk, &Core::Squawk::newAccount, &w, &Squawk::newAccount);
|
||||
QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact);
|
||||
@ -146,6 +147,7 @@ int main(int argc, char *argv[])
|
||||
QObject::connect(squawk, &Core::Squawk::uploadFileProgress, &w, &Squawk::fileProgress);
|
||||
QObject::connect(squawk, &Core::Squawk::uploadFileError, &w, &Squawk::fileError);
|
||||
QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard);
|
||||
QObject::connect(squawk, &Core::Squawk::requestPassword, &w, &Squawk::requestPassword);
|
||||
QObject::connect(squawk, &Core::Squawk::ready, &w, &Squawk::readSettings);
|
||||
|
||||
coreThread->start();
|
||||
|
@ -110,11 +110,11 @@ static const std::deque<QString> messageStateThemeIcons = {"state-offline", "sta
|
||||
enum class AccountPassword {
|
||||
plain,
|
||||
jammed,
|
||||
kwallet,
|
||||
alwaysAsk
|
||||
alwaysAsk,
|
||||
kwallet
|
||||
};
|
||||
Q_ENUM_NS(AccountPassword)
|
||||
static const AccountPassword AccountPasswordHighest = AccountPassword::alwaysAsk;
|
||||
static const AccountPassword AccountPasswordHighest = AccountPassword::kwallet;
|
||||
static const AccountPassword AccountPasswordLowest = AccountPassword::plain;
|
||||
|
||||
}
|
||||
|
@ -69,8 +69,8 @@ Shared::Global::Global():
|
||||
accountPassword({
|
||||
tr("Plain"),
|
||||
tr("Jammed"),
|
||||
tr("KWallet"),
|
||||
tr("Always Ask")
|
||||
tr("Always Ask"),
|
||||
tr("KWallet")
|
||||
})
|
||||
{
|
||||
if (instance != 0) {
|
||||
|
@ -39,6 +39,10 @@ Models::Account::Account(const QMap<QString, QVariant>& data, Models::Item* pare
|
||||
if (aItr != data.end()) {
|
||||
setAvailability(aItr.value().toUInt());
|
||||
}
|
||||
QMap<QString, QVariant>::const_iterator pItr = data.find("passwordType");
|
||||
if (pItr != data.end()) {
|
||||
setPasswordType(pItr.value().toUInt());
|
||||
}
|
||||
}
|
||||
|
||||
Models::Account::~Account()
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "ui_squawk.h"
|
||||
#include <QDebug>
|
||||
#include <QIcon>
|
||||
#include <QInputDialog>
|
||||
|
||||
Squawk::Squawk(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
@ -31,7 +30,9 @@ Squawk::Squawk(QWidget *parent) :
|
||||
contextMenu(new QMenu()),
|
||||
dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()),
|
||||
requestedFiles(),
|
||||
vCards()
|
||||
vCards(),
|
||||
requestedAccountsForPasswords(),
|
||||
prompt(0)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
m_ui->roster->setModel(&rosterModel);
|
||||
@ -62,6 +63,18 @@ Squawk::Squawk(QWidget *parent) :
|
||||
if (testAttribute(Qt::WA_TranslucentBackground)) {
|
||||
m_ui->roster->viewport()->setAutoFillBackground(false);
|
||||
}
|
||||
|
||||
QSettings settings;
|
||||
settings.beginGroup("ui");
|
||||
settings.beginGroup("window");
|
||||
if (settings.contains("geometry")) {
|
||||
restoreGeometry(settings.value("geometry").toByteArray());
|
||||
}
|
||||
if (settings.contains("state")) {
|
||||
restoreState(settings.value("state").toByteArray());
|
||||
}
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
Squawk::~Squawk() {
|
||||
@ -871,14 +884,6 @@ void Squawk::readSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
settings.beginGroup("ui");
|
||||
settings.beginGroup("window");
|
||||
if (settings.contains("geometry")) {
|
||||
restoreGeometry(settings.value("geometry").toByteArray());
|
||||
}
|
||||
if (settings.contains("state")) {
|
||||
restoreState(settings.value("state").toByteArray());
|
||||
}
|
||||
settings.endGroup();
|
||||
|
||||
if (settings.contains("availability")) {
|
||||
int avail = settings.value("availability").toInt();
|
||||
@ -958,3 +963,48 @@ void Squawk::onItemCollepsed(const QModelIndex& index)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Squawk::requestPassword(const QString& account)
|
||||
{
|
||||
requestedAccountsForPasswords.push_back(account);
|
||||
checkNextAccountForPassword();
|
||||
}
|
||||
|
||||
void Squawk::checkNextAccountForPassword()
|
||||
{
|
||||
if (prompt == 0 && requestedAccountsForPasswords.size() > 0) {
|
||||
prompt = new QInputDialog(this);
|
||||
QString accName = requestedAccountsForPasswords.front();
|
||||
connect(prompt, &QDialog::accepted, this, &Squawk::onPasswordPromptAccepted);
|
||||
connect(prompt, &QDialog::rejected, this, &Squawk::onPasswordPromptRejected);
|
||||
prompt->setInputMode(QInputDialog::TextInput);
|
||||
prompt->setTextEchoMode(QLineEdit::Password);
|
||||
prompt->setLabelText(tr("Input the password for account %1").arg(accName));
|
||||
prompt->setWindowTitle(tr("Password for account %1").arg(accName));
|
||||
prompt->setTextValue("");
|
||||
prompt->exec();
|
||||
}
|
||||
}
|
||||
|
||||
void Squawk::onPasswordPromptAccepted()
|
||||
{
|
||||
emit responsePassword(requestedAccountsForPasswords.front(), prompt->textValue());
|
||||
onPasswordPromptDone();
|
||||
}
|
||||
|
||||
void Squawk::onPasswordPromptDone()
|
||||
{
|
||||
prompt->deleteLater();
|
||||
prompt = 0;
|
||||
requestedAccountsForPasswords.pop_front();
|
||||
checkNextAccountForPassword();
|
||||
}
|
||||
|
||||
void Squawk::onPasswordPromptRejected()
|
||||
{
|
||||
//for now it's the same on reject and on accept, but one day I'm gonna make
|
||||
//"Asking for the password again on the authentication failure" feature
|
||||
//and here I'll be able to break the circle of password requests
|
||||
emit responsePassword(requestedAccountsForPasswords.front(), prompt->textValue());
|
||||
onPasswordPromptDone();
|
||||
}
|
||||
|
10
ui/squawk.h
10
ui/squawk.h
@ -24,6 +24,7 @@
|
||||
#include <QCloseEvent>
|
||||
#include <QtDBus/QDBusInterface>
|
||||
#include <QSettings>
|
||||
#include <QInputDialog>
|
||||
|
||||
#include <deque>
|
||||
#include <map>
|
||||
@ -79,6 +80,7 @@ signals:
|
||||
void downloadFileRequest(const QString& messageId, const QString& url);
|
||||
void requestVCard(const QString& account, const QString& jid);
|
||||
void uploadVCard(const QString& account, const Shared::VCard& card);
|
||||
void responsePassword(const QString& account, const QString& password);
|
||||
|
||||
public slots:
|
||||
void readSettings();
|
||||
@ -107,6 +109,7 @@ public slots:
|
||||
void fileProgress(const QString& messageId, qreal value);
|
||||
void responseVCard(const QString& jid, const Shared::VCard& card);
|
||||
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
||||
void requestPassword(const QString& account);
|
||||
|
||||
private:
|
||||
typedef std::map<Models::Roster::ElId, Conversation*> Conversations;
|
||||
@ -119,6 +122,8 @@ private:
|
||||
QDBusInterface dbus;
|
||||
std::map<QString, std::set<Models::Roster::ElId>> requestedFiles;
|
||||
std::map<QString, VCard*> vCards;
|
||||
std::deque<QString> requestedAccountsForPasswords;
|
||||
QInputDialog* prompt;
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent * event) override;
|
||||
@ -146,7 +151,12 @@ private slots:
|
||||
void onConversationRequestLocalFile(const QString& messageId, const QString& url);
|
||||
void onConversationDownloadFile(const QString& messageId, const QString& url);
|
||||
void onItemCollepsed(const QModelIndex& index);
|
||||
void onPasswordPromptAccepted();
|
||||
void onPasswordPromptRejected();
|
||||
|
||||
private:
|
||||
void checkNextAccountForPassword();
|
||||
void onPasswordPromptDone();
|
||||
};
|
||||
|
||||
#endif // SQUAWK_H
|
||||
|
Loading…
Reference in New Issue
Block a user