启用AES加密
同时考虑到低版本“记住密码”中缺少AES密钥的情况
This commit is contained in:
notify 2023-04-15 12:06:39 +08:00 committed by GitHub
parent 4d0d0c1d60
commit dd6093a5ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 145 additions and 12 deletions

View File

@ -26,6 +26,7 @@ QtObject {
property string screenName: ""
property string password: ""
property string cipherText
property string aeskey
// Client data
property int roomCapacity: 0

View File

@ -19,17 +19,23 @@ let sheduled_download = "";
callbacks["NetworkDelayTest"] = function(jsonData) {
// jsonData: RSA pub key
let cipherText
let cipherText;
let aeskey;
if (config.savedPassword[config.serverAddr] !== undefined
&& config.savedPassword[config.serverAddr].shorten_password === config.password) {
cipherText = config.savedPassword[config.serverAddr].password;
aeskey = config.savedPassword[config.serverAddr].key;
config.aeskey = aeskey;
Backend.setAESKey(aeskey);
if (Debugging)
console.log("use remembered password", config.password);
} else {
cipherText = Backend.pubEncrypt(jsonData, config.password);
config.aeskey = Backend.getAESKey();
}
config.cipherText = cipherText;
Backend.replyDelayTest(config.screenName, cipherText);
Backend.installAESKey();
}
callbacks["ErrorMsg"] = function(jsonData) {
@ -69,6 +75,7 @@ callbacks["EnterLobby"] = function(jsonData) {
config.savedPassword[config.serverAddr] = {
username: config.screenName,
password: config.cipherText,
key: config.aeskey,
shorten_password: config.cipherText.slice(0, 8)
}
mainStack.push(lobby);

View File

@ -68,3 +68,5 @@ void Client::removePlayer(int id) {
void Client::clearPlayers() { players.clear(); }
lua_State *Client::getLuaState() { return L; }
void Client::installAESKey(const QByteArray &key) { router->installAESKey(key); }

View File

@ -29,6 +29,7 @@ public:
Q_INVOKABLE void clearPlayers();
lua_State *getLuaState();
void installAESKey(const QByteArray &key);
signals:
void error_message(const QString &msg);

View File

@ -1,8 +1,13 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "client_socket.h"
#include <openssl/aes.h>
#include <qrandom.h>
ClientSocket::ClientSocket() : socket(new QTcpSocket(this)) { init(); }
ClientSocket::ClientSocket() : socket(new QTcpSocket(this)) {
aes_ready = false;
init();
}
ClientSocket::ClientSocket(QTcpSocket *socket) {
socket->setParent(this);
@ -28,6 +33,7 @@ void ClientSocket::connectToHost(const QString &address, ushort port) {
void ClientSocket::getMessage() {
while (socket->canReadLine()) {
auto msg = socket->readLine();
msg = aesDecrypt(msg);
if (msg.startsWith("Compressed")) {
msg = msg.sliced(10);
msg = qUncompress(QByteArray::fromBase64(msg));
@ -36,18 +42,22 @@ void ClientSocket::getMessage() {
}
}
void ClientSocket::disconnectFromHost() { socket->disconnectFromHost(); }
void ClientSocket::disconnectFromHost() {
aes_ready = false;
socket->disconnectFromHost();
}
void ClientSocket::send(const QByteArray &msg) {
QByteArray _msg;
if (msg.length() >= 1024) {
auto comp = qCompress(msg);
auto _msg = "Compressed" + comp.toBase64() + "\n";
socket->write(_msg);
socket->flush();
_msg = "Compressed" + comp.toBase64();
_msg = aesEncrypt(_msg) + "\n";
} else {
_msg = aesEncrypt(msg) + "\n";
}
socket->write(msg);
if (!msg.endsWith("\n"))
socket->write("\n");
socket->write(_msg);
socket->flush();
}
@ -96,3 +106,62 @@ void ClientSocket::raiseError(QAbstractSocket::SocketError socket_error) {
.arg(socket_error)
.arg(reason));
}
void ClientSocket::installAESKey(const QByteArray &key) {
if (key.length() != 32) {
return;
}
auto key_ = QByteArray::fromHex(key);
AES_set_encrypt_key((const unsigned char *)key_.data(), 16 * 8, &aes_key);
aes_ready = true;
}
QByteArray ClientSocket::aesEncrypt(const QByteArray &in) {
if (!aes_ready) {
return in;
}
int num = 0;
QByteArray out;
out.resize(in.length());
auto rand_generator = QRandomGenerator::securelySeeded();
QByteArray iv;
iv.append(QByteArray::number(rand_generator.generate64(), 16));
iv.append(QByteArray::number(rand_generator.generate64(), 16));
if (iv.length() < 32) {
iv.append(QByteArray("0").repeated(32 - iv.length()));
}
auto iv_raw = QByteArray::fromHex(iv);
unsigned char tempIv[16];
strncpy((char *)tempIv, iv_raw.constData(), 16);
AES_cfb128_encrypt((const unsigned char *)in.constData(),
(unsigned char *)out.data(), in.length(), &aes_key, tempIv,
&num, AES_ENCRYPT);
return iv + out.toBase64();
}
QByteArray ClientSocket::aesDecrypt(const QByteArray &in) {
if (!aes_ready) {
return in;
}
int num = 0;
auto iv = in.first(32);
auto aes_iv = QByteArray::fromHex(iv);
auto real_in = in;
real_in.remove(0, 32);
auto inenc = QByteArray::fromBase64(real_in);
QByteArray out;
out.resize(inenc.length());
unsigned char tempIv[16];
strncpy((char *)tempIv, aes_iv.constData(), 16);
AES_cfb128_encrypt((const unsigned char *)inenc.constData(),
(unsigned char *)out.data(), inenc.length(), &aes_key,
tempIv, &num, AES_DECRYPT);
return out;
}

View File

@ -3,6 +3,8 @@
#ifndef _CLIENT_SOCKET_H
#define _CLIENT_SOCKET_H
#include <openssl/aes.h>
class ClientSocket : public QObject {
Q_OBJECT
@ -13,6 +15,7 @@ public:
void connectToHost(const QString &address = "127.0.0.1", ushort port = 9527u);
void disconnectFromHost();
void installAESKey(const QByteArray &key);
void send(const QByteArray& msg);
bool isConnected() const;
QString peerName() const;
@ -30,6 +33,10 @@ private slots:
void raiseError(QAbstractSocket::SocketError error);
private:
QByteArray aesEncrypt(const QByteArray &in);
QByteArray aesDecrypt(const QByteArray &out);
AES_KEY aes_key;
bool aes_ready;
QTcpSocket *socket;
void init();
};

View File

@ -43,6 +43,10 @@ void Router::setSocket(ClientSocket *socket) {
}
}
void Router::installAESKey(const QByteArray &key) {
socket->installAESKey(key);
}
#ifndef FK_CLIENT_ONLY
void Router::setReplyReadySemaphore(QSemaphore *semaphore) {
extraReplyReadySemaphore = semaphore;

View File

@ -30,6 +30,7 @@ public:
ClientSocket *getSocket() const;
void setSocket(ClientSocket *socket);
void installAESKey(const QByteArray &key);
#ifndef FK_CLIENT_ONLY
void setReplyReadySemaphore(QSemaphore *semaphore);

View File

@ -227,6 +227,15 @@ void Server::handleNameAndPassword(ClientSocket *client, const QString &name,
buf, rsa, RSA_PKCS1_PADDING);
auto decrypted_pw =
QByteArray::fromRawData((const char *)buf, strlen((const char *)buf));
if (decrypted_pw.length() > 32) {
auto aes_bytes = decrypted_pw.first(32);
client->installAESKey(aes_bytes);
decrypted_pw.remove(0, 32);
} else {
decrypted_pw = "\xFF";
}
bool passed = false;
QString error_msg;
QJsonArray result;

View File

@ -1,11 +1,14 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "qmlbackend.h"
#include <QClipboard>
#include <QMediaPlayer>
#include <qaudiooutput.h>
#include <qmediaplayer.h>
#include <qrandom.h>
#include <QClipboard>
#include <QMediaPlayer>
#include <cstdlib>
#ifndef Q_OS_WASM
#include "server.h"
#endif
@ -179,14 +182,29 @@ QString QmlBackend::callLuaFunction(const QString &func_name,
}
QString QmlBackend::pubEncrypt(const QString &key, const QString &data) {
// 在用公钥加密口令时也随机生成AES密钥/IV并随着口令一起加密
// AES密钥和IV都是固定16字节的所以可以放在开头
auto key_bytes = key.toLatin1();
BIO *keyio = BIO_new_mem_buf(key_bytes.constData(), -1);
PEM_read_bio_RSAPublicKey(keyio, &rsa, NULL, NULL);
BIO_free_all(keyio);
auto data_bytes = data.toUtf8();
auto rand_generator = QRandomGenerator::securelySeeded();
QByteArray aes_key_;
for (int i = 0; i < 2; i++) {
aes_key_.append(QByteArray::number(rand_generator.generate64(), 16));
}
if (aes_key_.length() < 32) {
aes_key_.append(QByteArray("0").repeated(32 - aes_key_.length()));
}
aes_key = aes_key_;
data_bytes.prepend(aes_key_);
unsigned char buf[RSA_size(rsa)];
RSA_public_encrypt(data.length(),
RSA_public_encrypt(data.length() + 32,
(const unsigned char *)data_bytes.constData(), buf, rsa,
RSA_PKCS1_PADDING);
return QByteArray::fromRawData((const char *)buf, RSA_size(rsa)).toBase64();
@ -258,3 +276,11 @@ void QmlBackend::playSound(const QString &name, int index) {
void QmlBackend::copyToClipboard(const QString &s) {
QGuiApplication::clipboard()->setText(s);
}
void QmlBackend::setAESKey(const QString &key) { aes_key = key; }
QString QmlBackend::getAESKey() const { return aes_key; }
void QmlBackend::installAESKey() {
ClientInstance->installAESKey(aes_key.toLatin1());
}

View File

@ -3,6 +3,7 @@
#ifndef _QMLBACKEND_H
#define _QMLBACKEND_H
#include <qtmetamacros.h>
class QmlBackend : public QObject {
Q_OBJECT
public:
@ -42,12 +43,17 @@ public:
Q_INVOKABLE void copyToClipboard(const QString &s);
Q_INVOKABLE void setAESKey(const QString &key);
Q_INVOKABLE QString getAESKey() const;
Q_INVOKABLE void installAESKey();
signals:
void notifyUI(const QString &command, const QString &jsonData);
private:
QQmlApplicationEngine *engine;
RSA *rsa;
QString aes_key;
void pushLuaValue(lua_State *L, QVariant v);
};