File 0001-Add-a-verification-key-that-s-displayed-when-pairing.patch of Package kdeconnect-kde

From 76075427cd5f735627eb3cc16e7340e865ab9ed9 Mon Sep 17 00:00:00 2001
From: Luca Beltrame <lbeltrame@kde.org>
Date: Tue, 22 Dec 2020 17:13:00 +0100
Subject: [PATCH] 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.
---
 core/backends/devicelink.h                  |   3 +
 core/backends/lan/landevicelink.cpp         |   3 +-
 core/backends/lan/landevicelink.h           |   2 +-
 core/backends/loopback/loopbackdevicelink.h |   3 +
 core/device.cpp                             |  41 ++++++--
 core/device.h                               |   2 +
 daemon/kdeconnectd.cpp                      |  13 ++-
 kcm/kcm.cpp                                 |   1 +
 kcm/kcm.ui                                  | 109 ++++++++++++++------
 9 files changed, 131 insertions(+), 46 deletions(-)

diff --git a/core/backends/devicelink.h b/core/backends/devicelink.h
index c11c2796..c874a905 100644
--- a/core/backends/devicelink.h
+++ b/core/backends/devicelink.h
@@ -29,6 +29,7 @@ class PairingHandler;
 class NetworkPacket;
 class LinkProvider;
 class Device;
+class QSslCertificate;

 class DeviceLink
     : public QObject
@@ -58,6 +59,8 @@ public:
     //The daemon will periodically destroy unpaired links if this returns false
     virtual bool linkShouldBeKeptAlive() { return false; }

+    virtual QSslCertificate certificate() const = 0;
+
 Q_SIGNALS:
     void pairingRequest(PairingHandler* handler);
     void pairingRequestExpired(PairingHandler* handler);
diff --git a/core/backends/lan/landevicelink.cpp b/core/backends/lan/landevicelink.cpp
index 41af6f0e..d31aecc7 100644
--- a/core/backends/lan/landevicelink.cpp
+++ b/core/backends/lan/landevicelink.cpp
@@ -178,8 +178,7 @@ void LanDeviceLink::setPairStatus(PairStatus status)
     DeviceLink::setPairStatus(status);
     if (status == Paired) {
         Q_ASSERT(KdeConnectConfig::instance().trustedDevices().contains(deviceId()));
-        Q_ASSERT(!m_socketLineReader->peerCertificate().isNull());
-        KdeConnectConfig::instance().setDeviceProperty(deviceId(), QStringLiteral("certificate"), QString::fromLatin1(m_socketLineReader->peerCertificate().toPem().data()));
+        KdeConnectConfig::instance().setDeviceProperty(deviceId(), QStringLiteral("certificate"), QString::fromLatin1(certificate().toPem()));
     }
 }

diff --git a/core/backends/lan/landevicelink.h b/core/backends/lan/landevicelink.h
index 485c58b5..f218d063 100644
--- a/core/backends/lan/landevicelink.h
+++ b/core/backends/lan/landevicelink.h
@@ -56,7 +56,7 @@ public:
     bool linkShouldBeKeptAlive() override;

     QHostAddress hostAddress() const;
-    QSslCertificate certificate() const;
+    QSslCertificate certificate() const override;

 private Q_SLOTS:
     void dataReceived();
diff --git a/core/backends/loopback/loopbackdevicelink.h b/core/backends/loopback/loopbackdevicelink.h
index 54b016a7..444d7c4e 100644
--- a/core/backends/loopback/loopbackdevicelink.h
+++ b/core/backends/loopback/loopbackdevicelink.h
@@ -22,6 +22,7 @@
 #define LOOPBACKDEVICELINK_H

 #include "../devicelink.h"
+#include <QSslCertificate>

 class LoopbackLinkProvider;

@@ -37,6 +38,8 @@ public:

     void userRequestsPair() override { setPairStatus(Paired); }
     void userRequestsUnpair() override { setPairStatus(NotPaired); }
+
+    QSslCertificate certificate() const override { return QSslCertificate(); }
 };

 #endif
diff --git a/core/device.cpp b/core/device.cpp
index 32e20179..c30cebd5 100644
--- a/core/device.cpp
+++ b/core/device.cpp
@@ -23,6 +23,7 @@
 #include <QVector>
 #include <QSet>
 #include <QSslCertificate>
+#include <QSslKey>

 #include <KSharedConfig>
 #include <KConfigGroup>
@@ -540,25 +541,47 @@ bool Device::isPluginEnabled(const QString& pluginName) const
 QString Device::encryptionInfo() const
 {
     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());
-    for (int i = 2; i<localSha1.size(); i += 3) {
-        localSha1.insert(i, QStringLiteral(":")); // Improve readability
+    QString localChecksum = QString::fromLatin1(KdeConnectConfig::instance().certificate().digest(digestAlgorithm).toHex());
+    for (int i = 2; i<localChecksum.size(); i += 3) {
+        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();
     QSslCertificate remoteCertificate = QSslCertificate(QByteArray(remotePem.c_str(), (int)remotePem.size()));
-    QString remoteSha1 = QString::fromLatin1(remoteCertificate.digest(digestAlgorithm).toHex());
-    for (int i = 2; i < remoteSha1.size(); i += 3) {
-        remoteSha1.insert(i, QStringLiteral(":")); // Improve readability
+    QString remoteChecksum = QString::fromLatin1(remoteCertificate.digest(digestAlgorithm).toHex());
+    for (int i = 2; i < remoteChecksum.size(); i += 3) {
+        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;
 }

+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)
 {
     if (hasPlugin(pluginName)) {
diff --git a/core/device.h b/core/device.h
index 9b6dd9bc..66fc0965 100644
--- a/core/device.h
+++ b/core/device.h
@@ -78,6 +78,7 @@ public:
     QString type() const;
     QString iconName() const;
     QString statusIconName() const;
+    Q_SCRIPTABLE QByteArray verificationKey() const;
     Q_SCRIPTABLE QString encryptionInfo() const;

     //Add and remove links
@@ -144,6 +145,7 @@ private: //Methods

     void setName(const QString& name);
     QString iconForStatus(bool reachable, bool paired) const;
+    QSslCertificate certificate() const;

 private:
     class DevicePrivate;
diff --git a/daemon/kdeconnectd.cpp b/daemon/kdeconnectd.cpp
index a81ec3fd..a94eb0e4 100644
--- a/daemon/kdeconnectd.cpp
+++ b/daemon/kdeconnectd.cpp
@@ -62,14 +62,17 @@ public:
         notification->setIconName(QStringLiteral("dialog-information"));
         notification->setComponentName(QStringLiteral("kdeconnect"));
         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->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::action2Activated, device, &Device::rejectPairing);
-        connect(notification, QOverload<>::of(&KNotification::activated), this, []{
-            QProcess::startDetached(QStringLiteral("kdeconnect-settings"));
-        });
+        QString deviceId = device->id();
+        auto openSettings = [this, deviceId] {
+            QProcess::startDetached(QStringLiteral("kdeconnect-settings"), QStringList() << QStringLiteral("--args") << deviceId);
+        };
+        connect(notification, &KNotification::action3Activated, openSettings);
+        connect(notification, QOverload<>::of(&KNotification::activated), openSettings);
         notification->sendEvent();
     }

diff --git a/kcm/kcm.cpp b/kcm/kcm.cpp
index 721a5166..b21d98ad 100644
--- a/kcm/kcm.cpp
+++ b/kcm/kcm.cpp
@@ -235,6 +235,7 @@ void KdeConnectKcm::resetDeviceView()
     delete kcmUi->pluginSelector;
     kcmUi->pluginSelector = new KPluginSelector(this);
     kcmUi->deviceInfo_layout->addWidget(kcmUi->pluginSelector);
+    kcmUi->verificationKey->setText(i18n("Key: %1", QString::fromUtf8(currentDevice->verificationKey())));

     kcmUi->pluginSelector->setConfigurationArguments(QStringList(currentDevice->id()));

diff --git a/kcm/kcm.ui b/kcm/kcm.ui
index 706f106b..08ea0812 100644
--- a/kcm/kcm.ui
+++ b/kcm/kcm.ui
@@ -6,7 +6,7 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>965</width>
+    <width>1198</width>
     <height>528</height>
    </rect>
   </property>
@@ -143,34 +143,85 @@
              <enum>QLayout::SetMaximumSize</enum>
             </property>
             <item>
-             <widget class="QLabel" name="name_label">
-              <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>
+             <layout class="QVBoxLayout" name="verticalLayout_2">
+              <item>
+               <widget class="QWidget" name="deviceAndStatus" native="true">
+                <property name="sizePolicy">
+                 <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+                  <horstretch>0</horstretch>
+                  <verstretch>0</verstretch>
+                 </sizepolicy>
+                </property>
+                <layout class="QHBoxLayout" name="deviceAndStatus_layout">
+                 <property name="spacing">
+                  <number>6</number>
+                 </property>
+                 <property name="leftMargin">
+                  <number>0</number>
+                 </property>
+                 <property name="topMargin">
+                  <number>0</number>
+                 </property>
+                 <property name="rightMargin">
+                  <number>0</number>
+                 </property>
+                 <property name="bottomMargin">
+                  <number>0</number>
+                 </property>
+                 <item>
+                  <widget class="QLabel" name="name_label">
+                   <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>
              <spacer name="horizontalSpacer">
--
2.29.2

openSUSE Build Service is sponsored by