laptoy 6 months ago
commit e7778c32b7

@ -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…
Cancel
Save