/* * Squawk messenger. * Copyright (C) 2019 Yury Gubich * * 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 . */ #include "jammer.h" #include #include #include struct CipherCtxDeleter { void operator()(EVP_CIPHER_CTX* ctx) const { EVP_CIPHER_CTX_free(ctx); } }; typedef std::unique_ptr 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(&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(data.data());} const unsigned char* Core::Jammer::toUChar(const QByteArray& data) { return reinterpret_cast(data.constData());}