95 lines
3.3 KiB
C++
95 lines
3.3 KiB
C++
/*
|
|
* Squawk messenger.
|
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "jammer.h"
|
|
|
|
#include <memory>
|
|
|
|
#include <openssl/evp.h>
|
|
#include <openssl/err.h>
|
|
|
|
struct CipherCtxDeleter {
|
|
void operator()(EVP_CIPHER_CTX* ctx) const {
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
}
|
|
};
|
|
typedef std::unique_ptr<EVP_CIPHER_CTX, CipherCtxDeleter> CipherCtx;
|
|
|
|
QString Core::Jammer::encrypt(const QString& plaintext, qint64 key) {
|
|
QByteArray encryptedData = process(plaintext.toUtf8(), intToKey(key), true);
|
|
|
|
return QString::fromUtf8(encryptedData.toHex());
|
|
}
|
|
|
|
QString Core::Jammer::decrypt(const QString& ciphertext, qint64 key) {
|
|
QByteArray encryptedData = QByteArray::fromHex(ciphertext.toUtf8());
|
|
QByteArray decryptedData = process(encryptedData, intToKey(key), false);
|
|
|
|
return QString::fromUtf8(decryptedData);
|
|
}
|
|
|
|
std::string Core::Jammer::getOpenSSLErrorString() {
|
|
unsigned long errCode = ERR_get_error();
|
|
if (errCode == 0) {
|
|
return "No OpenSSL error";
|
|
}
|
|
const char *errMsg = ERR_reason_error_string(errCode);
|
|
return errMsg ? std::string(errMsg) : "Unknown OpenSSL error";
|
|
}
|
|
|
|
QByteArray Core::Jammer::process(const QByteArray& input, const QByteArray& key, bool encrypt) {
|
|
CipherCtx ctx(EVP_CIPHER_CTX_new());
|
|
if (!ctx)
|
|
throw std::runtime_error("Failed to create password jammer context");
|
|
|
|
QByteArray output(input.size() + 16, 0);
|
|
int outputLength = 0;
|
|
int finalLength = 0;
|
|
|
|
if (!ctx)
|
|
throw std::runtime_error("Failed to create EVP_CIPHER_CTX: " + getOpenSSLErrorString());
|
|
|
|
if (EVP_CipherInit_ex(ctx.get(), EVP_chacha20(), nullptr, toUChar(key), nullptr, encrypt) != 1)
|
|
throw std::runtime_error("EVP_CipherInit_ex failed. " + getOpenSSLErrorString());
|
|
|
|
if (EVP_CipherUpdate(ctx.get(), toUChar(output), &outputLength, toUChar(input), input.size()) != 1)
|
|
throw std::runtime_error("EVP_CipherUpdate failed. " + getOpenSSLErrorString());
|
|
|
|
if (EVP_CipherFinal_ex(ctx.get(), toUChar(output) + outputLength, &finalLength) != 1)
|
|
throw std::runtime_error("EVP_CipherFinal_ex failed. " + getOpenSSLErrorString());
|
|
|
|
output.resize(outputLength + finalLength);
|
|
|
|
return output;
|
|
}
|
|
|
|
QByteArray Core::Jammer::intToKey(qint64 key, int keySize) {
|
|
QByteArray keyBytes(reinterpret_cast<const char *>(&key), sizeof(key));
|
|
while (keyBytes.size() < keySize)
|
|
keyBytes.append(keyBytes);
|
|
|
|
keyBytes.truncate(keySize);
|
|
return keyBytes;
|
|
}
|
|
|
|
unsigned char* Core::Jammer::toUChar(QByteArray& data) {
|
|
return reinterpret_cast<unsigned char *>(data.data());}
|
|
|
|
const unsigned char* Core::Jammer::toUChar(const QByteArray& data) {
|
|
return reinterpret_cast<const unsigned char *>(data.constData());}
|