Compare commits
3 Commits
a529e82912
...
3b83e60352
Author | SHA1 | Date |
---|---|---|
laptoy | 3b83e60352 | 6 months ago |
laptoy | f6130b2d1f | 6 months ago |
laptoy | e7778c32b7 | 6 months ago |
@ -0,0 +1,575 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
*
|
||||||
|
* (c) 2019 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||||
|
*
|
||||||
|
* QGroundControl is licensed according to the terms in the file
|
||||||
|
* COPYING.md in the root of the source code directory.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "PairingManager.h"
|
||||||
|
#include "SettingsManager.h"
|
||||||
|
#include "MicrohardManager.h"
|
||||||
|
#include "QGCApplication.h"
|
||||||
|
#include "QGCCorePlugin.h"
|
||||||
|
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QMutexLocker>
|
||||||
|
|
||||||
|
QGC_LOGGING_CATEGORY(PairingManagerLog, "PairingManagerLog")
|
||||||
|
|
||||||
|
static const char* jsonFileName = "pairing.json";
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static QString
|
||||||
|
random_string(uint length)
|
||||||
|
{
|
||||||
|
auto randchar = []() -> char
|
||||||
|
{
|
||||||
|
const char charset[] =
|
||||||
|
"0123456789"
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
"abcdefghijklmnopqrstuvwxyz";
|
||||||
|
const uint max_index = (sizeof(charset) - 1);
|
||||||
|
return charset[static_cast<uint>(rand()) % max_index];
|
||||||
|
};
|
||||||
|
std::string str(length, 0);
|
||||||
|
std::generate_n(str.begin(), length, randchar);
|
||||||
|
return QString::fromStdString(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
PairingManager::PairingManager(QGCApplication* app, QGCToolbox* toolbox)
|
||||||
|
: QGCTool(app, toolbox)
|
||||||
|
, _aes("J6+KuWh9K2!hG(F'", 0x368de30e8ec063ce)
|
||||||
|
{
|
||||||
|
_jsonFileName = QDir::temp().filePath(jsonFileName);
|
||||||
|
connect(this, &PairingManager::parsePairingJson, this, &PairingManager::_parsePairingJson);
|
||||||
|
connect(this, &PairingManager::setPairingStatus, this, &PairingManager::_setPairingStatus);
|
||||||
|
connect(this, &PairingManager::startUpload, this, &PairingManager::_startUpload);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
PairingManager::~PairingManager()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
PairingManager::setToolbox(QGCToolbox *toolbox)
|
||||||
|
{
|
||||||
|
QGCTool::setToolbox(toolbox);
|
||||||
|
_updatePairedDeviceNameList();
|
||||||
|
emit pairedListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::_pairingCompleted(QString name)
|
||||||
|
{
|
||||||
|
_writeJson(_jsonDoc, _pairingCacheFile(name));
|
||||||
|
_remotePairingMap["NM"] = name;
|
||||||
|
_lastPaired = name;
|
||||||
|
_updatePairedDeviceNameList();
|
||||||
|
emit pairedListChanged();
|
||||||
|
emit pairedVehicleChanged();
|
||||||
|
//_app->informationMessageBoxOnMainThread("", tr("Paired with %1").arg(name));
|
||||||
|
setPairingStatus(PairingSuccess, tr("Pairing Successfull"));
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::_connectionCompleted(QString /*name*/)
|
||||||
|
{
|
||||||
|
//QString pwd = _remotePairingMap["PWD"].toString();
|
||||||
|
//_toolbox->microhardManager()->switchToConnectionEncryptionKey(pwd);
|
||||||
|
//_app->informationMessageBoxOnMainThread("", tr("Connected to %1").arg(name));
|
||||||
|
setPairingStatus(PairingConnected, tr("Connection Successfull"));
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::_startUpload(QString pairURL, QJsonDocument jsonDoc)
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&_uploadMutex);
|
||||||
|
if (_uploadManager != nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_uploadManager = new QNetworkAccessManager(this);
|
||||||
|
|
||||||
|
QString str = jsonDoc.toJson(QJsonDocument::JsonFormat::Compact);
|
||||||
|
qCDebug(PairingManagerLog) << "Starting upload to: " << pairURL << " " << str;
|
||||||
|
_uploadData = QString::fromStdString(_aes.encrypt(str.toStdString()));
|
||||||
|
_uploadURL = pairURL;
|
||||||
|
_startUploadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::_startUploadRequest()
|
||||||
|
{
|
||||||
|
QNetworkRequest req;
|
||||||
|
req.setUrl(QUrl(_uploadURL));
|
||||||
|
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||||
|
QNetworkReply *reply = _uploadManager->post(req, _uploadData.toUtf8());
|
||||||
|
connect(reply, &QNetworkReply::finished, this, &PairingManager::_uploadFinished);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::_stopUpload()
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&_uploadMutex);
|
||||||
|
if (_uploadManager != nullptr) {
|
||||||
|
delete _uploadManager;
|
||||||
|
_uploadManager = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::_uploadFinished()
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&_uploadMutex);
|
||||||
|
QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());
|
||||||
|
if (reply) {
|
||||||
|
if (_uploadManager != nullptr) {
|
||||||
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
|
qCDebug(PairingManagerLog) << "Upload finished.";
|
||||||
|
QByteArray bytes = reply->readAll();
|
||||||
|
QString str = QString::fromUtf8(bytes.data(), bytes.size());
|
||||||
|
qCDebug(PairingManagerLog) << "Reply: " << str;
|
||||||
|
auto a = str.split(QRegExp("\\s+"));
|
||||||
|
if (a[0] == "Accepted" && a.length() > 1) {
|
||||||
|
_pairingCompleted(a[1]);
|
||||||
|
} else if (a[0] == "Connected" && a.length() > 1) {
|
||||||
|
_connectionCompleted(a[1]);
|
||||||
|
} else if (a[0] == "Connection" && a.length() > 1) {
|
||||||
|
setPairingStatus(PairingConnectionRejected, tr("Connection Rejected"));
|
||||||
|
qCDebug(PairingManagerLog) << "Connection error: " << str;
|
||||||
|
} else {
|
||||||
|
setPairingStatus(PairingRejected, tr("Pairing Rejected"));
|
||||||
|
qCDebug(PairingManagerLog) << "Pairing error: " << str;
|
||||||
|
}
|
||||||
|
_uploadManager->deleteLater();
|
||||||
|
_uploadManager = nullptr;
|
||||||
|
} else {
|
||||||
|
if(++_pairRetryCount > 3) {
|
||||||
|
qCDebug(PairingManagerLog) << "Giving up";
|
||||||
|
setPairingStatus(PairingError, tr("No Response From Vehicle"));
|
||||||
|
_uploadManager->deleteLater();
|
||||||
|
_uploadManager = nullptr;
|
||||||
|
} else {
|
||||||
|
qCDebug(PairingManagerLog) << "Upload error: " + reply->errorString();
|
||||||
|
_startUploadRequest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::_parsePairingJsonFile()
|
||||||
|
{
|
||||||
|
QFile file(_jsonFileName);
|
||||||
|
file.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||||
|
QString json = file.readAll();
|
||||||
|
file.remove();
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
jsonReceived(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::connectToPairedDevice(QString name)
|
||||||
|
{
|
||||||
|
setPairingStatus(PairingConnecting, tr("Connecting to %1").arg(name));
|
||||||
|
QFile file(_pairingCacheFile(name));
|
||||||
|
file.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||||
|
QString json = file.readAll();
|
||||||
|
jsonReceived(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::removePairedDevice(QString name)
|
||||||
|
{
|
||||||
|
QFile file(_pairingCacheFile(name));
|
||||||
|
file.remove();
|
||||||
|
_updatePairedDeviceNameList();
|
||||||
|
emit pairedListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::_updatePairedDeviceNameList()
|
||||||
|
{
|
||||||
|
_deviceList.clear();
|
||||||
|
QDirIterator it(_pairingCacheDir().absolutePath(), QDir::Files);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
QFileInfo fileInfo(it.next());
|
||||||
|
_deviceList.append(fileInfo.fileName());
|
||||||
|
qCDebug(PairingManagerLog) << "Listing: " << fileInfo.fileName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
QString
|
||||||
|
PairingManager::_assumeMicrohardPairingJson()
|
||||||
|
{
|
||||||
|
QJsonDocument json;
|
||||||
|
QJsonObject jsonObject;
|
||||||
|
|
||||||
|
jsonObject.insert("LT", "MH");
|
||||||
|
jsonObject.insert("IP", "192.168.168.10");
|
||||||
|
jsonObject.insert("AIP", _toolbox->microhardManager()->remoteIPAddr());
|
||||||
|
jsonObject.insert("CU", _toolbox->microhardManager()->configUserName());
|
||||||
|
jsonObject.insert("CP", _toolbox->microhardManager()->configPassword());
|
||||||
|
jsonObject.insert("EK", _toolbox->microhardManager()->encryptionKey());
|
||||||
|
json.setObject(jsonObject);
|
||||||
|
|
||||||
|
return QString(json.toJson(QJsonDocument::Compact));
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::_parsePairingJson(QString jsonEnc)
|
||||||
|
{
|
||||||
|
QString json = QString::fromStdString(_aes.decrypt(jsonEnc.toStdString()));
|
||||||
|
if (json == "") {
|
||||||
|
json = jsonEnc;
|
||||||
|
}
|
||||||
|
qCDebug(PairingManagerLog) << "Parsing JSON: " << json;
|
||||||
|
|
||||||
|
_jsonDoc = QJsonDocument::fromJson(json.toUtf8());
|
||||||
|
|
||||||
|
if (_jsonDoc.isNull()) {
|
||||||
|
setPairingStatus(PairingError, tr("Invalid Pairing File"));
|
||||||
|
qCDebug(PairingManagerLog) << "Failed to create Pairing JSON doc.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_jsonDoc.isObject()) {
|
||||||
|
setPairingStatus(PairingError, tr("Error Parsing Pairing File"));
|
||||||
|
qCDebug(PairingManagerLog) << "Pairing JSON is not an object.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject jsonObj = _jsonDoc.object();
|
||||||
|
|
||||||
|
if (jsonObj.isEmpty()) {
|
||||||
|
setPairingStatus(PairingError, tr("Error Parsing Pairing File"));
|
||||||
|
qCDebug(PairingManagerLog) << "Pairing JSON object is empty.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_remotePairingMap = jsonObj.toVariantMap();
|
||||||
|
QString linkType = _remotePairingMap["LT"].toString();
|
||||||
|
QString pport = _remotePairingMap["PP"].toString();
|
||||||
|
if (pport.length()==0) {
|
||||||
|
pport = "29351";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linkType.length()==0) {
|
||||||
|
setPairingStatus(PairingError, tr("Error Parsing Pairing File"));
|
||||||
|
qCDebug(PairingManagerLog) << "Pairing JSON is malformed.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_toolbox->microhardManager()->switchToPairingEncryptionKey();
|
||||||
|
|
||||||
|
QString pairURL = "http://" + _remotePairingMap["IP"].toString() + ":" + pport;
|
||||||
|
bool connecting = jsonObj.contains("PWD");
|
||||||
|
QJsonDocument jsonDoc;
|
||||||
|
|
||||||
|
if (!connecting) {
|
||||||
|
pairURL += + "/pair";
|
||||||
|
QString pwd = random_string(8);
|
||||||
|
// TODO generate certificates
|
||||||
|
QString cert1 = "";
|
||||||
|
QString cert2 = "";
|
||||||
|
jsonObj.insert("PWD", pwd);
|
||||||
|
jsonObj.insert("CERT1", cert1);
|
||||||
|
jsonObj.insert("CERT2", cert2);
|
||||||
|
_jsonDoc.setObject(jsonObj);
|
||||||
|
if (linkType == "ZT") {
|
||||||
|
jsonDoc = _createZeroTierPairingJson(cert1);
|
||||||
|
} else if (linkType == "MH") {
|
||||||
|
jsonDoc = _createMicrohardPairingJson(pwd, cert1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pairURL += + "/connect";
|
||||||
|
QString cert2 = _remotePairingMap["CERT2"].toString();
|
||||||
|
if (linkType == "ZT") {
|
||||||
|
jsonDoc = _createZeroTierConnectJson(cert2);
|
||||||
|
} else if (linkType == "MH") {
|
||||||
|
jsonDoc = _createMicrohardConnectJson(cert2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linkType == "ZT") {
|
||||||
|
_toolbox->settingsManager()->appSettings()->enableMicrohard()->setRawValue(false);
|
||||||
|
_toolbox->settingsManager()->appSettings()->enableTaisync()->setRawValue(false);
|
||||||
|
emit startUpload(pairURL, jsonDoc);
|
||||||
|
} else if (linkType == "MH") {
|
||||||
|
_toolbox->settingsManager()->appSettings()->enableMicrohard()->setRawValue(true);
|
||||||
|
_toolbox->settingsManager()->appSettings()->enableTaisync()->setRawValue(false);
|
||||||
|
if (_remotePairingMap.contains("AIP")) {
|
||||||
|
_toolbox->microhardManager()->setRemoteIPAddr(_remotePairingMap["AIP"].toString());
|
||||||
|
}
|
||||||
|
if (_remotePairingMap.contains("CU")) {
|
||||||
|
_toolbox->microhardManager()->setConfigUserName(_remotePairingMap["CU"].toString());
|
||||||
|
}
|
||||||
|
if (_remotePairingMap.contains("CP")) {
|
||||||
|
_toolbox->microhardManager()->setConfigPassword(_remotePairingMap["CP"].toString());
|
||||||
|
}
|
||||||
|
if (_remotePairingMap.contains("EK") && !connecting) {
|
||||||
|
_toolbox->microhardManager()->setEncryptionKey(_remotePairingMap["EK"].toString());
|
||||||
|
}
|
||||||
|
_toolbox->microhardManager()->updateSettings();
|
||||||
|
emit startUpload(pairURL, jsonDoc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
QString
|
||||||
|
PairingManager::_getLocalIPInNetwork(QString remoteIP, int num)
|
||||||
|
{
|
||||||
|
QStringList pieces = remoteIP.split(".");
|
||||||
|
QString ipPrefix = "";
|
||||||
|
for (int i = 0; i<num && i<pieces.length(); i++) {
|
||||||
|
ipPrefix += pieces[i] + ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
const QHostAddress &localhost = QHostAddress(QHostAddress::LocalHost);
|
||||||
|
for (const QHostAddress &address: QNetworkInterface::allAddresses()) {
|
||||||
|
if (address.protocol() == QAbstractSocket::IPv4Protocol && address != localhost) {
|
||||||
|
if (address.toString().startsWith(ipPrefix)) {
|
||||||
|
return address.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
QDir
|
||||||
|
PairingManager::_pairingCacheDir()
|
||||||
|
{
|
||||||
|
const QString spath(QFileInfo(QSettings().fileName()).dir().absolutePath());
|
||||||
|
QDir dir = spath + QDir::separator() + "PairingCache";
|
||||||
|
if (!dir.exists()) {
|
||||||
|
dir.mkpath(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
QString
|
||||||
|
PairingManager::_pairingCacheFile(QString uavName)
|
||||||
|
{
|
||||||
|
return _pairingCacheDir().filePath(uavName);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::_writeJson(QJsonDocument &jsonDoc, QString fileName)
|
||||||
|
{
|
||||||
|
QString val = jsonDoc.toJson(QJsonDocument::JsonFormat::Compact);
|
||||||
|
qCDebug(PairingManagerLog) << "Write json " << val;
|
||||||
|
QString enc = QString::fromStdString(_aes.encrypt(val.toStdString()));
|
||||||
|
|
||||||
|
QFile file(fileName);
|
||||||
|
file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
|
||||||
|
file.write(enc.toUtf8());
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
QJsonDocument
|
||||||
|
PairingManager::_createZeroTierPairingJson(QString cert1)
|
||||||
|
{
|
||||||
|
QString localIP = _getLocalIPInNetwork(_remotePairingMap["IP"].toString(), 2);
|
||||||
|
|
||||||
|
QJsonObject jsonObj;
|
||||||
|
jsonObj.insert("LT", "ZT");
|
||||||
|
jsonObj.insert("IP", localIP);
|
||||||
|
jsonObj.insert("P", 14550);
|
||||||
|
jsonObj.insert("CERT1", cert1);
|
||||||
|
return QJsonDocument(jsonObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
QJsonDocument
|
||||||
|
PairingManager::_createMicrohardPairingJson(QString pwd, QString cert1)
|
||||||
|
{
|
||||||
|
QString localIP = _getLocalIPInNetwork(_remotePairingMap["IP"].toString(), 3);
|
||||||
|
|
||||||
|
QJsonObject jsonObj;
|
||||||
|
jsonObj.insert("LT", "MH");
|
||||||
|
jsonObj.insert("IP", localIP);
|
||||||
|
jsonObj.insert("P", 14550);
|
||||||
|
jsonObj.insert("PWD", pwd);
|
||||||
|
jsonObj.insert("CERT1", cert1);
|
||||||
|
return QJsonDocument(jsonObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
QJsonDocument
|
||||||
|
PairingManager::_createZeroTierConnectJson(QString cert2)
|
||||||
|
{
|
||||||
|
QString localIP = _getLocalIPInNetwork(_remotePairingMap["IP"].toString(), 2);
|
||||||
|
|
||||||
|
QJsonObject jsonObj;
|
||||||
|
jsonObj.insert("LT", "ZT");
|
||||||
|
jsonObj.insert("IP", localIP);
|
||||||
|
jsonObj.insert("P", 14550);
|
||||||
|
jsonObj.insert("CERT2", cert2);
|
||||||
|
return QJsonDocument(jsonObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
QJsonDocument
|
||||||
|
PairingManager::_createMicrohardConnectJson(QString cert2)
|
||||||
|
{
|
||||||
|
QString localIP = _getLocalIPInNetwork(_remotePairingMap["IP"].toString(), 3);
|
||||||
|
|
||||||
|
QJsonObject jsonObj;
|
||||||
|
jsonObj.insert("LT", "MH");
|
||||||
|
jsonObj.insert("IP", localIP);
|
||||||
|
jsonObj.insert("P", 14550);
|
||||||
|
jsonObj.insert("CERT2", cert2);
|
||||||
|
return QJsonDocument(jsonObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
QStringList
|
||||||
|
PairingManager::pairingLinkTypeStrings()
|
||||||
|
{
|
||||||
|
//-- Must follow same order as enum LinkType in LinkConfiguration.h
|
||||||
|
static QStringList list;
|
||||||
|
int i = 0;
|
||||||
|
if (!list.size()) {
|
||||||
|
#if defined QGC_ENABLE_QTNFC
|
||||||
|
list += tr("NFC");
|
||||||
|
_nfcIndex = i++;
|
||||||
|
#endif
|
||||||
|
#if defined QGC_GST_MICROHARD_ENABLED
|
||||||
|
list += tr("Microhard");
|
||||||
|
_microhardIndex = i++;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::_setPairingStatus(PairingStatus status, QString statusStr)
|
||||||
|
{
|
||||||
|
_status = status;
|
||||||
|
_statusString = statusStr;
|
||||||
|
emit pairingStatusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
QString
|
||||||
|
PairingManager::pairingStatusStr() const
|
||||||
|
{
|
||||||
|
return _statusString;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if QGC_GST_MICROHARD_ENABLED
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::startMicrohardPairing()
|
||||||
|
{
|
||||||
|
stopPairing();
|
||||||
|
_pairRetryCount = 0;
|
||||||
|
setPairingStatus(PairingActive, tr("Pairing..."));
|
||||||
|
_parsePairingJson(_assumeMicrohardPairingJson());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::stopPairing()
|
||||||
|
{
|
||||||
|
#if defined QGC_ENABLE_QTNFC
|
||||||
|
pairingNFC.stop();
|
||||||
|
#endif
|
||||||
|
_stopUpload();
|
||||||
|
setPairingStatus(PairingIdle, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined QGC_ENABLE_QTNFC
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingManager::startNFCScan()
|
||||||
|
{
|
||||||
|
stopPairing();
|
||||||
|
setPairingStatus(PairingActive, tr("Pairing..."));
|
||||||
|
pairingNFC.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#ifdef __android__
|
||||||
|
static const char kJniClassName[] {"org/mavlink/qgroundcontrol/QGCActivity"};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static void jniNFCTagReceived(JNIEnv *envA, jobject thizA, jstring messageA)
|
||||||
|
{
|
||||||
|
Q_UNUSED(thizA);
|
||||||
|
|
||||||
|
const char *stringL = envA->GetStringUTFChars(messageA, nullptr);
|
||||||
|
QString ndef = QString::fromUtf8(stringL);
|
||||||
|
envA->ReleaseStringUTFChars(messageA, stringL);
|
||||||
|
if (envA->ExceptionCheck())
|
||||||
|
envA->ExceptionClear();
|
||||||
|
qCDebug(PairingManagerLog) << "NDEF Tag Received: " << ndef;
|
||||||
|
qgcApp()->toolbox()->pairingManager()->jsonReceived(ndef);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void PairingManager::setNativeMethods(void)
|
||||||
|
{
|
||||||
|
// REGISTER THE C++ FUNCTION WITH JNI
|
||||||
|
JNINativeMethod javaMethods[] {
|
||||||
|
{"nativeNFCTagReceived", "(Ljava/lang/String;)V", reinterpret_cast<void *>(jniNFCTagReceived)}
|
||||||
|
};
|
||||||
|
|
||||||
|
QAndroidJniEnvironment jniEnv;
|
||||||
|
if (jniEnv->ExceptionCheck()) {
|
||||||
|
jniEnv->ExceptionDescribe();
|
||||||
|
jniEnv->ExceptionClear();
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass objectClass = jniEnv->FindClass(kJniClassName);
|
||||||
|
if(!objectClass) {
|
||||||
|
qWarning() << "Couldn't find class:" << kJniClassName;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
jint val = jniEnv->RegisterNatives(objectClass, javaMethods, sizeof(javaMethods) / sizeof(javaMethods[0]));
|
||||||
|
|
||||||
|
if (val < 0) {
|
||||||
|
qWarning() << "Error registering methods: " << val;
|
||||||
|
} else {
|
||||||
|
qCDebug(PairingManagerLog) << "Native Functions Registered";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jniEnv->ExceptionCheck()) {
|
||||||
|
jniEnv->ExceptionDescribe();
|
||||||
|
jniEnv->ExceptionClear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
//-----------------------------------------------------------------------------
|
@ -0,0 +1,153 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
*
|
||||||
|
* (c) 2019 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||||
|
*
|
||||||
|
* QGroundControl is licensed according to the terms in the file
|
||||||
|
* COPYING.md in the root of the source code directory.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QTime>
|
||||||
|
#include <QVariantMap>
|
||||||
|
|
||||||
|
#include "aes.h"
|
||||||
|
#include "QGCToolbox.h"
|
||||||
|
#include "QGCLoggingCategory.h"
|
||||||
|
#include "Fact.h"
|
||||||
|
#if defined QGC_ENABLE_QTNFC
|
||||||
|
#include "QtNFC.h"
|
||||||
|
#endif
|
||||||
|
#ifdef __android__
|
||||||
|
#include <jni.h>
|
||||||
|
#include <QtAndroidExtras/QtAndroidExtras>
|
||||||
|
#include <QtAndroidExtras/QAndroidJniObject>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(PairingManagerLog)
|
||||||
|
|
||||||
|
class AppSettings;
|
||||||
|
class QGCApplication;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
class PairingManager : public QGCTool
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit PairingManager (QGCApplication* app, QGCToolbox* toolbox);
|
||||||
|
~PairingManager () override;
|
||||||
|
|
||||||
|
// Override from QGCTool
|
||||||
|
virtual void setToolbox(QGCToolbox *toolbox) override;
|
||||||
|
|
||||||
|
enum PairingStatus {
|
||||||
|
PairingIdle,
|
||||||
|
PairingActive,
|
||||||
|
PairingSuccess,
|
||||||
|
PairingConnecting,
|
||||||
|
PairingConnected,
|
||||||
|
PairingRejected,
|
||||||
|
PairingConnectionRejected,
|
||||||
|
PairingError
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_ENUM(PairingStatus)
|
||||||
|
|
||||||
|
QStringList pairingLinkTypeStrings ();
|
||||||
|
QString pairingStatusStr () const;
|
||||||
|
QStringList pairedDeviceNameList () { return _deviceList; }
|
||||||
|
PairingStatus pairingStatus () { return _status; }
|
||||||
|
QString pairedVehicle () { return _lastPaired; }
|
||||||
|
int nfcIndex () { return _nfcIndex; }
|
||||||
|
int microhardIndex () { return _microhardIndex; }
|
||||||
|
bool firstBoot () { return _firstBoot; }
|
||||||
|
bool errorState () { return _status == PairingRejected || _status == PairingConnectionRejected || _status == PairingError; }
|
||||||
|
void setStatusMessage (PairingStatus status, QString statusStr) { emit setPairingStatus(status, statusStr); }
|
||||||
|
void jsonReceived (QString json) { emit parsePairingJson(json); }
|
||||||
|
void setFirstBoot (bool set) { _firstBoot = set; emit firstBootChanged(); }
|
||||||
|
#ifdef __android__
|
||||||
|
static void setNativeMethods (void);
|
||||||
|
#endif
|
||||||
|
Q_INVOKABLE void connectToPairedDevice (QString name);
|
||||||
|
Q_INVOKABLE void removePairedDevice (QString name);
|
||||||
|
|
||||||
|
#if defined defined QGC_ENABLE_QTNFC
|
||||||
|
Q_INVOKABLE void startNFCScan();
|
||||||
|
#endif
|
||||||
|
#if QGC_GST_MICROHARD_ENABLED
|
||||||
|
Q_INVOKABLE void startMicrohardPairing();
|
||||||
|
#endif
|
||||||
|
Q_INVOKABLE void stopPairing();
|
||||||
|
|
||||||
|
Q_PROPERTY(QString pairingStatusStr READ pairingStatusStr NOTIFY pairingStatusChanged)
|
||||||
|
Q_PROPERTY(PairingStatus pairingStatus READ pairingStatus NOTIFY pairingStatusChanged)
|
||||||
|
Q_PROPERTY(QStringList pairedDeviceNameList READ pairedDeviceNameList NOTIFY pairedListChanged)
|
||||||
|
Q_PROPERTY(QStringList pairingLinkTypeStrings READ pairingLinkTypeStrings CONSTANT)
|
||||||
|
Q_PROPERTY(QString pairedVehicle READ pairedVehicle NOTIFY pairedVehicleChanged)
|
||||||
|
Q_PROPERTY(bool errorState READ errorState NOTIFY pairingStatusChanged)
|
||||||
|
Q_PROPERTY(int nfcIndex READ nfcIndex CONSTANT)
|
||||||
|
Q_PROPERTY(int microhardIndex READ microhardIndex CONSTANT)
|
||||||
|
Q_PROPERTY(bool firstBoot READ firstBoot WRITE setFirstBoot NOTIFY firstBootChanged)
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void startUpload (QString pairURL, QJsonDocument);
|
||||||
|
void closeConnection ();
|
||||||
|
void pairingConfigurationsChanged ();
|
||||||
|
void nameListChanged ();
|
||||||
|
void pairingStatusChanged ();
|
||||||
|
void parsePairingJson (QString json);
|
||||||
|
void setPairingStatus (PairingStatus status, QString pairingStatus);
|
||||||
|
void pairedListChanged ();
|
||||||
|
void pairedVehicleChanged ();
|
||||||
|
void firstBootChanged ();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void _startUpload (QString pairURL, QJsonDocument);
|
||||||
|
void _stopUpload ();
|
||||||
|
void _startUploadRequest ();
|
||||||
|
void _parsePairingJson (QString jsonEnc);
|
||||||
|
void _setPairingStatus (PairingStatus status, QString pairingStatus);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _statusString;
|
||||||
|
QString _jsonFileName;
|
||||||
|
QString _lastPaired;
|
||||||
|
QVariantMap _remotePairingMap;
|
||||||
|
int _nfcIndex = -1;
|
||||||
|
int _microhardIndex = -1;
|
||||||
|
int _pairRetryCount = 0;
|
||||||
|
PairingStatus _status = PairingIdle;
|
||||||
|
AES _aes;
|
||||||
|
QJsonDocument _jsonDoc{};
|
||||||
|
QMutex _uploadMutex{};
|
||||||
|
QNetworkAccessManager* _uploadManager = nullptr;
|
||||||
|
QString _uploadURL{};
|
||||||
|
QString _uploadData{};
|
||||||
|
bool _firstBoot = true;
|
||||||
|
QStringList _deviceList;
|
||||||
|
|
||||||
|
void _parsePairingJsonFile ();
|
||||||
|
QJsonDocument _createZeroTierConnectJson (QString cert2);
|
||||||
|
QJsonDocument _createMicrohardConnectJson (QString cert2);
|
||||||
|
QJsonDocument _createZeroTierPairingJson (QString cert1);
|
||||||
|
QJsonDocument _createMicrohardPairingJson (QString pwd, QString cert1);
|
||||||
|
QString _assumeMicrohardPairingJson ();
|
||||||
|
void _writeJson (QJsonDocument &jsonDoc, QString fileName);
|
||||||
|
QString _getLocalIPInNetwork (QString remoteIP, int num);
|
||||||
|
void _uploadFinished ();
|
||||||
|
void _uploadError (QNetworkReply::NetworkError code);
|
||||||
|
void _pairingCompleted (QString name);
|
||||||
|
void _connectionCompleted (QString name);
|
||||||
|
QDir _pairingCacheDir ();
|
||||||
|
QString _pairingCacheFile (QString uavName);
|
||||||
|
void _updatePairedDeviceNameList ();
|
||||||
|
|
||||||
|
#if defined QGC_ENABLE_QTNFC
|
||||||
|
PairingNFC pairingNFC;
|
||||||
|
#endif
|
||||||
|
};
|
@ -0,0 +1,135 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
*
|
||||||
|
* (c) 2019 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||||
|
*
|
||||||
|
* QGroundControl is licensed according to the terms in the file
|
||||||
|
* COPYING.md in the root of the source code directory.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
#include "PairingManager.h"
|
||||||
|
#include "QtNFC.h"
|
||||||
|
#include "QGCApplication.h"
|
||||||
|
#include <QSoundEffect>
|
||||||
|
|
||||||
|
QGC_LOGGING_CATEGORY(PairingNFCLog, "PairingNFCLog")
|
||||||
|
|
||||||
|
#include <QNdefNfcTextRecord>
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
PairingNFC::PairingNFC()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingNFC::start()
|
||||||
|
{
|
||||||
|
if (manager != nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qgcApp()->toolbox()->pairingManager()->setStatusMessage(tr("Waiting for NFC connection"));
|
||||||
|
qCDebug(PairingNFCLog) << "Waiting for NFC connection";
|
||||||
|
|
||||||
|
manager = new QNearFieldManager(this);
|
||||||
|
if (!manager->isAvailable()) {
|
||||||
|
qWarning() << "NFC not available";
|
||||||
|
delete manager;
|
||||||
|
manager = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QNdefFilter filter;
|
||||||
|
filter.setOrderMatch(false);
|
||||||
|
filter.appendRecord<QNdefNfcTextRecord>(1, UINT_MAX);
|
||||||
|
// type parameter cannot specify substring so filter for "image/" below
|
||||||
|
filter.appendRecord(QNdefRecord::Mime, QByteArray(), 0, 1);
|
||||||
|
|
||||||
|
int result = manager->registerNdefMessageHandler(filter, this, SLOT(handleMessage(QNdefMessage, QNearFieldTarget*)));
|
||||||
|
|
||||||
|
if (result < 0)
|
||||||
|
qWarning() << "Platform does not support NDEF message handler registration";
|
||||||
|
|
||||||
|
manager->startTargetDetection();
|
||||||
|
connect(manager, &QNearFieldManager::targetDetected, this, &PairingNFC::targetDetected);
|
||||||
|
connect(manager, &QNearFieldManager::targetLost, this, &PairingNFC::targetLost);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingNFC::stop()
|
||||||
|
{
|
||||||
|
if (manager != nullptr) {
|
||||||
|
qgcApp()->toolbox()->pairingManager()->setStatusMessage("");
|
||||||
|
qCDebug(PairingNFCLog) << "NFC: Stop";
|
||||||
|
manager->stopTargetDetection();
|
||||||
|
delete manager;
|
||||||
|
manager = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingNFC::targetDetected(QNearFieldTarget *target)
|
||||||
|
{
|
||||||
|
if (!target) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qgcApp()->toolbox()->pairingManager()->setStatusMessage(tr("Device detected"));
|
||||||
|
qCDebug(PairingNFCLog) << "NFC: Device detected";
|
||||||
|
connect(target, &QNearFieldTarget::ndefMessageRead, this, &PairingNFC::handlePolledNdefMessage);
|
||||||
|
connect(target, SIGNAL(error(QNearFieldTarget::Error,QNearFieldTarget::RequestId)),
|
||||||
|
this, SLOT(targetError(QNearFieldTarget::Error,QNearFieldTarget::RequestId)));
|
||||||
|
connect(target, &QNearFieldTarget::requestCompleted, this, &PairingNFC::handleRequestCompleted);
|
||||||
|
|
||||||
|
manager->setTargetAccessModes(QNearFieldManager::NdefReadTargetAccess);
|
||||||
|
QNearFieldTarget::RequestId id = target->readNdefMessages();
|
||||||
|
if (target->waitForRequestCompleted(id)) {
|
||||||
|
qCDebug(PairingNFCLog) << "requestCompleted ";
|
||||||
|
QVariant res = target->requestResponse(id);
|
||||||
|
qCDebug(PairingNFCLog) << "Response: " << res.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingNFC::handleRequestCompleted(const QNearFieldTarget::RequestId& id)
|
||||||
|
{
|
||||||
|
Q_UNUSED(id);
|
||||||
|
qCDebug(PairingNFCLog) << "handleRequestCompleted ";
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingNFC::targetError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId& id)
|
||||||
|
{
|
||||||
|
Q_UNUSED(id);
|
||||||
|
qCDebug(PairingNFCLog) << "Error: " << error;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingNFC::targetLost(QNearFieldTarget *target)
|
||||||
|
{
|
||||||
|
qgcApp()->toolbox()->pairingManager()->setStatusMessage(tr("Device removed"));
|
||||||
|
qCDebug(PairingNFCLog) << "NFC: Device removed";
|
||||||
|
if (target) {
|
||||||
|
target->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
PairingNFC::handlePolledNdefMessage(QNdefMessage message)
|
||||||
|
{
|
||||||
|
qCDebug(PairingNFCLog) << "NFC: Handle NDEF message";
|
||||||
|
// QNearFieldTarget *target = qobject_cast<QNearFieldTarget *>(sender());
|
||||||
|
for (const QNdefRecord &record : message) {
|
||||||
|
if (record.isRecordType<QNdefNfcTextRecord>()) {
|
||||||
|
QNdefNfcTextRecord textRecord(record);
|
||||||
|
qgcApp()->toolbox()->pairingManager()->jsonReceived(textRecord.text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
@ -0,0 +1,45 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
*
|
||||||
|
* (c) 2019 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||||
|
*
|
||||||
|
* QGroundControl is licensed according to the terms in the file
|
||||||
|
* COPYING.md in the root of the source code directory.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QNdefMessage>
|
||||||
|
#include <QNearFieldManager>
|
||||||
|
#include <QNearFieldTarget>
|
||||||
|
|
||||||
|
#include "QGCLoggingCategory.h"
|
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(PairingNFCLog)
|
||||||
|
|
||||||
|
class PairingNFC : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PairingNFC();
|
||||||
|
|
||||||
|
void start();
|
||||||
|
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void parsePairingJson(QString json);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void targetDetected(QNearFieldTarget *target);
|
||||||
|
void targetLost(QNearFieldTarget *target);
|
||||||
|
void handlePolledNdefMessage(QNdefMessage message);
|
||||||
|
void targetError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId& id);
|
||||||
|
void handleRequestCompleted(const QNearFieldTarget::RequestId& id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _exitThread = false; ///< true: signal thread to exit
|
||||||
|
QNearFieldManager *manager = nullptr;
|
||||||
|
};
|
@ -0,0 +1,154 @@
|
|||||||
|
#include "aes.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <openssl/aes.h>
|
||||||
|
#include <openssl/bio.h>
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
AES::AES(std::string password, unsigned long long salt)
|
||||||
|
{
|
||||||
|
int nrounds = 5;
|
||||||
|
unsigned char key[32], iv[32];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gen key & IV for AES 256 CBC mode. A SHA1 digest is used to hash the supplied key material.
|
||||||
|
* nrounds is the number of times the we hash the material. More rounds are more secure but
|
||||||
|
* slower.
|
||||||
|
*/
|
||||||
|
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(),
|
||||||
|
reinterpret_cast<const unsigned char*>(&salt),
|
||||||
|
reinterpret_cast<const unsigned char*>(password.c_str()),
|
||||||
|
static_cast<int>(password.length()),
|
||||||
|
nrounds, key, iv);
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||||
|
encCipherContext = EVP_CIPHER_CTX_new();
|
||||||
|
decCipherContext = EVP_CIPHER_CTX_new();
|
||||||
|
|
||||||
|
EVP_CIPHER_CTX_init(encCipherContext);
|
||||||
|
EVP_EncryptInit_ex(encCipherContext, EVP_aes_256_cbc(), nullptr, key, iv);
|
||||||
|
EVP_CIPHER_CTX_init(decCipherContext);
|
||||||
|
EVP_DecryptInit_ex(decCipherContext, EVP_aes_256_cbc(), nullptr, key, iv);
|
||||||
|
#else
|
||||||
|
EVP_CIPHER_CTX_init(&encCipherContext);
|
||||||
|
EVP_EncryptInit_ex(&encCipherContext, EVP_aes_256_cbc(), nullptr, key, iv);
|
||||||
|
EVP_CIPHER_CTX_init(&decCipherContext);
|
||||||
|
EVP_DecryptInit_ex(&decCipherContext, EVP_aes_256_cbc(), nullptr, key, iv);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
AES::~AES()
|
||||||
|
{
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||||
|
EVP_CIPHER_CTX_free(encCipherContext);
|
||||||
|
EVP_CIPHER_CTX_free(decCipherContext);
|
||||||
|
#else
|
||||||
|
EVP_CIPHER_CTX_cleanup(&encCipherContext);
|
||||||
|
EVP_CIPHER_CTX_cleanup(&decCipherContext);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
std::string
|
||||||
|
AES::encrypt(std::string plainText)
|
||||||
|
{
|
||||||
|
unsigned long sourceLen = static_cast<unsigned long>(plainText.length() + 1);
|
||||||
|
unsigned long destLen = sourceLen * 2;
|
||||||
|
unsigned char* compressed = new unsigned char[destLen];
|
||||||
|
int err = compress2(compressed, &destLen,
|
||||||
|
reinterpret_cast<const unsigned char *>(plainText.c_str()),
|
||||||
|
sourceLen, 9);
|
||||||
|
if (err != Z_OK) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int pLen = static_cast<int>(destLen);
|
||||||
|
int cLen = pLen + AES_BLOCK_SIZE;
|
||||||
|
int fLen = 0;
|
||||||
|
unsigned char* cipherText = new unsigned char[cLen];
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||||
|
EVP_EncryptInit_ex(encCipherContext, nullptr, nullptr, nullptr, nullptr);
|
||||||
|
EVP_EncryptUpdate(encCipherContext, cipherText, &cLen, compressed, pLen);
|
||||||
|
EVP_EncryptFinal_ex(encCipherContext, cipherText + cLen, &fLen);
|
||||||
|
#else
|
||||||
|
EVP_EncryptInit_ex(&encCipherContext, nullptr, nullptr, nullptr, nullptr);
|
||||||
|
EVP_EncryptUpdate(&encCipherContext, cipherText, &cLen, compressed, pLen);
|
||||||
|
EVP_EncryptFinal_ex(&encCipherContext, cipherText + cLen, &fLen);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::vector<unsigned char> data(cipherText, cipherText + cLen + fLen);
|
||||||
|
std::string res = base64Encode(data);
|
||||||
|
delete[] cipherText;
|
||||||
|
delete[] compressed;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
std::string
|
||||||
|
AES::decrypt(std::string cipherText)
|
||||||
|
{
|
||||||
|
int fLen = 0;
|
||||||
|
std::vector<unsigned char> text = base64Decode(cipherText);
|
||||||
|
int pLen = static_cast<int>(text.size());
|
||||||
|
unsigned char* plainText = new unsigned char[pLen];
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||||
|
EVP_DecryptInit_ex(decCipherContext, nullptr, nullptr, nullptr, nullptr);
|
||||||
|
EVP_DecryptUpdate(decCipherContext, plainText, &pLen, text.data(), pLen);
|
||||||
|
EVP_DecryptFinal_ex(decCipherContext, plainText + pLen, &fLen);
|
||||||
|
#else
|
||||||
|
EVP_DecryptInit_ex(&decCipherContext, nullptr, nullptr, nullptr, nullptr);
|
||||||
|
EVP_DecryptUpdate(&decCipherContext, plainText, &pLen, text.data(), pLen);
|
||||||
|
EVP_DecryptFinal_ex(&decCipherContext, plainText + pLen, &fLen);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
unsigned long destLen = static_cast<unsigned long>((pLen + fLen) * 2);
|
||||||
|
unsigned char* uncompressed = new unsigned char[destLen];
|
||||||
|
int err = uncompress(uncompressed, &destLen, plainText, static_cast<unsigned long>(pLen + fLen));
|
||||||
|
if (err != Z_OK) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string res(reinterpret_cast<char*>(uncompressed));
|
||||||
|
delete[] uncompressed;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
struct BIOFreeAll { void operator()(BIO* p) { BIO_free_all(p); } };
|
||||||
|
|
||||||
|
std::string
|
||||||
|
AES::base64Encode(const std::vector<unsigned char>& binary)
|
||||||
|
{
|
||||||
|
std::unique_ptr<BIO, BIOFreeAll> b64(BIO_new(BIO_f_base64()));
|
||||||
|
BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);
|
||||||
|
BIO* sink = BIO_new(BIO_s_mem());
|
||||||
|
BIO_push(b64.get(), sink);
|
||||||
|
BIO_write(b64.get(), binary.data(), static_cast<int>(binary.size()));
|
||||||
|
BIO_ctrl(b64.get(), BIO_CTRL_FLUSH, 0, nullptr);
|
||||||
|
const char* encoded;
|
||||||
|
const unsigned long len = static_cast<unsigned long>(BIO_ctrl(sink, BIO_CTRL_INFO, 0, &encoded));
|
||||||
|
|
||||||
|
return std::string(encoded, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
std::vector<unsigned char>
|
||||||
|
AES::base64Decode(std::string encoded)
|
||||||
|
{
|
||||||
|
std::unique_ptr<BIO, BIOFreeAll> b64(BIO_new(BIO_f_base64()));
|
||||||
|
BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);
|
||||||
|
BIO* source = BIO_new_mem_buf(encoded.c_str(), -1); // read-only source
|
||||||
|
BIO_push(b64.get(), source);
|
||||||
|
const unsigned long maxlen = encoded.length() / 4 * 3 + 1;
|
||||||
|
std::vector<unsigned char> decoded(maxlen);
|
||||||
|
const unsigned long len = static_cast<unsigned long>(BIO_read(b64.get(), decoded.data(), static_cast<int>(maxlen)));
|
||||||
|
decoded.resize(len);
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef AES_H
|
||||||
|
#define AES_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
|
class AES
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AES(std::string password, unsigned long long salt);
|
||||||
|
|
||||||
|
~AES();
|
||||||
|
|
||||||
|
std::string encrypt(std::string plainText);
|
||||||
|
|
||||||
|
std::string decrypt(std::string cipherText);
|
||||||
|
|
||||||
|
private:
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
|
||||||
|
EVP_CIPHER_CTX *encCipherContext = nullptr;
|
||||||
|
EVP_CIPHER_CTX *decCipherContext = nullptr;
|
||||||
|
#else
|
||||||
|
EVP_CIPHER_CTX encCipherContext;
|
||||||
|
EVP_CIPHER_CTX decCipherContext;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string base64Encode(const std::vector<unsigned char>& binary);
|
||||||
|
|
||||||
|
std::vector<unsigned char> base64Decode(std::string encoded);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // AES_H
|
Loading…
Reference in new issue