Add a verification key that's displayed when pairing
The key is a sha256 of both devices' certificates. Both should generate the same key, so hey user can check they are pairing against the right device. Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
This commit is contained in:
parent
b272ab86b3
commit
e7518493df
9 changed files with 131 additions and 46 deletions
|
@ -15,6 +15,7 @@ class PairingHandler;
|
||||||
class NetworkPacket;
|
class NetworkPacket;
|
||||||
class LinkProvider;
|
class LinkProvider;
|
||||||
class Device;
|
class Device;
|
||||||
|
class QSslCertificate;
|
||||||
|
|
||||||
class DeviceLink
|
class DeviceLink
|
||||||
: public QObject
|
: public QObject
|
||||||
|
@ -44,6 +45,8 @@ public:
|
||||||
//The daemon will periodically destroy unpaired links if this returns false
|
//The daemon will periodically destroy unpaired links if this returns false
|
||||||
virtual bool linkShouldBeKeptAlive() { return false; }
|
virtual bool linkShouldBeKeptAlive() { return false; }
|
||||||
|
|
||||||
|
virtual QSslCertificate certificate() const = 0;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void pairingRequest(PairingHandler* handler);
|
void pairingRequest(PairingHandler* handler);
|
||||||
void pairingRequestExpired(PairingHandler* handler);
|
void pairingRequestExpired(PairingHandler* handler);
|
||||||
|
|
|
@ -164,8 +164,7 @@ void LanDeviceLink::setPairStatus(PairStatus status)
|
||||||
DeviceLink::setPairStatus(status);
|
DeviceLink::setPairStatus(status);
|
||||||
if (status == Paired) {
|
if (status == Paired) {
|
||||||
Q_ASSERT(KdeConnectConfig::instance().trustedDevices().contains(deviceId()));
|
Q_ASSERT(KdeConnectConfig::instance().trustedDevices().contains(deviceId()));
|
||||||
Q_ASSERT(!m_socketLineReader->peerCertificate().isNull());
|
KdeConnectConfig::instance().setDeviceProperty(deviceId(), QStringLiteral("certificate"), QString::fromLatin1(certificate().toPem()));
|
||||||
KdeConnectConfig::instance().setDeviceProperty(deviceId(), QStringLiteral("certificate"), QString::fromLatin1(m_socketLineReader->peerCertificate().toPem().data()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ public:
|
||||||
bool linkShouldBeKeptAlive() override;
|
bool linkShouldBeKeptAlive() override;
|
||||||
|
|
||||||
QHostAddress hostAddress() const;
|
QHostAddress hostAddress() const;
|
||||||
QSslCertificate certificate() const;
|
QSslCertificate certificate() const override;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void dataReceived();
|
void dataReceived();
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#define LOOPBACKDEVICELINK_H
|
#define LOOPBACKDEVICELINK_H
|
||||||
|
|
||||||
#include "../devicelink.h"
|
#include "../devicelink.h"
|
||||||
|
#include <QSslCertificate>
|
||||||
|
|
||||||
class LoopbackLinkProvider;
|
class LoopbackLinkProvider;
|
||||||
|
|
||||||
|
@ -23,6 +24,8 @@ public:
|
||||||
|
|
||||||
void userRequestsPair() override { setPairStatus(Paired); }
|
void userRequestsPair() override { setPairStatus(Paired); }
|
||||||
void userRequestsUnpair() override { setPairStatus(NotPaired); }
|
void userRequestsUnpair() override { setPairStatus(NotPaired); }
|
||||||
|
|
||||||
|
QSslCertificate certificate() const override { return QSslCertificate(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QSslCertificate>
|
#include <QSslCertificate>
|
||||||
|
#include <QSslKey>
|
||||||
|
|
||||||
#include <KSharedConfig>
|
#include <KSharedConfig>
|
||||||
#include <KConfigGroup>
|
#include <KConfigGroup>
|
||||||
|
@ -537,25 +538,47 @@ bool Device::isPluginEnabled(const QString& pluginName) const
|
||||||
QString Device::encryptionInfo() const
|
QString Device::encryptionInfo() const
|
||||||
{
|
{
|
||||||
QString result;
|
QString result;
|
||||||
QCryptographicHash::Algorithm digestAlgorithm = QCryptographicHash::Algorithm::Sha1;
|
const QCryptographicHash::Algorithm digestAlgorithm = QCryptographicHash::Algorithm::Sha256;
|
||||||
|
|
||||||
QString localSha1 = QString::fromLatin1(KdeConnectConfig::instance().certificate().digest(digestAlgorithm).toHex());
|
QString localChecksum = QString::fromLatin1(KdeConnectConfig::instance().certificate().digest(digestAlgorithm).toHex());
|
||||||
for (int i = 2; i<localSha1.size(); i += 3) {
|
for (int i = 2; i<localChecksum.size(); i += 3) {
|
||||||
localSha1.insert(i, QStringLiteral(":")); // Improve readability
|
localChecksum.insert(i, QStringLiteral(":")); // Improve readability
|
||||||
}
|
}
|
||||||
result += i18n("SHA1 fingerprint of your device certificate is: %1\n", localSha1);
|
result += i18n("SHA256 fingerprint of your device certificate is: %1\n", localChecksum);
|
||||||
|
|
||||||
std::string remotePem = KdeConnectConfig::instance().getDeviceProperty(id(), QStringLiteral("certificate")).toStdString();
|
std::string remotePem = KdeConnectConfig::instance().getDeviceProperty(id(), QStringLiteral("certificate")).toStdString();
|
||||||
QSslCertificate remoteCertificate = QSslCertificate(QByteArray(remotePem.c_str(), (int)remotePem.size()));
|
QSslCertificate remoteCertificate = QSslCertificate(QByteArray(remotePem.c_str(), (int)remotePem.size()));
|
||||||
QString remoteSha1 = QString::fromLatin1(remoteCertificate.digest(digestAlgorithm).toHex());
|
QString remoteChecksum = QString::fromLatin1(remoteCertificate.digest(digestAlgorithm).toHex());
|
||||||
for (int i = 2; i < remoteSha1.size(); i += 3) {
|
for (int i = 2; i < remoteChecksum.size(); i += 3) {
|
||||||
remoteSha1.insert(i, QStringLiteral(":")); // Improve readability
|
remoteChecksum.insert(i, QStringLiteral(":")); // Improve readability
|
||||||
}
|
}
|
||||||
result += i18n("SHA1 fingerprint of remote device certificate is: %1\n", remoteSha1);
|
result += i18n("SHA256 fingerprint of remote device certificate is: %1\n", remoteChecksum);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSslCertificate Device::certificate() const
|
||||||
|
{
|
||||||
|
if (!d->m_deviceLinks.isEmpty()) {
|
||||||
|
return d->m_deviceLinks[0]->certificate();
|
||||||
|
}
|
||||||
|
return QSslCertificate();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Device::verificationKey() const
|
||||||
|
{
|
||||||
|
auto a = KdeConnectConfig::instance().certificate().publicKey().toDer();
|
||||||
|
auto b = certificate().publicKey().toDer();
|
||||||
|
if (a < b) {
|
||||||
|
std::swap(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCryptographicHash hash(QCryptographicHash::Sha256);
|
||||||
|
hash.addData(a);
|
||||||
|
hash.addData(b);
|
||||||
|
return hash.result().toHex();
|
||||||
|
}
|
||||||
|
|
||||||
QString Device::pluginIconName(const QString& pluginName)
|
QString Device::pluginIconName(const QString& pluginName)
|
||||||
{
|
{
|
||||||
if (hasPlugin(pluginName)) {
|
if (hasPlugin(pluginName)) {
|
||||||
|
|
|
@ -64,6 +64,7 @@ public:
|
||||||
QString type() const;
|
QString type() const;
|
||||||
QString iconName() const;
|
QString iconName() const;
|
||||||
QString statusIconName() const;
|
QString statusIconName() const;
|
||||||
|
Q_SCRIPTABLE QByteArray verificationKey() const;
|
||||||
Q_SCRIPTABLE QString encryptionInfo() const;
|
Q_SCRIPTABLE QString encryptionInfo() const;
|
||||||
|
|
||||||
//Add and remove links
|
//Add and remove links
|
||||||
|
@ -132,6 +133,7 @@ private: //Methods
|
||||||
void setName(const QString& name);
|
void setName(const QString& name);
|
||||||
void setType(const QString& strtype);
|
void setType(const QString& strtype);
|
||||||
QString iconForStatus(bool reachable, bool paired) const;
|
QString iconForStatus(bool reachable, bool paired) const;
|
||||||
|
QSslCertificate certificate() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class DevicePrivate;
|
class DevicePrivate;
|
||||||
|
|
|
@ -49,14 +49,17 @@ public:
|
||||||
notification->setIconName(QStringLiteral("dialog-information"));
|
notification->setIconName(QStringLiteral("dialog-information"));
|
||||||
notification->setComponentName(QStringLiteral("kdeconnect"));
|
notification->setComponentName(QStringLiteral("kdeconnect"));
|
||||||
notification->setTitle(QStringLiteral("KDE Connect"));
|
notification->setTitle(QStringLiteral("KDE Connect"));
|
||||||
notification->setText(i18n("Pairing request from %1", device->name().toHtmlEscaped()));
|
notification->setText(i18n("Pairing request from %1\n<br/>Key: %2...", device->name().toHtmlEscaped(), QString::fromUtf8(device->verificationKey().left(8))));
|
||||||
notification->setDefaultAction(i18n("Open"));
|
notification->setDefaultAction(i18n("Open"));
|
||||||
notification->setActions(QStringList() << i18n("Accept") << i18n("Reject"));
|
notification->setActions(QStringList() << i18n("Accept") << i18n("Reject") << i18n("View key"));
|
||||||
connect(notification, &KNotification::action1Activated, device, &Device::acceptPairing);
|
connect(notification, &KNotification::action1Activated, device, &Device::acceptPairing);
|
||||||
connect(notification, &KNotification::action2Activated, device, &Device::rejectPairing);
|
connect(notification, &KNotification::action2Activated, device, &Device::rejectPairing);
|
||||||
connect(notification, QOverload<>::of(&KNotification::activated), this, [this, device] {
|
QString deviceId = device->id();
|
||||||
openConfiguration(device->id());
|
auto openSettings = [this, deviceId] {
|
||||||
});
|
openConfiguration(deviceId);
|
||||||
|
};
|
||||||
|
connect(notification, &KNotification::action3Activated, openSettings);
|
||||||
|
connect(notification, QOverload<>::of(&KNotification::activated), openSettings);
|
||||||
notification->sendEvent();
|
notification->sendEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -221,6 +221,7 @@ void KdeConnectKcm::resetDeviceView()
|
||||||
delete kcmUi->pluginSelector;
|
delete kcmUi->pluginSelector;
|
||||||
kcmUi->pluginSelector = new KPluginSelector(this);
|
kcmUi->pluginSelector = new KPluginSelector(this);
|
||||||
kcmUi->deviceInfo_layout->addWidget(kcmUi->pluginSelector);
|
kcmUi->deviceInfo_layout->addWidget(kcmUi->pluginSelector);
|
||||||
|
kcmUi->verificationKey->setText(i18n("Key: %1", QString::fromUtf8(currentDevice->verificationKey())));
|
||||||
|
|
||||||
kcmUi->pluginSelector->setConfigurationArguments(QStringList(currentDevice->id()));
|
kcmUi->pluginSelector->setConfigurationArguments(QStringList(currentDevice->id()));
|
||||||
|
|
||||||
|
|
109
kcm/kcm.ui
109
kcm/kcm.ui
|
@ -6,7 +6,7 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>965</width>
|
<width>1198</width>
|
||||||
<height>528</height>
|
<height>528</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -143,34 +143,85 @@
|
||||||
<enum>QLayout::SetMaximumSize</enum>
|
<enum>QLayout::SetMaximumSize</enum>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="name_label">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<property name="font">
|
<item>
|
||||||
<font>
|
<widget class="QWidget" name="deviceAndStatus" native="true">
|
||||||
<pointsize>10</pointsize>
|
<property name="sizePolicy">
|
||||||
<weight>75</weight>
|
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||||
<bold>true</bold>
|
<horstretch>0</horstretch>
|
||||||
</font>
|
<verstretch>0</verstretch>
|
||||||
</property>
|
</sizepolicy>
|
||||||
<property name="text">
|
</property>
|
||||||
<string>Device</string>
|
<layout class="QHBoxLayout" name="deviceAndStatus_layout">
|
||||||
</property>
|
<property name="spacing">
|
||||||
<property name="textFormat">
|
<number>6</number>
|
||||||
<enum>Qt::PlainText</enum>
|
</property>
|
||||||
</property>
|
<property name="leftMargin">
|
||||||
</widget>
|
<number>0</number>
|
||||||
</item>
|
</property>
|
||||||
<item>
|
<property name="topMargin">
|
||||||
<widget class="QLabel" name="status_label">
|
<number>0</number>
|
||||||
<property name="sizePolicy">
|
</property>
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
<property name="rightMargin">
|
||||||
<horstretch>0</horstretch>
|
<number>0</number>
|
||||||
<verstretch>0</verstretch>
|
</property>
|
||||||
</sizepolicy>
|
<property name="bottomMargin">
|
||||||
</property>
|
<number>0</number>
|
||||||
<property name="text">
|
</property>
|
||||||
<string>(status)</string>
|
<item>
|
||||||
</property>
|
<widget class="QLabel" name="name_label">
|
||||||
</widget>
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Device</string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::PlainText</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="status_label">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>(status)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="verificationKey">
|
||||||
|
<property name="text">
|
||||||
|
<string>🔑 abababab</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
|
|
Loading…
Reference in a new issue