From 9fbbe0c120f69acbf62d9a5adc0e052cba9d8021 Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Mon, 10 May 2021 22:42:17 +0300 Subject: [PATCH 01/20] build: add signal-protocol-c dependency --- .gitmodules | 3 +++ CMakeLists.txt | 10 +++++++++- external/signal-protocol-c | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) create mode 160000 external/signal-protocol-c diff --git a/.gitmodules b/.gitmodules index bbe5364..c205907 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "external/qxmpp"] path = external/qxmpp url = https://github.com/qxmpp-project/qxmpp.git +[submodule "external/signal-protocol-c"] + path = external/signal-protocol-c + url = https://github.com/signalapp/libsignal-protocol-c.git diff --git a/CMakeLists.txt b/CMakeLists.txt index e88fdc8..7193a6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,7 +64,8 @@ add_custom_target(translations ALL DEPENDS ${QM_FILES}) qt5_add_resources(RCC resources/resources.qrc) -option(SYSTEM_QXMPP "Use system qxmpp lib" ON) +option(SYSTEM_QXMPP "Use system qxmpp lib" ON) +option(SYSTEM_SIGNAL "Use system signal-protocol-c lib" OFF) option(WITH_KWALLET "Build KWallet support module" ON) option(WITH_KIO "Build KIO support module" ON) @@ -79,10 +80,16 @@ if (SYSTEM_QXMPP) endif() endif() +# TODO: detect if libsignal-protocol-c exists and set SYSTEM_SIGNAL? + if(NOT SYSTEM_QXMPP) add_subdirectory(external/qxmpp) endif() +if (NOT SYSTEM_SIGNAL) + add_subdirectory(external/signal-protocol-c) +endif() + if (WITH_KWALLET) find_package(KF5Wallet CONFIG) @@ -119,6 +126,7 @@ add_subdirectory(external/simpleCrypt) target_link_libraries(squawk squawkUI) target_link_libraries(squawk squawkCORE) target_link_libraries(squawk uuid) +target_link_libraries(squawk signal-protocol-c) diff --git a/external/signal-protocol-c b/external/signal-protocol-c new file mode 160000 index 0000000..3a83a4f --- /dev/null +++ b/external/signal-protocol-c @@ -0,0 +1 @@ +Subproject commit 3a83a4f4ed2302ff6e68ab569c88793b50c22d28 From 04e745fad42711ccdc74e818a3e9e710766148f6 Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Tue, 11 May 2021 00:12:33 +0300 Subject: [PATCH 02/20] build: add qomemo dir with TODO --- CMakeLists.txt | 1 + qomemo/CMakeLists.txt | 23 +++++++++++++++++++++++ qomemo/TODO | 12 ++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 qomemo/CMakeLists.txt create mode 100644 qomemo/TODO diff --git a/CMakeLists.txt b/CMakeLists.txt index 7193a6f..930ae45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,6 +120,7 @@ endif() add_subdirectory(ui) add_subdirectory(core) add_subdirectory(plugins) +add_subdirectory(qomemo) add_subdirectory(external/simpleCrypt) diff --git a/qomemo/CMakeLists.txt b/qomemo/CMakeLists.txt new file mode 100644 index 0000000..5f92df4 --- /dev/null +++ b/qomemo/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.3) +project(qomemo) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") +set(CMAKE_AUTOMOC ON) + +find_package(Qt5Core CONFIG REQUIRED) +find_package(Qt5Xml CONFIG REQUIRED) +find_package(LMDB REQUIRED) + +set(qomemo_SRC) + +add_library(qomemo STATIC ${qomemo_SRC}) + +if(SYSTEM_QXMPP) + get_target_property(QXMPP_INTERFACE_INCLUDE_DIRECTORIES QXmpp::QXmpp INTERFACE_INCLUDE_DIRECTORIES) + target_include_directories(qomemo PUBLIC ${QXMPP_INTERFACE_INCLUDE_DIRECTORIES}) +endif() + +target_link_libraries(qomemo Qt5::Core) +target_link_libraries(qomemo Qt5::Xml) +target_link_libraries(qomemo qxmpp) +target_link_libraries(qomemo lmdb) diff --git a/qomemo/TODO b/qomemo/TODO new file mode 100644 index 0000000..6a5709d --- /dev/null +++ b/qomemo/TODO @@ -0,0 +1,12 @@ +* Generate device w/ keys +* PubSub set urn:xmpp:omemo:1:devices to announce new device +* PubSub set urn:xmpp:omemo:1:bundles to announce new key bundles +* PubSub get urn:xmpp:omemo:1:bundles to get user bundles + +Sending a message: +* Create urn:xmpp:sce:0 with padding and content +* Add <encrypted xmlns='urn:xmpp:omemo:1'> with header (key list) and payload (b64) + +Receiving a message: +* Check <keys> => <key> +* Decrypt (TODO) \ No newline at end of file From 7c1ae4737e4461583f52f29a6cbeca7b5e3669aa Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Tue, 11 May 2021 00:32:44 +0300 Subject: [PATCH 03/20] feat(omemo): WIP signal-protocol-c cpp wrapper --- qomemo/CMakeLists.txt | 4 +++- qomemo/signal.h | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 qomemo/signal.h diff --git a/qomemo/CMakeLists.txt b/qomemo/CMakeLists.txt index 5f92df4..0837eac 100644 --- a/qomemo/CMakeLists.txt +++ b/qomemo/CMakeLists.txt @@ -8,7 +8,7 @@ find_package(Qt5Core CONFIG REQUIRED) find_package(Qt5Xml CONFIG REQUIRED) find_package(LMDB REQUIRED) -set(qomemo_SRC) +set(qomemo_SRC signal.h) add_library(qomemo STATIC ${qomemo_SRC}) @@ -17,6 +17,8 @@ if(SYSTEM_QXMPP) target_include_directories(qomemo PUBLIC ${QXMPP_INTERFACE_INCLUDE_DIRECTORIES}) endif() +target_include_directories(qomemo PUBLIC ${CMAKE_SOURCE_DIR}/external/signal-protocol-c/src) + target_link_libraries(qomemo Qt5::Core) target_link_libraries(qomemo Qt5::Xml) target_link_libraries(qomemo qxmpp) diff --git a/qomemo/signal.h b/qomemo/signal.h new file mode 100644 index 0000000..e7e4c8b --- /dev/null +++ b/qomemo/signal.h @@ -0,0 +1,20 @@ +/* + * Created by victoria on 2021-05-11. + */ + +#pragma once + +#include <signal_protocol.h> + +namespace Signal +{ + +class Context {}; +class RatchetIdentityPair {}; +class SessionSignedPreKey {}; +class ProtocolKeyHelper {}; +class ProtocolStoreContext {}; +class SessionBuilder {}; +class SessionCipher {}; + +} From 7d648ab081c54563a49e0e6001046c0e629b62ef Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Tue, 11 May 2021 19:11:02 +0300 Subject: [PATCH 04/20] feat: add omemo buttons --- ui/CMakeLists.txt | 2 ++ ui/squawk.ui | 20 +++++++++++++++++++- ui/widgets/conversation.ui | 17 +++++++++++++++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 11b8f3d..2ff67e6 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -18,6 +18,8 @@ add_subdirectory(widgets) set(squawkUI_SRC squawk.cpp + squawk.h + squawk.ui models/accounts.cpp models/roster.cpp models/item.cpp diff --git a/ui/squawk.ui b/ui/squawk.ui index f6cb300..cb48de6 100644 --- a/ui/squawk.ui +++ b/ui/squawk.ui @@ -162,14 +162,24 @@ <x>0</x> <y>0</y> <width>718</width> - <height>27</height> + <height>23</height> </rect> </property> <widget class="QMenu" name="menuSettings"> <property name="title"> <string>Settings</string> </property> + <widget class="QMenu" name="menuOMEMO"> + <property name="title"> + <string>OMEMO</string> + </property> + <property name="icon"> + <iconset theme="security-high"/> + </property> + <addaction name="actionDeviceList"/> + </widget> <addaction name="actionAccounts"/> + <addaction name="menuOMEMO"/> </widget> <widget class="QMenu" name="menuFile"> <property name="title"> @@ -224,6 +234,14 @@ <string>Add conference</string> </property> </action> + <action name="actionDeviceList"> + <property name="icon"> + <iconset theme="computer-laptop"/> + </property> + <property name="text"> + <string>Device list</string> + </property> + </action> </widget> <resources/> <connections/> diff --git a/ui/widgets/conversation.ui b/ui/widgets/conversation.ui index bb38666..36b74a4 100644 --- a/ui/widgets/conversation.ui +++ b/ui/widgets/conversation.ui @@ -292,6 +292,19 @@ </property> </spacer> </item> + <item> + <widget class="QPushButton" name="encryptionButton"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="security-low"/> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> <item> <widget class="QPushButton" name="attachButton"> <property name="text"> @@ -400,8 +413,8 @@ background-color: transparent <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Liberation Sans'; font-size:10pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></string> +</style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Liberation Sans';"><br /></p></body></html></string> </property> <property name="acceptRichText"> <bool>false</bool> From bc66ab7e52dfc49dd2a1ad2472eecdf86a4d8b41 Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Tue, 11 May 2021 23:17:57 +0300 Subject: [PATCH 05/20] build: signal-protocol-c --- CMakeLists.txt | 9 ++++++--- qomemo/CMakeLists.txt | 26 +------------------------- qomemo/signal.cpp | 5 +++++ qomemo/signal.h | 2 +- 4 files changed, 13 insertions(+), 29 deletions(-) create mode 100644 qomemo/signal.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b9d151..aafc412 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,8 +43,14 @@ else () target_link_libraries(squawk PRIVATE QXmpp::QXmpp) endif () +# Signal if (NOT SYSTEM_SIGNAL) add_subdirectory(external/signal-protocol-c) + add_dependencies(squawk signal-protocol-c) + target_link_libraries(squawk PRIVATE signal-protocol-c) +else () + find_package(Signal REQUIRED) + target_link_libraries(squawk PRIVATE Signal::Signal) endif () ## KIO @@ -73,9 +79,6 @@ if (WITH_KWALLET) endif () endif () -## Signal (TODO) -# find_package(Signal REQUIRED) - ## LMDB find_package(LMDB REQUIRED) diff --git a/qomemo/CMakeLists.txt b/qomemo/CMakeLists.txt index 0837eac..dc0e0ea 100644 --- a/qomemo/CMakeLists.txt +++ b/qomemo/CMakeLists.txt @@ -1,25 +1 @@ -cmake_minimum_required(VERSION 3.3) -project(qomemo) - -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") -set(CMAKE_AUTOMOC ON) - -find_package(Qt5Core CONFIG REQUIRED) -find_package(Qt5Xml CONFIG REQUIRED) -find_package(LMDB REQUIRED) - -set(qomemo_SRC signal.h) - -add_library(qomemo STATIC ${qomemo_SRC}) - -if(SYSTEM_QXMPP) - get_target_property(QXMPP_INTERFACE_INCLUDE_DIRECTORIES QXmpp::QXmpp INTERFACE_INCLUDE_DIRECTORIES) - target_include_directories(qomemo PUBLIC ${QXMPP_INTERFACE_INCLUDE_DIRECTORIES}) -endif() - -target_include_directories(qomemo PUBLIC ${CMAKE_SOURCE_DIR}/external/signal-protocol-c/src) - -target_link_libraries(qomemo Qt5::Core) -target_link_libraries(qomemo Qt5::Xml) -target_link_libraries(qomemo qxmpp) -target_link_libraries(qomemo lmdb) +target_sources(squawk PRIVATE signal.h signal.cpp) \ No newline at end of file diff --git a/qomemo/signal.cpp b/qomemo/signal.cpp new file mode 100644 index 0000000..0e5257b --- /dev/null +++ b/qomemo/signal.cpp @@ -0,0 +1,5 @@ +/* + * Created by victoria on 2021-05-11. + */ + +#include "signal.h" diff --git a/qomemo/signal.h b/qomemo/signal.h index e7e4c8b..4aa5701 100644 --- a/qomemo/signal.h +++ b/qomemo/signal.h @@ -4,7 +4,7 @@ #pragma once -#include <signal_protocol.h> +#include <signal/signal_protocol.h> namespace Signal { From cb7e2ede75c947a8adeeb58d7a53c6fc2a196de8 Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Wed, 12 May 2021 00:58:39 +0300 Subject: [PATCH 06/20] feat: add OMEMODevices (WIP) --- ui/widgets/CMakeLists.txt | 3 ++ ui/widgets/omemodevices.cpp | 6 ++++ ui/widgets/omemodevices.h | 18 ++++++++++ ui/widgets/omemodevices.ui | 72 +++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 ui/widgets/omemodevices.cpp create mode 100644 ui/widgets/omemodevices.h create mode 100644 ui/widgets/omemodevices.ui diff --git a/ui/widgets/CMakeLists.txt b/ui/widgets/CMakeLists.txt index 0cacf6f..ac8bc32 100644 --- a/ui/widgets/CMakeLists.txt +++ b/ui/widgets/CMakeLists.txt @@ -16,6 +16,9 @@ target_sources(squawk PRIVATE newcontact.cpp newcontact.h newcontact.ui + omemodevices.cpp + omemodevices.h + omemodevices.ui room.cpp room.h ) diff --git a/ui/widgets/omemodevices.cpp b/ui/widgets/omemodevices.cpp new file mode 100644 index 0000000..630b85a --- /dev/null +++ b/ui/widgets/omemodevices.cpp @@ -0,0 +1,6 @@ +/* + * Created by victoria on 2021-05-12. + */ +#include "omemodevices.h" + +OMEMODevices::OMEMODevices(QWidget *parent) {} diff --git a/ui/widgets/omemodevices.h b/ui/widgets/omemodevices.h new file mode 100644 index 0000000..6dd34eb --- /dev/null +++ b/ui/widgets/omemodevices.h @@ -0,0 +1,18 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#pragma once + +#include <QDialog> + +namespace Ui { +class OMEMODevices; +} + +class OMEMODevices : public QDialog { + Q_OBJECT +public: + explicit OMEMODevices(QWidget *parent = nullptr); + ~OMEMODevices() override = default; +}; diff --git a/ui/widgets/omemodevices.ui b/ui/widgets/omemodevices.ui new file mode 100644 index 0000000..fb923db --- /dev/null +++ b/ui/widgets/omemodevices.ui @@ -0,0 +1,72 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Dialog</class> + <widget class="QDialog" name="Dialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Dialog</string> + </property> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="geometry" > + <rect> + <x>30</x> + <y>240</y> + <width>341</width> + <height>32</height> + </rect> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> + From 140b0fa6b42aad8287e391058542aa103b3ac674 Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Wed, 12 May 2021 01:44:47 +0300 Subject: [PATCH 07/20] feat: add omemo key list entry WIP --- ui/widgets/omemodevices.ui | 133 ++++++++++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 31 deletions(-) diff --git a/ui/widgets/omemodevices.ui b/ui/widgets/omemodevices.ui index fb923db..e407655 100644 --- a/ui/widgets/omemodevices.ui +++ b/ui/widgets/omemodevices.ui @@ -1,38 +1,110 @@ -<ui version="4.0" > - <author></author> - <comment></comment> - <exportmacro></exportmacro> +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> <class>Dialog</class> - <widget class="QDialog" name="Dialog" > - <property name="geometry" > + <widget class="QDialog" name="Dialog"> + <property name="geometry"> <rect> <x>0</x> <y>0</y> - <width>400</width> - <height>300</height> + <width>617</width> + <height>281</height> </rect> </property> - <property name="windowTitle" > + <property name="windowTitle"> <string>Dialog</string> </property> - <widget class="QDialogButtonBox" name="buttonBox" > - <property name="geometry" > - <rect> - <x>30</x> - <y>240</y> - <width>341</width> - <height>32</height> - </rect> - </property> - <property name="orientation" > - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons" > - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> - </widget> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QWidget" name="widget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label_2"> + <property name="font"> + <font> + <family>Monospace</family> + <pointsize>8</pointsize> + </font> + </property> + <property name="text"> + <string>1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Phone</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="approved"/> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="delete"/> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QListView" name="listView"/> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> </widget> - <pixmapfunction></pixmapfunction> <resources/> <connections> <connection> @@ -41,11 +113,11 @@ <receiver>Dialog</receiver> <slot>accept()</slot> <hints> - <hint type="sourcelabel" > + <hint type="sourcelabel"> <x>248</x> <y>254</y> </hint> - <hint type="destinationlabel" > + <hint type="destinationlabel"> <x>157</x> <y>274</y> </hint> @@ -57,11 +129,11 @@ <receiver>Dialog</receiver> <slot>reject()</slot> <hints> - <hint type="sourcelabel" > + <hint type="sourcelabel"> <x>316</x> <y>260</y> </hint> - <hint type="destinationlabel" > + <hint type="destinationlabel"> <x>286</x> <y>274</y> </hint> @@ -69,4 +141,3 @@ </connection> </connections> </ui> - From a6254d88b34ad943e822ad00093cdf601196def1 Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Wed, 12 May 2021 10:05:17 +0300 Subject: [PATCH 08/20] feat: OMEMO devices ui mock --- ui/CMakeLists.txt | 1 + ui/omemo/CMakeLists.txt | 5 + ui/omemo/omemodevices.cpp | 13 + ui/{widgets => omemo}/omemodevices.h | 5 +- ui/omemo/omemodevices.ui | 508 +++++++++++++++++++++++++++ ui/squawk.cpp | 11 +- ui/squawk.h | 1 + ui/widgets/CMakeLists.txt | 3 - ui/widgets/omemodevices.cpp | 6 - ui/widgets/omemodevices.ui | 143 -------- 10 files changed, 542 insertions(+), 154 deletions(-) create mode 100644 ui/omemo/CMakeLists.txt create mode 100644 ui/omemo/omemodevices.cpp rename ui/{widgets => omemo}/omemodevices.h (74%) create mode 100644 ui/omemo/omemodevices.ui delete mode 100644 ui/widgets/omemodevices.cpp delete mode 100644 ui/widgets/omemodevices.ui diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 36207b6..222f8d7 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -3,3 +3,4 @@ target_sources(squawk PRIVATE squawk.cpp squawk.h squawk.ui) add_subdirectory(models) add_subdirectory(utils) add_subdirectory(widgets) +add_subdirectory(omemo) \ No newline at end of file diff --git a/ui/omemo/CMakeLists.txt b/ui/omemo/CMakeLists.txt new file mode 100644 index 0000000..41afa04 --- /dev/null +++ b/ui/omemo/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(squawk PRIVATE + omemodevices.cpp + omemodevices.h + omemodevices.ui + ) \ No newline at end of file diff --git a/ui/omemo/omemodevices.cpp b/ui/omemo/omemodevices.cpp new file mode 100644 index 0000000..e746f5b --- /dev/null +++ b/ui/omemo/omemodevices.cpp @@ -0,0 +1,13 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#include "omemodevices.h" +#include "ui_omemodevices.h" + +OMEMODevices::OMEMODevices(QWidget *parent) + : QDialog(parent), m_ui(new Ui::OMEMODevices()) { + m_ui->setupUi(this); +} + +OMEMODevices::~OMEMODevices() {} diff --git a/ui/widgets/omemodevices.h b/ui/omemo/omemodevices.h similarity index 74% rename from ui/widgets/omemodevices.h rename to ui/omemo/omemodevices.h index 6dd34eb..b107baa 100644 --- a/ui/widgets/omemodevices.h +++ b/ui/omemo/omemodevices.h @@ -14,5 +14,8 @@ class OMEMODevices : public QDialog { Q_OBJECT public: explicit OMEMODevices(QWidget *parent = nullptr); - ~OMEMODevices() override = default; + ~OMEMODevices() override; + +private: + QScopedPointer<Ui::OMEMODevices> m_ui; }; diff --git a/ui/omemo/omemodevices.ui b/ui/omemo/omemodevices.ui new file mode 100644 index 0000000..c15dc9d --- /dev/null +++ b/ui/omemo/omemodevices.ui @@ -0,0 +1,508 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>OMEMODevices</class> + <widget class="QDialog" name="OMEMODevices"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>560</width> + <height>357</height> + </rect> + </property> + <property name="windowTitle"> + <string>OMEMO Devices</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>List of OMEMO fingerprints for fooooo@bar.com</string> + </property> + </widget> + </item> + <item> + <widget class="QScrollArea" name="scrollArea"> + <property name="widgetResizable"> + <bool>true</bool> + </property> + <widget class="QWidget" name="scrollAreaWidgetContents"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>540</width> + <height>281</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QFrame" name="frame_3"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="styleSheet"> + <string notr="true">background-color: rgb(6, 88, 6)</string> + </property> + <property name="frameShape"> + <enum>QFrame::Panel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="label_6"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>240</width> + <height>0</height> + </size> + </property> + <property name="font"> + <font> + <family>Monospace</family> + <pointsize>8</pointsize> + </font> + </property> + <property name="text"> + <string>1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_7"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Squawk</string> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_6"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="view-barcode-qr"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_9"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="approved"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_10"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="edit-delete-remove"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QFrame" name="frame_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::Panel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="label_3"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>240</width> + <height>0</height> + </size> + </property> + <property name="font"> + <font> + <family>Monospace</family> + <pointsize>8</pointsize> + </font> + </property> + <property name="text"> + <string>1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_4"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Dino</string> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_5"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="view-barcode-qr"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_7"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="approved"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_8"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="edit-delete-remove"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QFrame" name="frame"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="autoFillBackground"> + <bool>false</bool> + </property> + <property name="styleSheet"> + <string notr="true">background: rgb(111, 47, 47)</string> + </property> + <property name="frameShape"> + <enum>QFrame::Panel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <property name="lineWidth"> + <number>1</number> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>240</width> + <height>0</height> + </size> + </property> + <property name="font"> + <font> + <family>Monospace</family> + <pointsize>8</pointsize> + </font> + </property> + <property name="text"> + <string>1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Conversations</string> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_4"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="view-barcode-qr"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="approved"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset theme="edit-delete-remove"> + <normaloff>.</normaloff>.</iconset> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Close</set> + </property> + <property name="centerButtons"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>OMEMODevices</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>OMEMODevices</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/ui/squawk.cpp b/ui/squawk.cpp index fb79592..13c452a 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -20,6 +20,7 @@ #include "ui_squawk.h" #include <QDebug> #include <QIcon> +#include <ui/omemo/omemodevices.h> Squawk::Squawk(QWidget *parent) : QMainWindow(parent), @@ -60,7 +61,8 @@ Squawk::Squawk(QWidget *parent) : connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed); connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged); connect(&rosterModel, &Models::Roster::unnoticedMessage, this, &Squawk::onUnnoticedMessage); - + connect(m_ui->actionDeviceList, &QAction::triggered, this, &Squawk::onOMEMODevices); + connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged); connect(&rosterModel, &Models::Roster::requestArchive, this, &Squawk::onRequestArchive); connect(&rosterModel, &Models::Roster::fileDownloadRequest, this, &Squawk::fileDownloadRequest); @@ -134,6 +136,13 @@ void Squawk::onNewContact() nc->exec(); } +void Squawk::onOMEMODevices() { + auto od = new OMEMODevices(this); + + od->setAttribute(Qt::WA_DeleteOnClose); + od->show(); +} + void Squawk::onNewConference() { JoinConference* jc = new JoinConference(rosterModel.accountsModel, this); diff --git a/ui/squawk.h b/ui/squawk.h index 15d3f82..a4c130e 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -155,6 +155,7 @@ private slots: void onPasswordPromptRejected(); void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous); void onContextAboutToHide(); + void onOMEMODevices(); void onUnnoticedMessage(const QString& account, const Shared::Message& msg); diff --git a/ui/widgets/CMakeLists.txt b/ui/widgets/CMakeLists.txt index ac8bc32..0cacf6f 100644 --- a/ui/widgets/CMakeLists.txt +++ b/ui/widgets/CMakeLists.txt @@ -16,9 +16,6 @@ target_sources(squawk PRIVATE newcontact.cpp newcontact.h newcontact.ui - omemodevices.cpp - omemodevices.h - omemodevices.ui room.cpp room.h ) diff --git a/ui/widgets/omemodevices.cpp b/ui/widgets/omemodevices.cpp deleted file mode 100644 index 630b85a..0000000 --- a/ui/widgets/omemodevices.cpp +++ /dev/null @@ -1,6 +0,0 @@ -/* - * Created by victoria on 2021-05-12. - */ -#include "omemodevices.h" - -OMEMODevices::OMEMODevices(QWidget *parent) {} diff --git a/ui/widgets/omemodevices.ui b/ui/widgets/omemodevices.ui deleted file mode 100644 index e407655..0000000 --- a/ui/widgets/omemodevices.ui +++ /dev/null @@ -1,143 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>Dialog</class> - <widget class="QDialog" name="Dialog"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>617</width> - <height>281</height> - </rect> - </property> - <property name="windowTitle"> - <string>Dialog</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QWidget" name="widget" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QLabel" name="label_2"> - <property name="font"> - <font> - <family>Monospace</family> - <pointsize>8</pointsize> - </font> - </property> - <property name="text"> - <string>1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd 1234abcd</string> - </property> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Phone</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButton_2"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string/> - </property> - <property name="icon"> - <iconset theme="approved"/> - </property> - <property name="flat"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="pushButton"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string/> - </property> - <property name="icon"> - <iconset theme="delete"/> - </property> - <property name="flat"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QListView" name="listView"/> - </item> - <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> - </widget> - </item> - </layout> - </widget> - <resources/> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>Dialog</receiver> - <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>248</x> - <y>254</y> - </hint> - <hint type="destinationlabel"> - <x>157</x> - <y>274</y> - </hint> - </hints> - </connection> - <connection> - <sender>buttonBox</sender> - <signal>rejected()</signal> - <receiver>Dialog</receiver> - <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>316</x> - <y>260</y> - </hint> - <hint type="destinationlabel"> - <x>286</x> - <y>274</y> - </hint> - </hints> - </connection> - </connections> -</ui> From b22a4c8ca3dc1870d0bc88fb94cc381eacbfe45d Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Wed, 12 May 2021 12:33:35 +0300 Subject: [PATCH 09/20] feat(OMEMO): add Device, DeviceList, PreKey, Bundle + XML --- qomemo/CMakeLists.txt | 7 +- qomemo/qomemo.cpp | 243 ++++++++++++++++++++++++++++++++++++++++++ qomemo/qomemo.h | 56 ++++++++++ 3 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 qomemo/qomemo.cpp create mode 100644 qomemo/qomemo.h diff --git a/qomemo/CMakeLists.txt b/qomemo/CMakeLists.txt index dc0e0ea..f17475c 100644 --- a/qomemo/CMakeLists.txt +++ b/qomemo/CMakeLists.txt @@ -1 +1,6 @@ -target_sources(squawk PRIVATE signal.h signal.cpp) \ No newline at end of file +target_sources(squawk PRIVATE + signal.h + signal.cpp + qomemo.cpp + qomemo.h + ) \ No newline at end of file diff --git a/qomemo/qomemo.cpp b/qomemo/qomemo.cpp new file mode 100644 index 0000000..1695c04 --- /dev/null +++ b/qomemo/qomemo.cpp @@ -0,0 +1,243 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#include "qomemo.h" + +#include <QDebug> + +static bool elementMatches(const QXmppElement &element, const QString &tagName, + const QString &xmlns = QStringLiteral("")) { + if (element.tagName() != tagName) { + qWarning() << "tag name: expected = " << tagName + << ", got = " << element.tagName(); + return false; + } + + if (!xmlns.isEmpty() && element.attribute("xmlns") != xmlns) { + qWarning() << "xmlns: expected = " << xmlns + << ", got = " << element.attribute("xmlns"); + return false; + } + + return true; +} + +static QXmppElement createElement(const QString &tagName, + const QString &xmlns = QStringLiteral("")) { + QXmppElement el{}; + el.setTagName(tagName); + + if (!xmlns.isEmpty()) + el.setAttribute("xmlns", xmlns); + + return el; +} + +static QXmppElement createValue(const QString &value) { + auto el = createElement("value"); + el.setValue(value); + + return el; +} + +static QXmppElement createField(const QString &key, const QString &value, + bool hidden = false) { + auto field = createElement("field"); + field.setAttribute("var", key); + if (hidden) + field.setAttribute("type", "hidden"); + field.appendChild(createValue(value)); + + return field; +} + +static QXmppElement +createOpenPublishOptions(const QString &maxItems = QStringLiteral("")) { + auto formType = createField( + "FORM_TYPE", "http://jabber.org/protocol/pubsub#publish-options", true); + auto accessModel = createField("pubsub#access_model", "open"); + + auto x = createElement("x", "jabber:x:data"); + x.setAttribute("type", "submit"); + + x.appendChild(formType); + x.appendChild(accessModel); + + if (!maxItems.isEmpty()) + x.appendChild(createField("pubsub#max_items", maxItems)); + + auto publishOptions = createElement("publish-options"); + publishOptions.appendChild(x); + + return publishOptions; +} + +QXmppElement QOmemo::DeviceList::toXml() const { + auto element = createElement("devices", "urn:xmpp:omemo:1"); + + for (const auto &d : devices) { + element.appendChild(d.toXml()); + } + + return element; +} + +QXmppIq QOmemo::DeviceList::toIq() const { + QXmppIq iq{}; + + iq.setType(QXmppIq::Set); + + auto item = createElement("item"); + item.setAttribute("id", "current"); + item.appendChild(toXml()); + + auto publish = createElement("publish"); + publish.setAttribute("node", "urn:xmpp:omemo:1:devices"); + publish.appendChild(item); + + auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); + pubSub.appendChild(publish); + pubSub.appendChild(createOpenPublishOptions()); + + iq.extensions().push_back(pubSub); + + return iq; +} + +void QOmemo::DeviceList::fromXml(const QXmppElement &element) { + if (!elementMatches(element, "devices", "urn:xmpp:omemo:1")) + return; + + devices.clear(); + + auto deviceElement = element.firstChildElement("device"); + while (!deviceElement.isNull()) { + Device device{}; + device.fromXml(deviceElement); + devices.push_back(device); + + deviceElement = deviceElement.nextSiblingElement("device"); + } +} + +QXmppElement QOmemo::Device::toXml() const { + auto result = createElement("device"); + result.setAttribute("id", QString::number(id)); + + if (!label.isEmpty()) { + result.setAttribute("label", label); + } + + return result; +} + +void QOmemo::Device::fromXml(const QXmppElement &element) { + if (!elementMatches(element, "device")) + return; + + id = element.attribute("id").toInt(); + label = element.attribute("label"); +} + +QXmppElement QOmemo::PreKey::toXml() const { + auto pk = createElement("pk"); + pk.setAttribute("id", QString::number(id)); + // TODO: Base64 + pk.setValue(data); + + return pk; +} + +void QOmemo::PreKey::fromXml(const QXmppElement &element) { + if (!elementMatches(element, "pk")) + return; + + id = element.attribute("id").toInt(); + // TODO: Base64 + data = element.value(); +} + +QXmppElement QOmemo::Bundle::toXml() const { + auto spkNode = createElement("spk"); + spkNode.setAttribute("id", QString::number(spkId)); + spkNode.setValue(spk); + + auto spksNode = createElement("spks"); + spksNode.setValue(spks); + + auto ikNode = createElement("ik"); + ikNode.setValue(ik); + + auto prekeysNode = createElement("prekeys"); + for (const auto &pk : prekeys) { + prekeysNode.appendChild(pk.toXml()); + } + + auto result = createElement("bundle", "urn:xmpp:omemo:1"); + result.appendChild(spkNode); + result.appendChild(spksNode); + result.appendChild(ikNode); + result.appendChild(prekeysNode); + + return result; +} + +QXmppIq QOmemo::Bundle::toIq() const { + QXmppIq iq{}; + + iq.setType(QXmppIq::Set); + + auto item = createElement("item"); + item.setAttribute("id", QString::number(deviceId)); + item.appendChild(toXml()); + + auto publish = createElement("publish"); + publish.setAttribute("node", "urn:xmpp:omemo:1:bundles"); + publish.appendChild(item); + + auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); + pubSub.appendChild(publish); + pubSub.appendChild(createOpenPublishOptions("max")); + + iq.extensions().push_back(pubSub); + + return iq; +} + +void QOmemo::Bundle::fromXml(const QXmppElement &element) { + if (!elementMatches(element, "bundle", "urn:xmpp:omemo:1")) + return; + + auto spkNode = element.firstChildElement("spk"); + if (spkNode.isNull()) { + qWarning() << "'bundle': missing 'spk'"; + return; + } + spk = spkNode.value(); + spkId = spkNode.attribute("id").toInt(); + + auto spksNode = element.firstChildElement("spks"); + if (spksNode.isNull()) { + qWarning() << "'bundle': missing 'spks'"; + return; + } + spks = spksNode.value(); + + auto ikNode = element.firstChildElement("ik"); + if (ikNode.isNull()) { + qWarning() << "'bundle': missing 'ik'"; + return; + } + ik = ikNode.value(); + + auto prekeysNode = element.firstChildElement("prekeys"); + auto pkNode = prekeysNode.firstChildElement("pk"); + + prekeys.clear(); + while (!pkNode.isNull()) { + PreKey pk{}; + pk.fromXml(pkNode); + prekeys.push_back(pk); + } +} diff --git a/qomemo/qomemo.h b/qomemo/qomemo.h new file mode 100644 index 0000000..8955d1e --- /dev/null +++ b/qomemo/qomemo.h @@ -0,0 +1,56 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#pragma once + +#include <QXmppPubSubIq.h> + +namespace QOmemo { + +class Device { +public: + [[nodiscard]] QXmppElement toXml() const; + void fromXml(const QXmppElement &element); + + int id; + QString label; +}; + +class DeviceList { +public: + [[nodiscard]] QXmppElement toXml() const; + [[nodiscard]] QXmppIq toIq() const; + /// Expects a urn:xmpp:omemo:1:devices node + void fromXml(const QXmppElement &element); + +private: + QList<Device> devices; +}; + +class PreKey { +public: + [[nodiscard]] QXmppElement toXml() const; + /// Expects a <pk> + void fromXml(const QXmppElement &element); + + int id; + QString data; +}; + +class Bundle { +public: + [[nodiscard]] QXmppElement toXml() const; + [[nodiscard]] QXmppIq toIq() const; + void fromXml(const QXmppElement &element); + + int deviceId; + + QString spk; + int spkId; + QString spks; + QString ik; + QList<PreKey> prekeys; +}; + +} // namespace QOmemo From 6721b62629ab036df7230fc98ac70486e3d1825c Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Wed, 12 May 2021 15:00:41 +0300 Subject: [PATCH 10/20] feat(OMEMO): qxmppfactories, refactoring --- qomemo/CMakeLists.txt | 2 + qomemo/qomemo.cpp | 189 +++++++++++++++++++++++--------------- qomemo/qomemo.h | 40 +++++++- qomemo/sce.cpp | 24 +++++ qomemo/sce.h | 14 +++ shared/CMakeLists.txt | 2 + shared/qxmppfactories.cpp | 75 +++++++++++++++ shared/qxmppfactories.h | 25 +++++ 8 files changed, 294 insertions(+), 77 deletions(-) create mode 100644 qomemo/sce.cpp create mode 100644 qomemo/sce.h create mode 100644 shared/qxmppfactories.cpp create mode 100644 shared/qxmppfactories.h diff --git a/qomemo/CMakeLists.txt b/qomemo/CMakeLists.txt index f17475c..e8e4502 100644 --- a/qomemo/CMakeLists.txt +++ b/qomemo/CMakeLists.txt @@ -3,4 +3,6 @@ target_sources(squawk PRIVATE signal.cpp qomemo.cpp qomemo.h + sce.cpp + sce.h ) \ No newline at end of file diff --git a/qomemo/qomemo.cpp b/qomemo/qomemo.cpp index 1695c04..9d4266a 100644 --- a/qomemo/qomemo.cpp +++ b/qomemo/qomemo.cpp @@ -3,77 +3,16 @@ */ #include "qomemo.h" +#include "sce.h" +#include <shared/qxmppfactories.h> +#include <QBuffer> #include <QDebug> +#include <QDomDocument> -static bool elementMatches(const QXmppElement &element, const QString &tagName, - const QString &xmlns = QStringLiteral("")) { - if (element.tagName() != tagName) { - qWarning() << "tag name: expected = " << tagName - << ", got = " << element.tagName(); - return false; - } +using namespace QXmpp::Factories; - if (!xmlns.isEmpty() && element.attribute("xmlns") != xmlns) { - qWarning() << "xmlns: expected = " << xmlns - << ", got = " << element.attribute("xmlns"); - return false; - } - - return true; -} - -static QXmppElement createElement(const QString &tagName, - const QString &xmlns = QStringLiteral("")) { - QXmppElement el{}; - el.setTagName(tagName); - - if (!xmlns.isEmpty()) - el.setAttribute("xmlns", xmlns); - - return el; -} - -static QXmppElement createValue(const QString &value) { - auto el = createElement("value"); - el.setValue(value); - - return el; -} - -static QXmppElement createField(const QString &key, const QString &value, - bool hidden = false) { - auto field = createElement("field"); - field.setAttribute("var", key); - if (hidden) - field.setAttribute("type", "hidden"); - field.appendChild(createValue(value)); - - return field; -} - -static QXmppElement -createOpenPublishOptions(const QString &maxItems = QStringLiteral("")) { - auto formType = createField( - "FORM_TYPE", "http://jabber.org/protocol/pubsub#publish-options", true); - auto accessModel = createField("pubsub#access_model", "open"); - - auto x = createElement("x", "jabber:x:data"); - x.setAttribute("type", "submit"); - - x.appendChild(formType); - x.appendChild(accessModel); - - if (!maxItems.isEmpty()) - x.appendChild(createField("pubsub#max_items", maxItems)); - - auto publishOptions = createElement("publish-options"); - publishOptions.appendChild(x); - - return publishOptions; -} - -QXmppElement QOmemo::DeviceList::toXml() const { +QXmppElement QXmpp::Omemo::DeviceList::toXml() const { auto element = createElement("devices", "urn:xmpp:omemo:1"); for (const auto &d : devices) { @@ -83,7 +22,7 @@ QXmppElement QOmemo::DeviceList::toXml() const { return element; } -QXmppIq QOmemo::DeviceList::toIq() const { +QXmppIq QXmpp::Omemo::DeviceList::toIq() const { QXmppIq iq{}; iq.setType(QXmppIq::Set); @@ -105,7 +44,7 @@ QXmppIq QOmemo::DeviceList::toIq() const { return iq; } -void QOmemo::DeviceList::fromXml(const QXmppElement &element) { +void QXmpp::Omemo::DeviceList::fromXml(const QXmppElement &element) { if (!elementMatches(element, "devices", "urn:xmpp:omemo:1")) return; @@ -121,7 +60,7 @@ void QOmemo::DeviceList::fromXml(const QXmppElement &element) { } } -QXmppElement QOmemo::Device::toXml() const { +QXmppElement QXmpp::Omemo::Device::toXml() const { auto result = createElement("device"); result.setAttribute("id", QString::number(id)); @@ -132,7 +71,7 @@ QXmppElement QOmemo::Device::toXml() const { return result; } -void QOmemo::Device::fromXml(const QXmppElement &element) { +void QXmpp::Omemo::Device::fromXml(const QXmppElement &element) { if (!elementMatches(element, "device")) return; @@ -140,7 +79,7 @@ void QOmemo::Device::fromXml(const QXmppElement &element) { label = element.attribute("label"); } -QXmppElement QOmemo::PreKey::toXml() const { +QXmppElement QXmpp::Omemo::PreKey::toXml() const { auto pk = createElement("pk"); pk.setAttribute("id", QString::number(id)); // TODO: Base64 @@ -149,7 +88,7 @@ QXmppElement QOmemo::PreKey::toXml() const { return pk; } -void QOmemo::PreKey::fromXml(const QXmppElement &element) { +void QXmpp::Omemo::PreKey::fromXml(const QXmppElement &element) { if (!elementMatches(element, "pk")) return; @@ -158,7 +97,7 @@ void QOmemo::PreKey::fromXml(const QXmppElement &element) { data = element.value(); } -QXmppElement QOmemo::Bundle::toXml() const { +QXmppElement QXmpp::Omemo::Bundle::toXml() const { auto spkNode = createElement("spk"); spkNode.setAttribute("id", QString::number(spkId)); spkNode.setValue(spk); @@ -183,7 +122,7 @@ QXmppElement QOmemo::Bundle::toXml() const { return result; } -QXmppIq QOmemo::Bundle::toIq() const { +QXmppIq QXmpp::Omemo::Bundle::toIq() const { QXmppIq iq{}; iq.setType(QXmppIq::Set); @@ -205,7 +144,7 @@ QXmppIq QOmemo::Bundle::toIq() const { return iq; } -void QOmemo::Bundle::fromXml(const QXmppElement &element) { +void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) { if (!elementMatches(element, "bundle", "urn:xmpp:omemo:1")) return; @@ -241,3 +180,101 @@ void QOmemo::Bundle::fromXml(const QXmppElement &element) { prekeys.push_back(pk); } } + +QXmppPubSubIq QXmpp::Omemo::Bundle::fetchDeviceBundleIq(int deviceId) { + QXmppPubSubIq iq{}; + iq.setType(QXmppIq::Get); + iq.setQueryNode("urn:xmpp:omemo:1:bundles"); + + QXmppPubSubItem item{}; + item.setId(QString::number(deviceId)); + iq.setItems({item}); + + return iq; +} + +QXmppElement QXmpp::Omemo::EncryptedMessage::header() const { + auto result = createElement("header"); + result.setAttribute("sid", QString::number(fromDeviceId)); + + return result; +} + +QXmppElement QXmpp::Omemo::EncryptedMessage::toXml() const { + auto result = createElement("encrypted", "urn:xmpp:omemo:1"); + + result.appendChild(header()); + + for (const auto &key : keys) { + result.appendChild(key.toXml()); + } + + return result; +} + +QXmppElement QXmpp::Omemo::EncryptedMessage::payload() const { + QBuffer buffer; + buffer.open(QIODevice::ReadWrite); + QXmlStreamWriter writer(&buffer); + message.toXml(&writer); + + QDomDocument doc; + doc.setContent(buffer.data(), true); + + QXmppElement root(doc.documentElement()); + root.setTagName("payload"); + + return root; +} + +QXmppElement QXmpp::Omemo::EncryptedMessage::content() const { + auto envelope = createElement("content", "urn:xmpp:sce:0"); + + envelope.appendChild(payload()); + + if (!from.isEmpty()) { + auto fromNode = createElement("from"); + fromNode.setAttribute("jid", from); + envelope.appendChild(fromNode); + } + + if (!to.isEmpty()) { + auto toNode = createElement("to"); + toNode.setAttribute("jid", to); + envelope.appendChild(toNode); + } + + if (!timestamp.isNull()) { + auto timeNode = createElement("time"); + timeNode.setAttribute("stamp", timestamp.toString(Qt::DateFormat::ISODate)); + envelope.appendChild(timeNode); + } + + auto rpad = createElement("rpad"); + rpad.setValue(QXmpp::Sce::generatePadding()); + envelope.appendChild(rpad); + + return envelope; +} + +QXmppElement QXmpp::Omemo::MessageKey::toXml() const { + auto result = createElement("key"); + + if (kex) + result.setAttribute("kex", "true"); + + result.setValue(key); + + return result; +} + +QXmppElement QXmpp::Omemo::HeaderKeys::toXml() const { + auto result = createElement("keys"); + result.setAttribute("jid", jid); + + for (const auto &key : keys) { + result.appendChild(key.toXml()); + } + + return result; +} diff --git a/qomemo/qomemo.h b/qomemo/qomemo.h index 8955d1e..906d0ba 100644 --- a/qomemo/qomemo.h +++ b/qomemo/qomemo.h @@ -6,7 +6,10 @@ #include <QXmppPubSubIq.h> -namespace QOmemo { +#include <QDateTime> +#include <QXmppMessage.h> + +namespace QXmpp::Omemo { class Device { public: @@ -40,6 +43,8 @@ public: class Bundle { public: + [[nodiscard]] static QXmppPubSubIq fetchDeviceBundleIq(int deviceId); + [[nodiscard]] QXmppElement toXml() const; [[nodiscard]] QXmppIq toIq() const; void fromXml(const QXmppElement &element); @@ -53,4 +58,37 @@ public: QList<PreKey> prekeys; }; +class MessageKey { +public: + [[nodiscard]] QXmppElement toXml() const; + + bool kex{}; + QString key{}; +}; + +class HeaderKeys { +public: + [[nodiscard]] QXmppElement toXml() const; + + QString jid{}; + QList<MessageKey> keys{}; +}; + +class EncryptedMessage { +public: + [[nodiscard]] QXmppElement header() const; + [[nodiscard]] QXmppElement content() const; + [[nodiscard]] QXmppElement toXml() const; + [[nodiscard]] QXmppElement payload() const; + + int fromDeviceId{}; + + QList<HeaderKeys> keys{}; + QString from{}; + QString to{}; + QDateTime timestamp{}; + + QXmppMessage message{}; +}; + } // namespace QOmemo diff --git a/qomemo/sce.cpp b/qomemo/sce.cpp new file mode 100644 index 0000000..decb8b3 --- /dev/null +++ b/qomemo/sce.cpp @@ -0,0 +1,24 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#include "sce.h" + +#include <QRandomGenerator> + +constexpr int RPAD_MAX_LENGTH = 200; + +QString QXmpp::Sce::generatePadding() { + QRandomGenerator random{}; + QString result{}; + QString alphabet{ QStringLiteral("!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~") }; + + auto length = random.bounded(RPAD_MAX_LENGTH); + result.resize(length); + + for (auto i = 0; i < length; ++i) { + result[i] = alphabet[random.bounded(alphabet.length())]; + } + + return result; +} diff --git a/qomemo/sce.h b/qomemo/sce.h new file mode 100644 index 0000000..2cea049 --- /dev/null +++ b/qomemo/sce.h @@ -0,0 +1,14 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#pragma once + +#include <QXmppElement.h> +#include <QDateTime> + +namespace QXmpp::Sce { + +QString generatePadding(); + +} diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index a36b516..97ed619 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -16,4 +16,6 @@ target_sources(squawk PRIVATE utils.h vcard.cpp vcard.h + qxmppfactories.cpp + qxmppfactories.h ) diff --git a/shared/qxmppfactories.cpp b/shared/qxmppfactories.cpp new file mode 100644 index 0000000..dc53388 --- /dev/null +++ b/shared/qxmppfactories.cpp @@ -0,0 +1,75 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#include "qxmppfactories.h" + +#include <QDebug> + +bool QXmpp::Factories::elementMatches(const QXmppElement &element, + const QString &tagName, + const QString &xmlns) { + if (element.tagName() != tagName) { + qWarning() << "tag name: expected = " << tagName + << ", got = " << element.tagName(); + return false; + } + + if (!xmlns.isEmpty() && element.attribute("xmlns") != xmlns) { + qWarning() << "xmlns: expected = " << xmlns + << ", got = " << element.attribute("xmlns"); + return false; + } + + return true; +} + +QXmppElement QXmpp::Factories::createElement(const QString &tagName, + const QString &xmlns) { + QXmppElement el{}; + el.setTagName(tagName); + + if (!xmlns.isEmpty()) + el.setAttribute("xmlns", xmlns); + + return el; +} + +QXmppElement QXmpp::Factories::createValue(const QString &value) { + auto el = createElement("value"); + el.setValue(value); + + return el; +} + +QXmppElement QXmpp::Factories::createField(const QString &key, + const QString &value, bool hidden) { + auto field = createElement("field"); + field.setAttribute("var", key); + if (hidden) + field.setAttribute("type", "hidden"); + field.appendChild(createValue(value)); + + return field; +} + +QXmppElement +QXmpp::Factories::createOpenPublishOptions(const QString &maxItems) { + auto formType = createField( + "FORM_TYPE", "http://jabber.org/protocol/pubsub#publish-options", true); + auto accessModel = createField("pubsub#access_model", "open"); + + auto x = createElement("x", "jabber:x:data"); + x.setAttribute("type", "submit"); + + x.appendChild(formType); + x.appendChild(accessModel); + + if (!maxItems.isEmpty()) + x.appendChild(createField("pubsub#max_items", maxItems)); + + auto publishOptions = createElement("publish-options"); + publishOptions.appendChild(x); + + return publishOptions; +} \ No newline at end of file diff --git a/shared/qxmppfactories.h b/shared/qxmppfactories.h new file mode 100644 index 0000000..d7d78e6 --- /dev/null +++ b/shared/qxmppfactories.h @@ -0,0 +1,25 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#pragma once + +#include <QXmppElement.h> + +namespace QXmpp::Factories { + +bool elementMatches(const QXmppElement &element, const QString &tagName, + const QString &xmlns = QStringLiteral("")); + +QXmppElement createElement(const QString &tagName, + const QString &xmlns = QStringLiteral("")); + +QXmppElement createValue(const QString &value); + +QXmppElement createField(const QString &key, const QString &value, + bool hidden = false); + +QXmppElement +createOpenPublishOptions(const QString &maxItems = QStringLiteral("")); + +} // namespace QXmpp::Factories From b1a8f162ceee0978c14362d065572a3054b4864e Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Wed, 12 May 2021 15:50:52 +0300 Subject: [PATCH 11/20] feat(OMEMO): DeviceKeyStorage, DeviceService, UserDeviceList stubs --- qomemo/CMakeLists.txt | 6 ++++++ qomemo/device_key_storage.cpp | 15 +++++++++++++++ qomemo/device_key_storage.h | 18 ++++++++++++++++++ qomemo/device_service.cpp | 30 ++++++++++++++++++++++++++++++ qomemo/device_service.h | 31 +++++++++++++++++++++++++++++++ qomemo/qomemo.h | 2 +- qomemo/user_device_list.cpp | 10 ++++++++++ qomemo/user_device_list.h | 17 +++++++++++++++++ 8 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 qomemo/device_key_storage.cpp create mode 100644 qomemo/device_key_storage.h create mode 100644 qomemo/device_service.cpp create mode 100644 qomemo/device_service.h create mode 100644 qomemo/user_device_list.cpp create mode 100644 qomemo/user_device_list.h diff --git a/qomemo/CMakeLists.txt b/qomemo/CMakeLists.txt index e8e4502..40abc25 100644 --- a/qomemo/CMakeLists.txt +++ b/qomemo/CMakeLists.txt @@ -1,8 +1,14 @@ target_sources(squawk PRIVATE + device_key_storage.cpp + device_key_storage.h + device_service.cpp + device_service.h signal.h signal.cpp qomemo.cpp qomemo.h sce.cpp sce.h + user_device_list.cpp + user_device_list.h ) \ No newline at end of file diff --git a/qomemo/device_key_storage.cpp b/qomemo/device_key_storage.cpp new file mode 100644 index 0000000..1ba017e --- /dev/null +++ b/qomemo/device_key_storage.cpp @@ -0,0 +1,15 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#include "device_key_storage.h" +#include <QRandomGenerator> + +int QXmpp::Omemo::DeviceKeyStorage::generateDeviceId() { + QRandomGenerator random{}; + + return 1 + random.bounded(INT32_MAX - 1); +} + +QXmpp::Omemo::DeviceKeyStorage::DeviceKeyStorage(int deviceId) + : deviceId(deviceId) {} diff --git a/qomemo/device_key_storage.h b/qomemo/device_key_storage.h new file mode 100644 index 0000000..a0d8ce2 --- /dev/null +++ b/qomemo/device_key_storage.h @@ -0,0 +1,18 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#pragma once + +namespace QXmpp::Omemo { + +class DeviceKeyStorage { +public: + static int generateDeviceId(); + + explicit DeviceKeyStorage(int deviceId); + + const int deviceId; +}; + +} // namespace QXmpp::Omemo diff --git a/qomemo/device_service.cpp b/qomemo/device_service.cpp new file mode 100644 index 0000000..4333d7f --- /dev/null +++ b/qomemo/device_service.cpp @@ -0,0 +1,30 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#include "device_service.h" + +#include <QXmppClient.h> +#include <QXmppPubSubIq.h> + +#include <QDebug> + +QXmpp::Omemo::DeviceService::DeviceService(QXmppClient &client, QObject *parent) + : QObject(parent), client(client) { + connect(&client, &QXmppClient::iqReceived, this, + &DeviceService::onIqReceived); +} + +void QXmpp::Omemo::DeviceService::onIqReceived(const QXmppIq &iq) { + // Update OMEMO device list +} + +void QXmpp::Omemo::DeviceService::fetch() { + QXmppPubSubIq fetchOwnDevices{}; + fetchOwnDevices.setFrom(client.configuration().jid()); + fetchOwnDevices.setTo(client.configuration().jidBare()); + fetchOwnDevices.setType(QXmppIq::Get); + fetchOwnDevices.setQueryNode("urn:xmpp:omemo:1:devices"); + + client.sendPacket(fetchOwnDevices); +} diff --git a/qomemo/device_service.h b/qomemo/device_service.h new file mode 100644 index 0000000..fc2e4d5 --- /dev/null +++ b/qomemo/device_service.h @@ -0,0 +1,31 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#pragma once + +#include "user_device_list.h" + +#include <QXmppClient.h> + +namespace QXmpp::Omemo { + +class DeviceService : public QObject { + Q_OBJECT + +public: + DeviceService(QXmppClient& client, QObject *parent); + + void fetch(); + +public slots: + void onIqReceived(const QXmppIq& iq); + +private: + void announce(); + + QXmppClient& client; + QMap<QString, UserDeviceList> device_lists{}; +}; + +} // namespace QXmpp::Omemo diff --git a/qomemo/qomemo.h b/qomemo/qomemo.h index 906d0ba..e815d11 100644 --- a/qomemo/qomemo.h +++ b/qomemo/qomemo.h @@ -91,4 +91,4 @@ public: QXmppMessage message{}; }; -} // namespace QOmemo +} // namespace QXmpp::Omemo diff --git a/qomemo/user_device_list.cpp b/qomemo/user_device_list.cpp new file mode 100644 index 0000000..9a3f18a --- /dev/null +++ b/qomemo/user_device_list.cpp @@ -0,0 +1,10 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#include "user_device_list.h" + +#include <utility> + +QXmpp::Omemo::UserDeviceList::UserDeviceList(QString jid) + : jid(std::move(jid)) {} diff --git a/qomemo/user_device_list.h b/qomemo/user_device_list.h new file mode 100644 index 0000000..26a88ca --- /dev/null +++ b/qomemo/user_device_list.h @@ -0,0 +1,17 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#pragma once + +#include <QString> +namespace QXmpp::Omemo { + +class UserDeviceList { +public: + explicit UserDeviceList(QString jid); + + const QString jid; +}; + +} // namespace QXmpp::Omemo From 006752b31ca21cd2b911911d002a09d6c4d3b322 Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Wed, 12 May 2021 17:33:34 +0300 Subject: [PATCH 12/20] feat(OMEMO): QXmppClientExtension for OMEMO --- core/account.cpp | 6 ++- core/account.h | 21 ++++++---- qomemo/CMakeLists.txt | 2 + qomemo/device_service.cpp | 27 +++--------- qomemo/device_service.h | 10 ++--- qomemo/qomemo.cpp | 76 ++++++++++++++-------------------- qomemo/qomemo.h | 21 +++------- qomemo/qxmpp_omemo_manager.cpp | 66 +++++++++++++++++++++++++++++ qomemo/qxmpp_omemo_manager.h | 36 ++++++++++++++++ 9 files changed, 167 insertions(+), 98 deletions(-) create mode 100644 qomemo/qxmpp_omemo_manager.cpp create mode 100644 qomemo/qxmpp_omemo_manager.h diff --git a/core/account.cpp b/core/account.cpp index 5ce29ee..d38e889 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -47,7 +47,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& network(p_net), passwordType(Shared::AccountPassword::plain), mh(new MessageHandler(this)), - rh(new RosterHandler(this)) + rh(new RosterHandler(this)), + omemo(new QXmpp::Omemo::Manager()) { config.setUser(p_login); config.setDomain(p_server); @@ -90,7 +91,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(rcpm); QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived); - + + client.addExtension(omemo.get()); QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); path += "/" + name; diff --git a/core/account.h b/core/account.h index a0db9f9..2a3816e 100644 --- a/core/account.h +++ b/core/account.h @@ -30,23 +30,24 @@ #include <map> #include <set> -#include <QXmppRosterManager.h> -#include <QXmppCarbonManager.h> -#include <QXmppDiscoveryManager.h> -#include <QXmppMamManager.h> -#include <QXmppMucManager.h> -#include <QXmppClient.h> #include <QXmppBookmarkManager.h> #include <QXmppBookmarkSet.h> +#include <QXmppCarbonManager.h> +#include <QXmppClient.h> +#include <QXmppDiscoveryManager.h> +#include <QXmppMamManager.h> +#include <QXmppMessageReceiptManager.h> +#include <QXmppMucManager.h> +#include <QXmppRosterManager.h> #include <QXmppUploadRequestManager.h> #include <QXmppVCardIq.h> #include <QXmppVCardManager.h> -#include <QXmppMessageReceiptManager.h> +#include <qomemo/qxmpp_omemo_manager.h> -#include "shared/shared.h" -#include "contact.h" #include "conference.h" +#include "contact.h" #include "networkaccess.h" +#include "shared/shared.h" #include "handlers/messagehandler.h" #include "handlers/rosterhandler.h" @@ -165,6 +166,8 @@ private: MessageHandler* mh; RosterHandler* rh; + + QScopedPointer<QXmpp::Omemo::Manager> omemo; private slots: void onClientStateChange(QXmppClient::State state); diff --git a/qomemo/CMakeLists.txt b/qomemo/CMakeLists.txt index 40abc25..1512264 100644 --- a/qomemo/CMakeLists.txt +++ b/qomemo/CMakeLists.txt @@ -11,4 +11,6 @@ target_sources(squawk PRIVATE sce.h user_device_list.cpp user_device_list.h + qxmpp_omemo_manager.cpp + qxmpp_omemo_manager.h ) \ No newline at end of file diff --git a/qomemo/device_service.cpp b/qomemo/device_service.cpp index 4333d7f..5e9a460 100644 --- a/qomemo/device_service.cpp +++ b/qomemo/device_service.cpp @@ -4,27 +4,12 @@ #include "device_service.h" -#include <QXmppClient.h> -#include <QXmppPubSubIq.h> +QXmpp::Omemo::DeviceService::DeviceService(QObject *parent) : QObject(parent) {} -#include <QDebug> +void QXmpp::Omemo::DeviceService::onDeviceListReceived( + const QString &jid, const QXmpp::Omemo::DeviceList &list) { -QXmpp::Omemo::DeviceService::DeviceService(QXmppClient &client, QObject *parent) - : QObject(parent), client(client) { - connect(&client, &QXmppClient::iqReceived, this, - &DeviceService::onIqReceived); -} - -void QXmpp::Omemo::DeviceService::onIqReceived(const QXmppIq &iq) { - // Update OMEMO device list -} - -void QXmpp::Omemo::DeviceService::fetch() { - QXmppPubSubIq fetchOwnDevices{}; - fetchOwnDevices.setFrom(client.configuration().jid()); - fetchOwnDevices.setTo(client.configuration().jidBare()); - fetchOwnDevices.setType(QXmppIq::Get); - fetchOwnDevices.setQueryNode("urn:xmpp:omemo:1:devices"); - - client.sendPacket(fetchOwnDevices); + for (const auto &device : list.devices) { + qInfo() << "Got device for" << jid << ":" << device.id; + } } diff --git a/qomemo/device_service.h b/qomemo/device_service.h index fc2e4d5..9d87efb 100644 --- a/qomemo/device_service.h +++ b/qomemo/device_service.h @@ -4,6 +4,7 @@ #pragma once +#include "qomemo.h" #include "user_device_list.h" #include <QXmppClient.h> @@ -14,17 +15,12 @@ class DeviceService : public QObject { Q_OBJECT public: - DeviceService(QXmppClient& client, QObject *parent); - - void fetch(); + explicit DeviceService(QObject *parent); public slots: - void onIqReceived(const QXmppIq& iq); + void onDeviceListReceived(const QString& jid, const DeviceList& list); private: - void announce(); - - QXmppClient& client; QMap<QString, UserDeviceList> device_lists{}; }; diff --git a/qomemo/qomemo.cpp b/qomemo/qomemo.cpp index 9d4266a..ab863f2 100644 --- a/qomemo/qomemo.cpp +++ b/qomemo/qomemo.cpp @@ -13,7 +13,7 @@ using namespace QXmpp::Factories; QXmppElement QXmpp::Omemo::DeviceList::toXml() const { - auto element = createElement("devices", "urn:xmpp:omemo:1"); + auto element = createElement("list", "eu.siacs.conversations.axolotl"); for (const auto &d : devices) { element.appendChild(d.toXml()); @@ -28,11 +28,10 @@ QXmppIq QXmpp::Omemo::DeviceList::toIq() const { iq.setType(QXmppIq::Set); auto item = createElement("item"); - item.setAttribute("id", "current"); item.appendChild(toXml()); auto publish = createElement("publish"); - publish.setAttribute("node", "urn:xmpp:omemo:1:devices"); + publish.setAttribute("node", "eu.siacs.conversations.axolotl.devicelist"); publish.appendChild(item); auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); @@ -45,7 +44,7 @@ QXmppIq QXmpp::Omemo::DeviceList::toIq() const { } void QXmpp::Omemo::DeviceList::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "devices", "urn:xmpp:omemo:1")) + if (!elementMatches(element, "list", "eu.siacs.conversations.axolotl")) return; devices.clear(); @@ -64,10 +63,6 @@ QXmppElement QXmpp::Omemo::Device::toXml() const { auto result = createElement("device"); result.setAttribute("id", QString::number(id)); - if (!label.isEmpty()) { - result.setAttribute("label", label); - } - return result; } @@ -76,12 +71,11 @@ void QXmpp::Omemo::Device::fromXml(const QXmppElement &element) { return; id = element.attribute("id").toInt(); - label = element.attribute("label"); } QXmppElement QXmpp::Omemo::PreKey::toXml() const { - auto pk = createElement("pk"); - pk.setAttribute("id", QString::number(id)); + auto pk = createElement("preKeyPublic"); + pk.setAttribute("preKeyId", QString::number(id)); // TODO: Base64 pk.setValue(data); @@ -89,23 +83,23 @@ QXmppElement QXmpp::Omemo::PreKey::toXml() const { } void QXmpp::Omemo::PreKey::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "pk")) + if (!elementMatches(element, "preKeyPublic")) return; - id = element.attribute("id").toInt(); + id = element.attribute("preKeyId").toInt(); // TODO: Base64 data = element.value(); } QXmppElement QXmpp::Omemo::Bundle::toXml() const { - auto spkNode = createElement("spk"); - spkNode.setAttribute("id", QString::number(spkId)); + auto spkNode = createElement("signedPreKeyPublic"); + spkNode.setAttribute("signedPreKeyId", QString::number(spkId)); spkNode.setValue(spk); - auto spksNode = createElement("spks"); + auto spksNode = createElement("signedPreKeySignature"); spksNode.setValue(spks); - auto ikNode = createElement("ik"); + auto ikNode = createElement("identityKey"); ikNode.setValue(ik); auto prekeysNode = createElement("prekeys"); @@ -113,7 +107,7 @@ QXmppElement QXmpp::Omemo::Bundle::toXml() const { prekeysNode.appendChild(pk.toXml()); } - auto result = createElement("bundle", "urn:xmpp:omemo:1"); + auto result = createElement("bundle", "eu.siacs.conversations.axolotl"); result.appendChild(spkNode); result.appendChild(spksNode); result.appendChild(ikNode); @@ -122,22 +116,21 @@ QXmppElement QXmpp::Omemo::Bundle::toXml() const { return result; } -QXmppIq QXmpp::Omemo::Bundle::toIq() const { +QXmppIq QXmpp::Omemo::Bundle::toIq(int deviceId) const { QXmppIq iq{}; iq.setType(QXmppIq::Set); auto item = createElement("item"); - item.setAttribute("id", QString::number(deviceId)); item.appendChild(toXml()); auto publish = createElement("publish"); - publish.setAttribute("node", "urn:xmpp:omemo:1:bundles"); + publish.setAttribute("node", QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId)); publish.appendChild(item); auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); pubSub.appendChild(publish); - pubSub.appendChild(createOpenPublishOptions("max")); + pubSub.appendChild(createOpenPublishOptions()); iq.extensions().push_back(pubSub); @@ -145,7 +138,7 @@ QXmppIq QXmpp::Omemo::Bundle::toIq() const { } void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "bundle", "urn:xmpp:omemo:1")) + if (!elementMatches(element, "bundle", "eu.siacs.conversations.axolotl")) return; auto spkNode = element.firstChildElement("spk"); @@ -184,7 +177,7 @@ void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) { QXmppPubSubIq QXmpp::Omemo::Bundle::fetchDeviceBundleIq(int deviceId) { QXmppPubSubIq iq{}; iq.setType(QXmppIq::Get); - iq.setQueryNode("urn:xmpp:omemo:1:bundles"); + iq.setQueryNode(QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId)); QXmppPubSubItem item{}; item.setId(QString::number(deviceId)); @@ -197,13 +190,8 @@ QXmppElement QXmpp::Omemo::EncryptedMessage::header() const { auto result = createElement("header"); result.setAttribute("sid", QString::number(fromDeviceId)); - return result; -} - -QXmppElement QXmpp::Omemo::EncryptedMessage::toXml() const { - auto result = createElement("encrypted", "urn:xmpp:omemo:1"); - - result.appendChild(header()); + auto ivNode = createElement("iv"); + ivNode.setValue(iv); for (const auto &key : keys) { result.appendChild(key.toXml()); @@ -212,6 +200,16 @@ QXmppElement QXmpp::Omemo::EncryptedMessage::toXml() const { return result; } +QXmppElement QXmpp::Omemo::EncryptedMessage::toXml() const { + auto result = createElement("encrypted", "eu.siacs.conversations.axolotl"); + + result.appendChild(header()); + // TODO: Payload is optional + result.appendChild(payload()); + + return result; +} + QXmppElement QXmpp::Omemo::EncryptedMessage::payload() const { QBuffer buffer; buffer.open(QIODevice::ReadWrite); @@ -260,21 +258,11 @@ QXmppElement QXmpp::Omemo::EncryptedMessage::content() const { QXmppElement QXmpp::Omemo::MessageKey::toXml() const { auto result = createElement("key"); - if (kex) - result.setAttribute("kex", "true"); + result.setAttribute("rid", QString::number(receivingDeviceId)); + if (prekey) + result.setAttribute("prekey", "true"); result.setValue(key); return result; } - -QXmppElement QXmpp::Omemo::HeaderKeys::toXml() const { - auto result = createElement("keys"); - result.setAttribute("jid", jid); - - for (const auto &key : keys) { - result.appendChild(key.toXml()); - } - - return result; -} diff --git a/qomemo/qomemo.h b/qomemo/qomemo.h index e815d11..346cc90 100644 --- a/qomemo/qomemo.h +++ b/qomemo/qomemo.h @@ -17,7 +17,6 @@ public: void fromXml(const QXmppElement &element); int id; - QString label; }; class DeviceList { @@ -27,7 +26,6 @@ public: /// Expects a urn:xmpp:omemo:1:devices node void fromXml(const QXmppElement &element); -private: QList<Device> devices; }; @@ -46,11 +44,9 @@ public: [[nodiscard]] static QXmppPubSubIq fetchDeviceBundleIq(int deviceId); [[nodiscard]] QXmppElement toXml() const; - [[nodiscard]] QXmppIq toIq() const; + [[nodiscard]] QXmppIq toIq(int deviceId) const; void fromXml(const QXmppElement &element); - int deviceId; - QString spk; int spkId; QString spks; @@ -62,18 +58,11 @@ class MessageKey { public: [[nodiscard]] QXmppElement toXml() const; - bool kex{}; + int receivingDeviceId{}; + bool prekey{}; QString key{}; }; -class HeaderKeys { -public: - [[nodiscard]] QXmppElement toXml() const; - - QString jid{}; - QList<MessageKey> keys{}; -}; - class EncryptedMessage { public: [[nodiscard]] QXmppElement header() const; @@ -83,11 +72,13 @@ public: int fromDeviceId{}; - QList<HeaderKeys> keys{}; + QList<MessageKey> keys{}; QString from{}; QString to{}; QDateTime timestamp{}; + QString iv{}; + QXmppMessage message{}; }; diff --git a/qomemo/qxmpp_omemo_manager.cpp b/qomemo/qxmpp_omemo_manager.cpp new file mode 100644 index 0000000..26598f7 --- /dev/null +++ b/qomemo/qxmpp_omemo_manager.cpp @@ -0,0 +1,66 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#include "qxmpp_omemo_manager.h" + +#include <QDomElement> + +#include <QXmppClient.h> +#include <QXmppPubSubIq.h> +#include <iostream> + +QXmpp::Omemo::Manager::Manager() : deviceService(new QXmpp::Omemo::DeviceService(this)) { + connect(this, &Manager::deviceListReceived, deviceService.get(), &DeviceService::onDeviceListReceived); +} + +bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) { + QString str{}; + QTextStream info(&str); + stanza.save(info, 4); + + std::cout << str.toStdString(); + + if (stanza.tagName() == "iq") { + if (stanza.attribute("type") == "result") { + auto pubsub = stanza.firstChildElement("pubsub"); + if (!pubsub.isNull()) { + auto items = pubsub.firstChildElement("items"); + if (items.attribute("node") == "eu.siacs.conversations.axolotl.devicelist") { + auto item = items.firstChildElement("item"); + if (!item.isNull()) { + auto list = item.firstChildElement("list"); + if (!list.isNull()) { + DeviceList deviceList{}; + deviceList.fromXml(list); + emit deviceListReceived(stanza.attribute("from"), deviceList); + + return true; + } + } + } + } + } + } + + return false; +} + +void QXmpp::Omemo::Manager::setClient(QXmppClient *client) { + QXmppClientExtension::setClient(client); + + if (!client) + return; + + QObject::connect(client, &QXmppClient::connected, this, &Manager::fetchOwnDevices); +} + +void QXmpp::Omemo::Manager::fetchOwnDevices() { + QXmppPubSubIq iq{}; + iq.setFrom(client()->configuration().jid()); + iq.setTo(client()->configuration().jidBare()); + iq.setType(QXmppIq::Get); + iq.setQueryNode("eu.siacs.conversations.axolotl.devicelist"); + + client()->sendPacket(iq); +} diff --git a/qomemo/qxmpp_omemo_manager.h b/qomemo/qxmpp_omemo_manager.h new file mode 100644 index 0000000..7a6ddf4 --- /dev/null +++ b/qomemo/qxmpp_omemo_manager.h @@ -0,0 +1,36 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#pragma once + +#include "device_service.h" +#include "qomemo.h" + +#include <QXmppClientExtension.h> + +namespace QXmpp::Omemo { + +class Manager : public QXmppClientExtension { + Q_OBJECT; + +public: + Manager(); + ~Manager() override = default; + + bool handleStanza(const QDomElement &stanza) override; + +public slots: + void fetchOwnDevices(); + +signals: + void deviceListReceived(const QString& jid, const DeviceList& list); + +protected: + void setClient(QXmppClient *client) override; + +private: + QScopedPointer<DeviceService> deviceService; +}; + +} // namespace QXmpp::Omemo From 12ffe8e8e6f9edd9bb6cbcc74eedb1ce59007c69 Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Thu, 13 May 2021 00:32:13 +0300 Subject: [PATCH 13/20] ref(omemo): add qomemo/variant --- qomemo/CMakeLists.txt | 8 +- qomemo/bundle.cpp | 126 ++++++++++++++++++++++ qomemo/bundle.h | 41 ++++++++ qomemo/device.cpp | 5 + qomemo/device.h | 24 +++++ qomemo/device_service.cpp | 1 + qomemo/device_service.h | 2 + qomemo/qomemo.cpp | 174 ------------------------------- qomemo/qomemo.h | 43 -------- qomemo/qxmpp_omemo_manager.cpp | 10 +- qomemo/qxmpp_omemo_manager.h | 4 +- qomemo/variant/CMakeLists.txt | 1 + qomemo/variant/conversations.cpp | 78 ++++++++++++++ qomemo/variant/conversations.h | 23 ++++ qomemo/variant/omemo_base.cpp | 5 + qomemo/variant/omemo_base.h | 31 ++++++ qomemo/variant/xep0384.cpp | 5 + qomemo/variant/xep0384.h | 7 ++ 18 files changed, 366 insertions(+), 222 deletions(-) create mode 100644 qomemo/bundle.cpp create mode 100644 qomemo/bundle.h create mode 100644 qomemo/device.cpp create mode 100644 qomemo/device.h create mode 100644 qomemo/variant/CMakeLists.txt create mode 100644 qomemo/variant/conversations.cpp create mode 100644 qomemo/variant/conversations.h create mode 100644 qomemo/variant/omemo_base.cpp create mode 100644 qomemo/variant/omemo_base.h create mode 100644 qomemo/variant/xep0384.cpp create mode 100644 qomemo/variant/xep0384.h diff --git a/qomemo/CMakeLists.txt b/qomemo/CMakeLists.txt index 1512264..95fca3f 100644 --- a/qomemo/CMakeLists.txt +++ b/qomemo/CMakeLists.txt @@ -1,4 +1,8 @@ target_sources(squawk PRIVATE + bundle.cpp + bundle.h + device.cpp + device.h device_key_storage.cpp device_key_storage.h device_service.cpp @@ -13,4 +17,6 @@ target_sources(squawk PRIVATE user_device_list.h qxmpp_omemo_manager.cpp qxmpp_omemo_manager.h - ) \ No newline at end of file + ) + +add_subdirectory(variant) diff --git a/qomemo/bundle.cpp b/qomemo/bundle.cpp new file mode 100644 index 0000000..aec605a --- /dev/null +++ b/qomemo/bundle.cpp @@ -0,0 +1,126 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#include "bundle.h" + +#include <QXmppPubSubIq.h> + +#include <QDebug> + +#include "shared/qxmppfactories.h" + +using namespace QXmpp::Factories; + +QXmppElement QXmpp::Omemo::PreKey::toXml() const { + auto pk = createElement("preKeyPublic"); + pk.setAttribute("preKeyId", QString::number(id)); + // TODO: Base64 + pk.setValue(data); + + return pk; +} + +void QXmpp::Omemo::PreKey::fromXml(const QXmppElement &element) { + if (!elementMatches(element, "preKeyPublic")) + return; + + id = element.attribute("preKeyId").toInt(); + // TODO: Base64 + data = element.value(); +} + +QXmppElement QXmpp::Omemo::Bundle::toXml() const { + auto spkNode = createElement("signedPreKeyPublic"); + spkNode.setAttribute("signedPreKeyId", QString::number(spkId)); + spkNode.setValue(spk); + + auto spksNode = createElement("signedPreKeySignature"); + spksNode.setValue(spks); + + auto ikNode = createElement("identityKey"); + ikNode.setValue(ik); + + auto prekeysNode = createElement("prekeys"); + for (const auto &pk : prekeys) { + prekeysNode.appendChild(pk.toXml()); + } + + auto result = createElement("bundle", "eu.siacs.conversations.axolotl"); + result.appendChild(spkNode); + result.appendChild(spksNode); + result.appendChild(ikNode); + result.appendChild(prekeysNode); + + return result; +} + +QXmppIq QXmpp::Omemo::Bundle::toIq(int deviceId) const { + QXmppIq iq{}; + + iq.setType(QXmppIq::Set); + + auto item = createElement("item"); + item.appendChild(toXml()); + + auto publish = createElement("publish"); + publish.setAttribute("node", QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId)); + publish.appendChild(item); + + auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); + pubSub.appendChild(publish); + pubSub.appendChild(createOpenPublishOptions()); + + iq.extensions().push_back(pubSub); + + return iq; +} + +void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) { + if (!elementMatches(element, "bundle", "eu.siacs.conversations.axolotl")) + return; + + auto spkNode = element.firstChildElement("spk"); + if (spkNode.isNull()) { + qWarning() << "'bundle': missing 'spk'"; + return; + } + spk = spkNode.value(); + spkId = spkNode.attribute("id").toInt(); + + auto spksNode = element.firstChildElement("spks"); + if (spksNode.isNull()) { + qWarning() << "'bundle': missing 'spks'"; + return; + } + spks = spksNode.value(); + + auto ikNode = element.firstChildElement("ik"); + if (ikNode.isNull()) { + qWarning() << "'bundle': missing 'ik'"; + return; + } + ik = ikNode.value(); + + auto prekeysNode = element.firstChildElement("prekeys"); + auto pkNode = prekeysNode.firstChildElement("pk"); + + prekeys.clear(); + while (!pkNode.isNull()) { + PreKey pk{}; + pk.fromXml(pkNode); + prekeys.push_back(pk); + } +} + +QXmppPubSubIq QXmpp::Omemo::Bundle::fetchDeviceBundleIq(int deviceId) { + QXmppPubSubIq iq{}; + iq.setType(QXmppIq::Get); + iq.setQueryNode(QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId)); + + QXmppPubSubItem item{}; + item.setId(QString::number(deviceId)); + iq.setItems({item}); + + return iq; +} \ No newline at end of file diff --git a/qomemo/bundle.h b/qomemo/bundle.h new file mode 100644 index 0000000..d29bf02 --- /dev/null +++ b/qomemo/bundle.h @@ -0,0 +1,41 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#pragma once + +#include <QList> +#include <QString> + +class QXmppPubSubIq; +class QXmppElement; +class QXmppIq; + +namespace QXmpp::Omemo { + +class PreKey { +public: + [[nodiscard]] QXmppElement toXml() const; + /// Expects a <pk> + void fromXml(const QXmppElement &element); + + int id; + QString data; +}; + +class Bundle { +public: + [[nodiscard]] static QXmppPubSubIq fetchDeviceBundleIq(int deviceId); + + [[nodiscard]] QXmppElement toXml() const; + [[nodiscard]] QXmppIq toIq(int deviceId) const; + void fromXml(const QXmppElement &element); + + QString spk; + int spkId; + QString spks; + QString ik; + QList<PreKey> prekeys; +}; + +} diff --git a/qomemo/device.cpp b/qomemo/device.cpp new file mode 100644 index 0000000..edfee1c --- /dev/null +++ b/qomemo/device.cpp @@ -0,0 +1,5 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "device.h" diff --git a/qomemo/device.h b/qomemo/device.h new file mode 100644 index 0000000..4af6ada --- /dev/null +++ b/qomemo/device.h @@ -0,0 +1,24 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include <QList> + +class QXmppElement; +class QXmppIq; + +namespace QXmpp::Omemo { + +class Device { +public: + int id; +}; + +class DeviceList { +public: + QList<Device> devices; +}; + +} // namespace QXmpp::Omemo diff --git a/qomemo/device_service.cpp b/qomemo/device_service.cpp index 5e9a460..fcc6b72 100644 --- a/qomemo/device_service.cpp +++ b/qomemo/device_service.cpp @@ -3,6 +3,7 @@ */ #include "device_service.h" +#include "device.h" QXmpp::Omemo::DeviceService::DeviceService(QObject *parent) : QObject(parent) {} diff --git a/qomemo/device_service.h b/qomemo/device_service.h index 9d87efb..774b7f4 100644 --- a/qomemo/device_service.h +++ b/qomemo/device_service.h @@ -11,6 +11,8 @@ namespace QXmpp::Omemo { +class DeviceList; + class DeviceService : public QObject { Q_OBJECT diff --git a/qomemo/qomemo.cpp b/qomemo/qomemo.cpp index ab863f2..d078f37 100644 --- a/qomemo/qomemo.cpp +++ b/qomemo/qomemo.cpp @@ -12,180 +12,6 @@ using namespace QXmpp::Factories; -QXmppElement QXmpp::Omemo::DeviceList::toXml() const { - auto element = createElement("list", "eu.siacs.conversations.axolotl"); - - for (const auto &d : devices) { - element.appendChild(d.toXml()); - } - - return element; -} - -QXmppIq QXmpp::Omemo::DeviceList::toIq() const { - QXmppIq iq{}; - - iq.setType(QXmppIq::Set); - - auto item = createElement("item"); - item.appendChild(toXml()); - - auto publish = createElement("publish"); - publish.setAttribute("node", "eu.siacs.conversations.axolotl.devicelist"); - publish.appendChild(item); - - auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); - pubSub.appendChild(publish); - pubSub.appendChild(createOpenPublishOptions()); - - iq.extensions().push_back(pubSub); - - return iq; -} - -void QXmpp::Omemo::DeviceList::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "list", "eu.siacs.conversations.axolotl")) - return; - - devices.clear(); - - auto deviceElement = element.firstChildElement("device"); - while (!deviceElement.isNull()) { - Device device{}; - device.fromXml(deviceElement); - devices.push_back(device); - - deviceElement = deviceElement.nextSiblingElement("device"); - } -} - -QXmppElement QXmpp::Omemo::Device::toXml() const { - auto result = createElement("device"); - result.setAttribute("id", QString::number(id)); - - return result; -} - -void QXmpp::Omemo::Device::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "device")) - return; - - id = element.attribute("id").toInt(); -} - -QXmppElement QXmpp::Omemo::PreKey::toXml() const { - auto pk = createElement("preKeyPublic"); - pk.setAttribute("preKeyId", QString::number(id)); - // TODO: Base64 - pk.setValue(data); - - return pk; -} - -void QXmpp::Omemo::PreKey::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "preKeyPublic")) - return; - - id = element.attribute("preKeyId").toInt(); - // TODO: Base64 - data = element.value(); -} - -QXmppElement QXmpp::Omemo::Bundle::toXml() const { - auto spkNode = createElement("signedPreKeyPublic"); - spkNode.setAttribute("signedPreKeyId", QString::number(spkId)); - spkNode.setValue(spk); - - auto spksNode = createElement("signedPreKeySignature"); - spksNode.setValue(spks); - - auto ikNode = createElement("identityKey"); - ikNode.setValue(ik); - - auto prekeysNode = createElement("prekeys"); - for (const auto &pk : prekeys) { - prekeysNode.appendChild(pk.toXml()); - } - - auto result = createElement("bundle", "eu.siacs.conversations.axolotl"); - result.appendChild(spkNode); - result.appendChild(spksNode); - result.appendChild(ikNode); - result.appendChild(prekeysNode); - - return result; -} - -QXmppIq QXmpp::Omemo::Bundle::toIq(int deviceId) const { - QXmppIq iq{}; - - iq.setType(QXmppIq::Set); - - auto item = createElement("item"); - item.appendChild(toXml()); - - auto publish = createElement("publish"); - publish.setAttribute("node", QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId)); - publish.appendChild(item); - - auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); - pubSub.appendChild(publish); - pubSub.appendChild(createOpenPublishOptions()); - - iq.extensions().push_back(pubSub); - - return iq; -} - -void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "bundle", "eu.siacs.conversations.axolotl")) - return; - - auto spkNode = element.firstChildElement("spk"); - if (spkNode.isNull()) { - qWarning() << "'bundle': missing 'spk'"; - return; - } - spk = spkNode.value(); - spkId = spkNode.attribute("id").toInt(); - - auto spksNode = element.firstChildElement("spks"); - if (spksNode.isNull()) { - qWarning() << "'bundle': missing 'spks'"; - return; - } - spks = spksNode.value(); - - auto ikNode = element.firstChildElement("ik"); - if (ikNode.isNull()) { - qWarning() << "'bundle': missing 'ik'"; - return; - } - ik = ikNode.value(); - - auto prekeysNode = element.firstChildElement("prekeys"); - auto pkNode = prekeysNode.firstChildElement("pk"); - - prekeys.clear(); - while (!pkNode.isNull()) { - PreKey pk{}; - pk.fromXml(pkNode); - prekeys.push_back(pk); - } -} - -QXmppPubSubIq QXmpp::Omemo::Bundle::fetchDeviceBundleIq(int deviceId) { - QXmppPubSubIq iq{}; - iq.setType(QXmppIq::Get); - iq.setQueryNode(QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId)); - - QXmppPubSubItem item{}; - item.setId(QString::number(deviceId)); - iq.setItems({item}); - - return iq; -} - QXmppElement QXmpp::Omemo::EncryptedMessage::header() const { auto result = createElement("header"); result.setAttribute("sid", QString::number(fromDeviceId)); diff --git a/qomemo/qomemo.h b/qomemo/qomemo.h index 346cc90..b79ef1b 100644 --- a/qomemo/qomemo.h +++ b/qomemo/qomemo.h @@ -11,49 +11,6 @@ namespace QXmpp::Omemo { -class Device { -public: - [[nodiscard]] QXmppElement toXml() const; - void fromXml(const QXmppElement &element); - - int id; -}; - -class DeviceList { -public: - [[nodiscard]] QXmppElement toXml() const; - [[nodiscard]] QXmppIq toIq() const; - /// Expects a urn:xmpp:omemo:1:devices node - void fromXml(const QXmppElement &element); - - QList<Device> devices; -}; - -class PreKey { -public: - [[nodiscard]] QXmppElement toXml() const; - /// Expects a <pk> - void fromXml(const QXmppElement &element); - - int id; - QString data; -}; - -class Bundle { -public: - [[nodiscard]] static QXmppPubSubIq fetchDeviceBundleIq(int deviceId); - - [[nodiscard]] QXmppElement toXml() const; - [[nodiscard]] QXmppIq toIq(int deviceId) const; - void fromXml(const QXmppElement &element); - - QString spk; - int spkId; - QString spks; - QString ik; - QList<PreKey> prekeys; -}; - class MessageKey { public: [[nodiscard]] QXmppElement toXml() const; diff --git a/qomemo/qxmpp_omemo_manager.cpp b/qomemo/qxmpp_omemo_manager.cpp index 26598f7..f29ec98 100644 --- a/qomemo/qxmpp_omemo_manager.cpp +++ b/qomemo/qxmpp_omemo_manager.cpp @@ -4,13 +4,18 @@ #include "qxmpp_omemo_manager.h" +#include "device.h" +#include "variant/conversations.h" + #include <QDomElement> #include <QXmppClient.h> #include <QXmppPubSubIq.h> #include <iostream> -QXmpp::Omemo::Manager::Manager() : deviceService(new QXmpp::Omemo::DeviceService(this)) { +using namespace QXmpp::Omemo; + +Manager::Manager() : deviceService(new DeviceService(this)), omemoVariant(new Variant::Conversations) { connect(this, &Manager::deviceListReceived, deviceService.get(), &DeviceService::onDeviceListReceived); } @@ -31,8 +36,7 @@ bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) { if (!item.isNull()) { auto list = item.firstChildElement("list"); if (!list.isNull()) { - DeviceList deviceList{}; - deviceList.fromXml(list); + DeviceList deviceList = omemoVariant->deviceListFromXml(list); emit deviceListReceived(stanza.attribute("from"), deviceList); return true; diff --git a/qomemo/qxmpp_omemo_manager.h b/qomemo/qxmpp_omemo_manager.h index 7a6ddf4..377c77d 100644 --- a/qomemo/qxmpp_omemo_manager.h +++ b/qomemo/qxmpp_omemo_manager.h @@ -6,6 +6,7 @@ #include "device_service.h" #include "qomemo.h" +#include "variant/omemo_base.h" #include <QXmppClientExtension.h> @@ -24,13 +25,14 @@ public slots: void fetchOwnDevices(); signals: - void deviceListReceived(const QString& jid, const DeviceList& list); + void deviceListReceived(const QString &jid, const DeviceList &list); protected: void setClient(QXmppClient *client) override; private: QScopedPointer<DeviceService> deviceService; + QScopedPointer<Variant::Base> omemoVariant; }; } // namespace QXmpp::Omemo diff --git a/qomemo/variant/CMakeLists.txt b/qomemo/variant/CMakeLists.txt new file mode 100644 index 0000000..a2b211a --- /dev/null +++ b/qomemo/variant/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(squawk PRIVATE xep0384.cpp xep0384.h conversations.cpp conversations.h omemo_base.cpp omemo_base.h) diff --git a/qomemo/variant/conversations.cpp b/qomemo/variant/conversations.cpp new file mode 100644 index 0000000..b2f85c4 --- /dev/null +++ b/qomemo/variant/conversations.cpp @@ -0,0 +1,78 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "conversations.h" + +#include "qomemo/device.h" +#include "shared/qxmppfactories.h" + +#include <QXmppIq.h> + +using namespace QXmpp::Omemo; +using namespace QXmpp::Factories; + +QXmppElement Variant::Conversations::deviceToXml(const Device &device) { + auto result = createElement("device"); + result.setAttribute("id", QString::number(device.id)); + + return result; +} + +Device Variant::Conversations::deviceFromXml(const QXmppElement &xml) { + Device result{}; + + if (!elementMatches(xml, "device")) + return result; + + result.id = xml.attribute("id").toInt(); + + return result; +} + +QXmppElement +Variant::Conversations::deviceListToXml(const DeviceList &deviceList) { + auto element = createElement("list", "eu.siacs.conversations.axolotl"); + + for (const auto &device : deviceList.devices) { + element.appendChild(deviceToXml(device)); + } + + return element; +} + +DeviceList Variant::Conversations::deviceListFromXml(const QXmppElement &xml) { + DeviceList result{}; + + if (!elementMatches(xml, "list", "eu.siacs.conversations.axolotl")) + return result; + + auto deviceElement = xml.firstChildElement("device"); + while (!deviceElement.isNull()) { + result.devices.push_back(deviceFromXml(deviceElement)); + deviceElement = deviceElement.nextSiblingElement("device"); + } + + return result; +} + +QXmppIq Variant::Conversations::deviceListSetIq(const DeviceList &deviceList) { + QXmppIq iq{}; + + iq.setType(QXmppIq::Set); + + auto item = createElement("item"); + item.appendChild(deviceListToXml(deviceList)); + + auto publish = createElement("publish"); + publish.setAttribute("node", "eu.siacs.conversations.axolotl.devicelist"); + publish.appendChild(item); + + auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); + pubSub.appendChild(publish); + pubSub.appendChild(createOpenPublishOptions()); + + iq.extensions().push_back(pubSub); + + return iq; +} diff --git a/qomemo/variant/conversations.h b/qomemo/variant/conversations.h new file mode 100644 index 0000000..917dbcd --- /dev/null +++ b/qomemo/variant/conversations.h @@ -0,0 +1,23 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include "omemo_base.h" + +namespace QXmpp::Omemo::Variant { + +class Conversations : public Base { +public: + ~Conversations() override = default; + + QXmppElement deviceToXml(const Device &device) override; + Device deviceFromXml(const QXmppElement &xml) override; + + QXmppElement deviceListToXml(const DeviceList &deviceList) override; + DeviceList deviceListFromXml(const QXmppElement &xml) override; + QXmppIq deviceListSetIq(const DeviceList &deviceList) override; +}; + +} // namespace QXmpp::Omemo::Variant diff --git a/qomemo/variant/omemo_base.cpp b/qomemo/variant/omemo_base.cpp new file mode 100644 index 0000000..a3e8493 --- /dev/null +++ b/qomemo/variant/omemo_base.cpp @@ -0,0 +1,5 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "omemo_base.h" diff --git a/qomemo/variant/omemo_base.h b/qomemo/variant/omemo_base.h new file mode 100644 index 0000000..f383a59 --- /dev/null +++ b/qomemo/variant/omemo_base.h @@ -0,0 +1,31 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +class QXmppElement; +class QXmppIq; + +namespace QXmpp::Omemo { + +class Device; +class DeviceList; + +namespace Variant { + +class Base { +public: + virtual ~Base() = default; + + virtual QXmppElement deviceToXml(const Device& device) = 0; + virtual Device deviceFromXml(const QXmppElement& xml) = 0; + + virtual QXmppElement deviceListToXml(const DeviceList& deviceList) = 0; + virtual DeviceList deviceListFromXml(const QXmppElement& xml) = 0; + virtual QXmppIq deviceListSetIq(const DeviceList& deviceList) = 0; +}; + +} // namespace Variant + +} // namespace QXmpp::Omemo diff --git a/qomemo/variant/xep0384.cpp b/qomemo/variant/xep0384.cpp new file mode 100644 index 0000000..8ec2597 --- /dev/null +++ b/qomemo/variant/xep0384.cpp @@ -0,0 +1,5 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "xep0384.h" diff --git a/qomemo/variant/xep0384.h b/qomemo/variant/xep0384.h new file mode 100644 index 0000000..9298b24 --- /dev/null +++ b/qomemo/variant/xep0384.h @@ -0,0 +1,7 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +namespace QXmpp::Omemo {} From 2654e386657fcd5d6b1e568587598c60c38e8741 Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Thu, 13 May 2021 17:50:29 +0300 Subject: [PATCH 14/20] feat(omemo): add signal protocol wrappers --- qomemo/CMakeLists.txt | 5 +- qomemo/signal.cpp | 5 - qomemo/signal.h | 20 -- qomemo/signal/CMakeLists.txt | 7 + qomemo/signal/context.cpp | 9 + qomemo/signal/context.h | 23 +++ qomemo/signal/crypto/CMakeLists.txt | 10 + qomemo/signal/crypto/aes_openssl.cpp | 187 ++++++++++++++++++ qomemo/signal/crypto/aes_openssl.h | 17 ++ qomemo/signal/crypto/crypto.cpp | 40 ++++ qomemo/signal/crypto/crypto.h | 13 ++ qomemo/signal/crypto/hmac_sha256_openssl.cpp | 71 +++++++ qomemo/signal/crypto/hmac_sha256_openssl.h | 16 ++ .../signal/crypto/sha512_digest_openssl.cpp | 67 +++++++ qomemo/signal/crypto/sha512_digest_openssl.h | 16 ++ qomemo/signal/stores/CMakeLists.txt | 14 ++ qomemo/signal/stores/identity_key_store.cpp | 47 +++++ qomemo/signal/stores/identity_key_store.h | 21 ++ qomemo/signal/stores/pre_key_store.cpp | 43 ++++ qomemo/signal/stores/pre_key_store.h | 21 ++ qomemo/signal/stores/sender_key_store.cpp | 36 ++++ qomemo/signal/stores/sender_key_store.h | 21 ++ qomemo/signal/stores/session_store.cpp | 63 ++++++ qomemo/signal/stores/session_store.h | 24 +++ qomemo/signal/stores/signed_pre_key_store.cpp | 48 +++++ qomemo/signal/stores/signed_pre_key_store.h | 20 ++ qomemo/signal/stores/store_context.cpp | 13 ++ qomemo/signal/stores/store_context.h | 24 +++ 28 files changed, 874 insertions(+), 27 deletions(-) delete mode 100644 qomemo/signal.cpp delete mode 100644 qomemo/signal.h create mode 100644 qomemo/signal/CMakeLists.txt create mode 100644 qomemo/signal/context.cpp create mode 100644 qomemo/signal/context.h create mode 100644 qomemo/signal/crypto/CMakeLists.txt create mode 100644 qomemo/signal/crypto/aes_openssl.cpp create mode 100644 qomemo/signal/crypto/aes_openssl.h create mode 100644 qomemo/signal/crypto/crypto.cpp create mode 100644 qomemo/signal/crypto/crypto.h create mode 100644 qomemo/signal/crypto/hmac_sha256_openssl.cpp create mode 100644 qomemo/signal/crypto/hmac_sha256_openssl.h create mode 100644 qomemo/signal/crypto/sha512_digest_openssl.cpp create mode 100644 qomemo/signal/crypto/sha512_digest_openssl.h create mode 100644 qomemo/signal/stores/CMakeLists.txt create mode 100644 qomemo/signal/stores/identity_key_store.cpp create mode 100644 qomemo/signal/stores/identity_key_store.h create mode 100644 qomemo/signal/stores/pre_key_store.cpp create mode 100644 qomemo/signal/stores/pre_key_store.h create mode 100644 qomemo/signal/stores/sender_key_store.cpp create mode 100644 qomemo/signal/stores/sender_key_store.h create mode 100644 qomemo/signal/stores/session_store.cpp create mode 100644 qomemo/signal/stores/session_store.h create mode 100644 qomemo/signal/stores/signed_pre_key_store.cpp create mode 100644 qomemo/signal/stores/signed_pre_key_store.h create mode 100644 qomemo/signal/stores/store_context.cpp create mode 100644 qomemo/signal/stores/store_context.h diff --git a/qomemo/CMakeLists.txt b/qomemo/CMakeLists.txt index 95fca3f..7391404 100644 --- a/qomemo/CMakeLists.txt +++ b/qomemo/CMakeLists.txt @@ -1,14 +1,14 @@ target_sources(squawk PRIVATE bundle.cpp bundle.h + database.cpp + database.h device.cpp device.h device_key_storage.cpp device_key_storage.h device_service.cpp device_service.h - signal.h - signal.cpp qomemo.cpp qomemo.h sce.cpp @@ -19,4 +19,5 @@ target_sources(squawk PRIVATE qxmpp_omemo_manager.h ) +add_subdirectory(signal) add_subdirectory(variant) diff --git a/qomemo/signal.cpp b/qomemo/signal.cpp deleted file mode 100644 index 0e5257b..0000000 --- a/qomemo/signal.cpp +++ /dev/null @@ -1,5 +0,0 @@ -/* - * Created by victoria on 2021-05-11. - */ - -#include "signal.h" diff --git a/qomemo/signal.h b/qomemo/signal.h deleted file mode 100644 index 4aa5701..0000000 --- a/qomemo/signal.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Created by victoria on 2021-05-11. - */ - -#pragma once - -#include <signal/signal_protocol.h> - -namespace Signal -{ - -class Context {}; -class RatchetIdentityPair {}; -class SessionSignedPreKey {}; -class ProtocolKeyHelper {}; -class ProtocolStoreContext {}; -class SessionBuilder {}; -class SessionCipher {}; - -} diff --git a/qomemo/signal/CMakeLists.txt b/qomemo/signal/CMakeLists.txt new file mode 100644 index 0000000..60cfdd8 --- /dev/null +++ b/qomemo/signal/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(squawk PRIVATE + context.cpp + context.h + ) + +add_subdirectory(crypto) +add_subdirectory(stores) \ No newline at end of file diff --git a/qomemo/signal/context.cpp b/qomemo/signal/context.cpp new file mode 100644 index 0000000..bc2322e --- /dev/null +++ b/qomemo/signal/context.cpp @@ -0,0 +1,9 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "context.h" + +Signal::Context::Context() {} + +Signal::Context::~Context() {} diff --git a/qomemo/signal/context.h b/qomemo/signal/context.h new file mode 100644 index 0000000..c398105 --- /dev/null +++ b/qomemo/signal/context.h @@ -0,0 +1,23 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include <signal/signal_protocol.h> + +namespace Signal { + + class Context { + public: + Context(); + ~Context(); + Context(const Context &) = delete; + Context(Context &&) = delete; + Context &operator=(const Context &) = delete; + + private: + signal_context *ctx{nullptr}; + }; + +} // namespace Signal diff --git a/qomemo/signal/crypto/CMakeLists.txt b/qomemo/signal/crypto/CMakeLists.txt new file mode 100644 index 0000000..6359510 --- /dev/null +++ b/qomemo/signal/crypto/CMakeLists.txt @@ -0,0 +1,10 @@ +target_sources(squawk PRIVATE + aes_openssl.cpp + aes_openssl.h + crypto.cpp + crypto.h + hmac_sha256_openssl.cpp + hmac_sha256_openssl.h + sha512_digest_openssl.cpp + sha512_digest_openssl.h + ) diff --git a/qomemo/signal/crypto/aes_openssl.cpp b/qomemo/signal/crypto/aes_openssl.cpp new file mode 100644 index 0000000..17f2df8 --- /dev/null +++ b/qomemo/signal/crypto/aes_openssl.cpp @@ -0,0 +1,187 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "aes_openssl.h" +#include <memory> + +extern "C" { +#include <openssl/evp.h> +#include <openssl/opensslv.h> +} + +using namespace Signal::Crypto; + +class EVPCipherCtxWrapper { +public: + EVPCipherCtxWrapper() { +#if OPENSSL_VERSION_NUMBER >= 0x1010000fL + ctx = EVP_CIPHER_CTX_new(); +#else + ctx = new EVP_CIPHER_CTX; + EVP_CIPHER_CTX_init(ctx); +#endif + } + + ~EVPCipherCtxWrapper() { + if (good()) { +#if OPENSSL_VERSION_NUMBER >= 0x1010000fL + EVP_CIPHER_CTX_free(ctx); +#else + EVP_CIPHER_CTX_cleanup(ctx); + delete ctx; +#endif + } + } + + EVPCipherCtxWrapper(const EVPCipherCtxWrapper &) = delete; + EVPCipherCtxWrapper(EVPCipherCtxWrapper &&) = delete; + EVPCipherCtxWrapper &operator=(const EVPCipherCtxWrapper &) = delete; + + [[nodiscard]] bool good() const { return ctx != nullptr; } + + [[nodiscard]] EVP_CIPHER_CTX *operator*() const { return ctx; } + +private: + EVP_CIPHER_CTX *ctx{nullptr}; +}; + +static const EVP_CIPHER *aes_cipher(int cipher, size_t key_len) { + if (cipher == SG_CIPHER_AES_CBC_PKCS5) { + if (key_len == 16) { + return EVP_aes_128_cbc(); + } else if (key_len == 24) { + return EVP_aes_192_cbc(); + } else if (key_len == 32) { + return EVP_aes_256_cbc(); + } + } else if (cipher == SG_CIPHER_AES_CTR_NOPADDING) { + if (key_len == 16) { + return EVP_aes_128_ctr(); + } else if (key_len == 24) { + return EVP_aes_192_ctr(); + } else if (key_len == 32) { + return EVP_aes_256_ctr(); + } + } + return nullptr; +} + +int Aes::encrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, + size_t iv_len, const uint8_t *plaintext, size_t plaintext_len, void *) { + const EVP_CIPHER *evp_cipher = aes_cipher(cipher, key_len); + if (!evp_cipher) { + fprintf(stderr, "invalid AES mode or key size: %zu\n", key_len); + return SG_ERR_UNKNOWN; + } + + if (iv_len != 16) { + fprintf(stderr, "invalid AES IV size: %zu\n", iv_len); + return SG_ERR_UNKNOWN; + } + + if (plaintext_len > INT_MAX - EVP_CIPHER_block_size(evp_cipher)) { + fprintf(stderr, "invalid plaintext length: %zu\n", plaintext_len); + return SG_ERR_UNKNOWN; + } + + EVPCipherCtxWrapper ctx{}; + if (!ctx.good()) { + fprintf(stderr, "could not create context\n"); + return SG_ERR_UNKNOWN; + } + + auto result = EVP_EncryptInit_ex(*ctx, evp_cipher, nullptr, key, iv); + if (!result) { + fprintf(stderr, "cannot initialize cipher\n"); + return SG_ERR_UNKNOWN; + } + + if (cipher == SG_CIPHER_AES_CTR_NOPADDING) { + result = EVP_CIPHER_CTX_set_padding(*ctx, 0); + if (!result) { + fprintf(stderr, "cannot set padding\n"); + return SG_ERR_UNKNOWN; + } + } + + auto out_buf = std::make_unique<uint8_t>(plaintext_len + EVP_CIPHER_block_size(evp_cipher)); + + int out_len = 0; + result = EVP_EncryptUpdate(*ctx, out_buf.get(), &out_len, plaintext, + plaintext_len); + if (!result) { + fprintf(stderr, "cannot encrypt plaintext\n"); + return SG_ERR_UNKNOWN; + } + + int final_len = 0; + result = EVP_EncryptFinal_ex(*ctx, out_buf.get() + out_len, &final_len); + if (!result) { + fprintf(stderr, "cannot finish encrypting plaintext\n"); + return SG_ERR_UNKNOWN; + } + + *output = signal_buffer_create(out_buf.get(), out_len + final_len); + + return result; +} + +int Aes::decrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, + size_t iv_len, const uint8_t *ciphertext, size_t ciphertext_len, void *) { + const EVP_CIPHER *evp_cipher = aes_cipher(cipher, key_len); + if (!evp_cipher) { + fprintf(stderr, "invalid AES mode or key size: %zu\n", key_len); + return SG_ERR_INVAL; + } + + if (iv_len != 16) { + fprintf(stderr, "invalid AES IV size: %zu\n", iv_len); + return SG_ERR_INVAL; + } + + if (ciphertext_len > INT_MAX - EVP_CIPHER_block_size(evp_cipher)) { + fprintf(stderr, "invalid ciphertext length: %zu\n", ciphertext_len); + return SG_ERR_UNKNOWN; + } + + EVPCipherCtxWrapper ctx{}; + if (!ctx.good()) { + fprintf(stderr, "could not create context\n"); + return SG_ERR_UNKNOWN; + } + + auto result = EVP_DecryptInit_ex(*ctx, evp_cipher, nullptr, key, iv); + if (!result) { + fprintf(stderr, "cannot initialize cipher\n"); + return SG_ERR_UNKNOWN; + } + + if (cipher == SG_CIPHER_AES_CTR_NOPADDING) { + result = EVP_CIPHER_CTX_set_padding(*ctx, 0); + if (!result) { + fprintf(stderr, "cannot set padding\n"); + return SG_ERR_UNKNOWN; + } + } + + auto out_buf = std::make_unique<uint8_t>(ciphertext_len + EVP_CIPHER_block_size(evp_cipher)); + + int out_len = 0; + result = EVP_DecryptUpdate(*ctx, out_buf.get(), &out_len, ciphertext, ciphertext_len); + if (!result) { + fprintf(stderr, "cannot decrypt ciphertext\n"); + return SG_ERR_UNKNOWN; + } + + int final_len = 0; + result = EVP_DecryptFinal_ex(*ctx, out_buf.get() + out_len, &final_len); + if (!result) { + fprintf(stderr, "cannot finish decrypting ciphertext\n"); + return SG_ERR_UNKNOWN; + } + + *output = signal_buffer_create(out_buf.get(), out_len + final_len); + + return result; +} \ No newline at end of file diff --git a/qomemo/signal/crypto/aes_openssl.h b/qomemo/signal/crypto/aes_openssl.h new file mode 100644 index 0000000..63f4635 --- /dev/null +++ b/qomemo/signal/crypto/aes_openssl.h @@ -0,0 +1,17 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include <signal/signal_protocol.h> + +namespace Signal::Crypto::Aes { + + int encrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, + size_t iv_len, const uint8_t *plaintext, size_t plaintext_len, void *user_data); + + int decrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, + size_t iv_len, const uint8_t *ciphertext, size_t ciphertext_len, void *user_data); + +} // namespace Signal::Crypto::Aes diff --git a/qomemo/signal/crypto/crypto.cpp b/qomemo/signal/crypto/crypto.cpp new file mode 100644 index 0000000..d6a6058 --- /dev/null +++ b/qomemo/signal/crypto/crypto.cpp @@ -0,0 +1,40 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "crypto.h" + +extern "C" { +#include <openssl/rand.h> +} + +#include "aes_openssl.h" +#include "hmac_sha256_openssl.h" +#include "sha512_digest_openssl.h" + +int random_func(uint8_t *data, size_t len, void *) { + if (RAND_bytes(data, len)) { + return 0; + } else { + return SG_ERR_UNKNOWN; + } +} + +signal_crypto_provider Signal::Crypto::createProvider() { + signal_crypto_provider result{}; + + result.random_func = random_func; + result.hmac_sha256_init_func = HmacSha256::init; + result.hmac_sha256_update_func = HmacSha256::update; + result.hmac_sha256_final_func = HmacSha256::final; + result.hmac_sha256_cleanup_func = HmacSha256::cleanup; + result.sha512_digest_init_func = Sha512::init; + result.sha512_digest_update_func = Sha512::update; + result.sha512_digest_final_func = Sha512::final; + result.sha512_digest_cleanup_func = Sha512::cleanup; + result.encrypt_func = Aes::encrypt; + result.decrypt_func = Aes::decrypt; + result.user_data = nullptr; + + return result; +} diff --git a/qomemo/signal/crypto/crypto.h b/qomemo/signal/crypto/crypto.h new file mode 100644 index 0000000..2fbff31 --- /dev/null +++ b/qomemo/signal/crypto/crypto.h @@ -0,0 +1,13 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include <signal/signal_protocol.h> + +namespace Signal::Crypto { + + signal_crypto_provider createProvider(); + +} diff --git a/qomemo/signal/crypto/hmac_sha256_openssl.cpp b/qomemo/signal/crypto/hmac_sha256_openssl.cpp new file mode 100644 index 0000000..2a8aba5 --- /dev/null +++ b/qomemo/signal/crypto/hmac_sha256_openssl.cpp @@ -0,0 +1,71 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "hmac_sha256_openssl.h" + +extern "C" { +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/sha.h> +} + +using namespace Signal::Crypto; + +int HmacSha256::init(void **hmac_context, const uint8_t *key, size_t key_len, void *) { +#if OPENSSL_VERSION_NUMBER >= 0x1010000fL + HMAC_CTX *ctx = HMAC_CTX_new(); + if (!ctx) { + return SG_ERR_NOMEM; + } +#else + auto ctx = new HMAC_CTX; + HMAC_CTX_init(ctx); +#endif + + *hmac_context = ctx; + + if (HMAC_Init_ex(ctx, key, key_len, EVP_sha256(), nullptr) != 1) { + return SG_ERR_UNKNOWN; + } + + return SG_SUCCESS; +} + +int HmacSha256::update(void *hmac_context, const uint8_t *data, size_t data_len, void *) { + auto ctx = static_cast<HMAC_CTX *>(hmac_context); + int result = HMAC_Update(ctx, data, data_len); + + return (result == 1) ? SG_SUCCESS : SG_ERR_UNKNOWN; +} + +int HmacSha256::final(void *hmac_context, signal_buffer **output, void *) { + auto ctx = static_cast<HMAC_CTX *>(hmac_context); + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int len = 0; + + if (HMAC_Final(ctx, md, &len) != 1) { + return SG_ERR_UNKNOWN; + } + + signal_buffer *output_buffer = signal_buffer_create(md, len); + if (!output_buffer) { + return SG_ERR_NOMEM; + } + + *output = output_buffer; + + return SG_SUCCESS; +} + +void HmacSha256::cleanup(void *hmac_context, void *) { + if (hmac_context) { + auto ctx = static_cast<HMAC_CTX *>(hmac_context); +#if OPENSSL_VERSION_NUMBER >= 0x1010000fL + HMAC_CTX_free(ctx); +#else + HMAC_CTX_cleanup(ctx); + delete ctx; +#endif + } +} \ No newline at end of file diff --git a/qomemo/signal/crypto/hmac_sha256_openssl.h b/qomemo/signal/crypto/hmac_sha256_openssl.h new file mode 100644 index 0000000..03fe8e0 --- /dev/null +++ b/qomemo/signal/crypto/hmac_sha256_openssl.h @@ -0,0 +1,16 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include <signal/signal_protocol.h> + +namespace Signal::Crypto::HmacSha256 { + + int init(void **hmac_context, const uint8_t *key, size_t key_len, void *); + int update(void *hmac_context, const uint8_t *data, size_t data_len, void *); + int final(void *hmac_context, signal_buffer **output, void *); + void cleanup(void *hmac_context, void *); + +} // namespace Signal::Crypto::HmacSha256 diff --git a/qomemo/signal/crypto/sha512_digest_openssl.cpp b/qomemo/signal/crypto/sha512_digest_openssl.cpp new file mode 100644 index 0000000..360f11e --- /dev/null +++ b/qomemo/signal/crypto/sha512_digest_openssl.cpp @@ -0,0 +1,67 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "sha512_digest_openssl.h" + +extern "C" { +#include <openssl/evp.h> +#include <openssl/rand.h> +#include <openssl/sha.h> +} + +using namespace Signal::Crypto; + +int Sha512::init(void **digest_context, void *) { + auto ctx = EVP_MD_CTX_create(); + if (!ctx) { + return SG_ERR_NOMEM; + } + + auto result = EVP_DigestInit_ex(ctx, EVP_sha512(), nullptr); + + if (result == 1) { + *digest_context = ctx; + return SG_SUCCESS; + } + + EVP_MD_CTX_destroy(ctx); + return SG_ERR_UNKNOWN; +} + +int Sha512::update(void *digest_context, const uint8_t *data, size_t data_len, void *) { + auto ctx = static_cast<EVP_MD_CTX *>(digest_context); + auto result = EVP_DigestUpdate(ctx, data, data_len); + + return (result == 1) ? SG_SUCCESS : SG_ERR_UNKNOWN; +} + +int Sha512::final(void *digest_context, signal_buffer **output, void *) { + auto ctx = static_cast<EVP_MD_CTX *>(digest_context); + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int len = 0; + + auto result = EVP_DigestFinal_ex(ctx, md, &len); + if (result != 1) { + return SG_ERR_UNKNOWN; + } + + result = EVP_DigestInit_ex(ctx, EVP_sha512(), nullptr); + if (result != 1) { + return SG_ERR_UNKNOWN; + } + + signal_buffer *output_buffer = signal_buffer_create(md, len); + if (!output_buffer) { + return SG_ERR_NOMEM; + } + + *output = output_buffer; + + return SG_SUCCESS; +} + +void Sha512::cleanup(void *digest_context, void *) { + auto ctx = static_cast<EVP_MD_CTX *>(digest_context); + EVP_MD_CTX_destroy(ctx); +} \ No newline at end of file diff --git a/qomemo/signal/crypto/sha512_digest_openssl.h b/qomemo/signal/crypto/sha512_digest_openssl.h new file mode 100644 index 0000000..8b263a2 --- /dev/null +++ b/qomemo/signal/crypto/sha512_digest_openssl.h @@ -0,0 +1,16 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include <signal/signal_protocol.h> + +namespace Signal::Crypto::Sha512 { + + int init(void **digest_context, void *); + int update(void *digest_context, const uint8_t *data, size_t data_len, void *); + int final(void *digest_context, signal_buffer **output, void *); + void cleanup(void *digest_context, void *); + +} // namespace Signal::Crypto::Sha512 diff --git a/qomemo/signal/stores/CMakeLists.txt b/qomemo/signal/stores/CMakeLists.txt new file mode 100644 index 0000000..971b87b --- /dev/null +++ b/qomemo/signal/stores/CMakeLists.txt @@ -0,0 +1,14 @@ +target_sources(squawk PRIVATE + identity_key_store.cpp + identity_key_store.h + pre_key_store.cpp + pre_key_store.h + sender_key_store.cpp + sender_key_store.h + session_store.cpp + session_store.h + signed_pre_key_store.cpp + signed_pre_key_store.h + store_context.cpp + store_context.h + ) \ No newline at end of file diff --git a/qomemo/signal/stores/identity_key_store.cpp b/qomemo/signal/stores/identity_key_store.cpp new file mode 100644 index 0000000..a8b28df --- /dev/null +++ b/qomemo/signal/stores/identity_key_store.cpp @@ -0,0 +1,47 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "identity_key_store.h" + +void Signal::Store::IdentityKeyStore::boundToContext( + signal_protocol_store_context *ctx) { + signal_protocol_identity_key_store store{}; + + store.user_data = nullptr; + store.destroy_func = nullptr; + + store.get_identity_key_pair = [](signal_buffer **public_data, signal_buffer **private_data, void *ptr) { + return static_cast<IdentityKeyStore *>(ptr)->getIdentityKeyPair(public_data, private_data); + }; + store.get_local_registration_id = [](void *ptr, uint32_t *registrationId) { + return static_cast<IdentityKeyStore *>(ptr)->getLocalRegistrationId(registrationId); + }; + store.is_trusted_identity = [](const signal_protocol_address *address, uint8_t *key_data, size_t key_len, + void *ptr) { + return static_cast<IdentityKeyStore *>(ptr)->isTrustedIdentity(address, key_data, key_len); + }; + store.save_identity = [](const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *ptr) { + return static_cast<IdentityKeyStore *>(ptr)->saveIdentity(address, key_data, key_len); + }; + + signal_protocol_store_context_set_identity_key_store(ctx, &store); +} + +int Signal::Store::IdentityKeyStore::getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data) { + return 0; +} + +int Signal::Store::IdentityKeyStore::getLocalRegistrationId(uint32_t *registration_id) { + return 0; +} + +int Signal::Store::IdentityKeyStore::saveIdentity(const signal_protocol_address *address, uint8_t *key_data, + size_t key_len) { + return 0; +} + +int Signal::Store::IdentityKeyStore::isTrustedIdentity(const signal_protocol_address *address, uint8_t *key_data, + size_t key_len) { + return 0; +} diff --git a/qomemo/signal/stores/identity_key_store.h b/qomemo/signal/stores/identity_key_store.h new file mode 100644 index 0000000..4179430 --- /dev/null +++ b/qomemo/signal/stores/identity_key_store.h @@ -0,0 +1,21 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include <signal/signal_protocol.h> + +namespace Signal::Store { + + class IdentityKeyStore { + public: + static void boundToContext(signal_protocol_store_context *ctx); + + int getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data); + int getLocalRegistrationId(uint32_t *registration_id); + int saveIdentity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len); + int isTrustedIdentity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len); + }; + +} // namespace Signal::Store diff --git a/qomemo/signal/stores/pre_key_store.cpp b/qomemo/signal/stores/pre_key_store.cpp new file mode 100644 index 0000000..fe4cd60 --- /dev/null +++ b/qomemo/signal/stores/pre_key_store.cpp @@ -0,0 +1,43 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "pre_key_store.h" + +void Signal::Store::PreKeyStore::boundToContext( + signal_protocol_store_context *ctx) { + signal_protocol_pre_key_store store{}; + + store.destroy_func = nullptr; + store.user_data = nullptr; + + store.contains_pre_key = [](uint32_t id, void *ptr) { + return static_cast<PreKeyStore *>(ptr)->containsPreKey(id); + }; + store.load_pre_key = [](signal_buffer **record, uint32_t id, void *ptr) { + return static_cast<PreKeyStore *>(ptr)->loadPreKey(record, id); + }; + store.remove_pre_key = [](uint32_t id, void *ptr) { + return static_cast<PreKeyStore *>(ptr)->removePreKey(id); + }; + store.store_pre_key = [](uint32_t id, uint8_t *record, size_t size, + void *ptr) { + return static_cast<PreKeyStore *>(ptr)->storePreKey(id, record, size); + }; + + signal_protocol_store_context_set_pre_key_store(ctx, &store); +} + +int Signal::Store::PreKeyStore::containsPreKey(uint32_t pre_key_id) { + return 0; +} + +int Signal::Store::PreKeyStore::loadPreKey(signal_buffer **record, uint32_t pre_key_id) { + return 0; +} + +int Signal::Store::PreKeyStore::storePreKey(uint32_t pre_key_id, uint8_t *record, size_t record_len) { + return 0; +} + +int Signal::Store::PreKeyStore::removePreKey(uint32_t pre_key_id) { return 0; } diff --git a/qomemo/signal/stores/pre_key_store.h b/qomemo/signal/stores/pre_key_store.h new file mode 100644 index 0000000..eb70786 --- /dev/null +++ b/qomemo/signal/stores/pre_key_store.h @@ -0,0 +1,21 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include <signal/signal_protocol.h> + +namespace Signal::Store { + + class PreKeyStore { + public: + static void boundToContext(signal_protocol_store_context *ctx); + + int containsPreKey(uint32_t pre_key_id); + int loadPreKey(signal_buffer **record, uint32_t pre_key_id); + int storePreKey(uint32_t pre_key_id, uint8_t *record, size_t record_len); + int removePreKey(uint32_t pre_key_id); + }; + +} // namespace Signal::Store diff --git a/qomemo/signal/stores/sender_key_store.cpp b/qomemo/signal/stores/sender_key_store.cpp new file mode 100644 index 0000000..a1cb687 --- /dev/null +++ b/qomemo/signal/stores/sender_key_store.cpp @@ -0,0 +1,36 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "sender_key_store.h" + +void Signal::Store::SenderKeyStore::boundToContext( + signal_protocol_store_context *ctx) { + signal_protocol_sender_key_store store{}; + + store.user_data = nullptr; + store.destroy_func = nullptr; + + store.load_sender_key = [](signal_buffer **record, signal_buffer **user_record, + const signal_protocol_sender_key_name *sender_key_name, void *ptr) { + return static_cast<SenderKeyStore *>(ptr)->loadSenderKey(record, user_record, sender_key_name); + }; + store.store_sender_key = [](const signal_protocol_sender_key_name *sender_key_name, uint8_t *record, + size_t record_len, uint8_t *user_record, size_t user_record_len, void *ptr) { + return static_cast<SenderKeyStore *>(ptr)->storeSenderKey(sender_key_name, record, record_len, user_record, + user_record_len); + }; + + signal_protocol_store_context_set_sender_key_store(ctx, &store); +} + +int Signal::Store::SenderKeyStore::loadSenderKey(signal_buffer **record, signal_buffer **user_record, + const signal_protocol_sender_key_name *sender_key_name) { + return 0; +} + +int Signal::Store::SenderKeyStore::storeSenderKey(const signal_protocol_sender_key_name *sender_key_name, + uint8_t *record, size_t record_len, uint8_t *user_record, + size_t user_record_len) { + return 0; +} diff --git a/qomemo/signal/stores/sender_key_store.h b/qomemo/signal/stores/sender_key_store.h new file mode 100644 index 0000000..904377e --- /dev/null +++ b/qomemo/signal/stores/sender_key_store.h @@ -0,0 +1,21 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include <signal/signal_protocol.h> + +namespace Signal::Store { + + class SenderKeyStore { + public: + static void boundToContext(signal_protocol_store_context *ctx); + + int storeSenderKey(const signal_protocol_sender_key_name *sender_key_name, uint8_t *record, size_t record_len, + uint8_t *user_record, size_t user_record_len); + int loadSenderKey(signal_buffer **record, signal_buffer **user_record, + const signal_protocol_sender_key_name *sender_key_name); + }; + +} // namespace Signal::Store diff --git a/qomemo/signal/stores/session_store.cpp b/qomemo/signal/stores/session_store.cpp new file mode 100644 index 0000000..311b3e6 --- /dev/null +++ b/qomemo/signal/stores/session_store.cpp @@ -0,0 +1,63 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "session_store.h" + +void Signal::Store::SessionStore::boundToContext( + signal_protocol_store_context *ctx) { + signal_protocol_session_store store{}; + + store.user_data = nullptr; + store.destroy_func = nullptr; + + store.load_session_func = [](signal_buffer **record, signal_buffer **user_record, + const signal_protocol_address *address, void *ptr) { + return static_cast<SessionStore *>(ptr)->loadSession(record, user_record, address); + }; + store.get_sub_device_sessions_func = [](signal_int_list **sessions, const char *name, size_t name_len, void *ptr) { + return static_cast<SessionStore *>(ptr)->getSubDeviceSessions(sessions, name, name_len); + }; + store.store_session_func = [](const signal_protocol_address *address, uint8_t *record, size_t record_len, + uint8_t *user_record, size_t user_record_len, void *ptr) { + return static_cast<SessionStore *>(ptr)->storeSession(address, record, record_len, user_record, + user_record_len); + }; + store.contains_session_func = [](const signal_protocol_address *address, void *ptr) { + return static_cast<SessionStore *>(ptr)->containsSession(address); + }; + store.delete_session_func = [](const signal_protocol_address *address, void *ptr) { + return static_cast<SessionStore *>(ptr)->deleteSession(address); + }; + store.delete_all_sessions_func = [](const char *name, size_t name_len, void *ptr) { + return static_cast<SessionStore *>(ptr)->deleteAllSessions(name, name_len); + }; + + signal_protocol_store_context_set_session_store(ctx, &store); +} + +int Signal::Store::SessionStore::loadSession(signal_buffer **record, signal_buffer **user_record, + const signal_protocol_address *address) { + return 0; +} + +int Signal::Store::SessionStore::getSubDeviceSessions(signal_int_list **sessions, const char *name, size_t name_len) { + return 0; +} + +int Signal::Store::SessionStore::storeSession(const signal_protocol_address *address, uint8_t *record, + size_t record_len, uint8_t *user_record, size_t user_record_len) { + return 0; +} + +int Signal::Store::SessionStore::containsSession(const signal_protocol_address *address) { + return 0; +} + +int Signal::Store::SessionStore::deleteSession(const signal_protocol_address *address) { + return 0; +} + +int Signal::Store::SessionStore::deleteAllSessions(const char *name, size_t name_len) { + return 0; +} diff --git a/qomemo/signal/stores/session_store.h b/qomemo/signal/stores/session_store.h new file mode 100644 index 0000000..848e5b8 --- /dev/null +++ b/qomemo/signal/stores/session_store.h @@ -0,0 +1,24 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include <signal/signal_protocol.h> + +namespace Signal::Store { + + class SessionStore { + public: + static void boundToContext(signal_protocol_store_context *ctx); + + int loadSession(signal_buffer **record, signal_buffer **user_record, const signal_protocol_address *address); + int getSubDeviceSessions(signal_int_list **sessions, const char *name, size_t name_len); + int storeSession(const signal_protocol_address *address, uint8_t *record, size_t record_len, + uint8_t *user_record, size_t user_record_len); + int containsSession(const signal_protocol_address *address); + int deleteSession(const signal_protocol_address *address); + int deleteAllSessions(const char *name, size_t name_len); + }; + +} // namespace Signal::Store diff --git a/qomemo/signal/stores/signed_pre_key_store.cpp b/qomemo/signal/stores/signed_pre_key_store.cpp new file mode 100644 index 0000000..0156894 --- /dev/null +++ b/qomemo/signal/stores/signed_pre_key_store.cpp @@ -0,0 +1,48 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "signed_pre_key_store.h" + +void Signal::Store::SignedPreKeyStore::boundToContext( + signal_protocol_store_context *ctx) { + signal_protocol_signed_pre_key_store store{}; + + store.user_data = nullptr; + store.destroy_func = nullptr; + + store.load_signed_pre_key = [](signal_buffer **record, uint32_t signed_pre_key_id, void *ptr) { + return static_cast<SignedPreKeyStore *>(ptr)->loadSignedPreKey( + record, signed_pre_key_id); + }; + store.store_signed_pre_key = [](uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *ptr) { + return static_cast<SignedPreKeyStore *>(ptr)->storeSignedPreKey( + signed_pre_key_id, record, record_len); + }; + store.contains_signed_pre_key = [](uint32_t signed_pre_key_id, void *ptr) { + return static_cast<SignedPreKeyStore *>(ptr)->containsSignedPreKey( + signed_pre_key_id); + }; + store.remove_signed_pre_key = [](uint32_t signed_pre_key_id, void *ptr) { + return static_cast<SignedPreKeyStore *>(ptr)->removeSignedPreKey( + signed_pre_key_id); + }; + + signal_protocol_store_context_set_signed_pre_key_store(ctx, &store); +} + +int Signal::Store::SignedPreKeyStore::loadSignedPreKey(signal_buffer **record, uint32_t signed_pre_key_id) { + return 0; +} + +int Signal::Store::SignedPreKeyStore::storeSignedPreKey(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len) { + return 0; +} + +int Signal::Store::SignedPreKeyStore::containsSignedPreKey(uint32_t signed_pre_key_id) { + return 0; +} + +int Signal::Store::SignedPreKeyStore::removeSignedPreKey(uint32_t signed_pre_key_id) { + return 0; +} diff --git a/qomemo/signal/stores/signed_pre_key_store.h b/qomemo/signal/stores/signed_pre_key_store.h new file mode 100644 index 0000000..3f58851 --- /dev/null +++ b/qomemo/signal/stores/signed_pre_key_store.h @@ -0,0 +1,20 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include <signal/signal_protocol.h> + +namespace Signal::Store { + + class SignedPreKeyStore { + public: + static void boundToContext(signal_protocol_store_context *ctx); + int loadSignedPreKey(signal_buffer **record, uint32_t signed_pre_key_id); + int storeSignedPreKey(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len); + int containsSignedPreKey(uint32_t signed_pre_key_id); + int removeSignedPreKey(uint32_t signed_pre_key_id); + }; + +} // namespace Signal::Store diff --git a/qomemo/signal/stores/store_context.cpp b/qomemo/signal/stores/store_context.cpp new file mode 100644 index 0000000..dd5bb5e --- /dev/null +++ b/qomemo/signal/stores/store_context.cpp @@ -0,0 +1,13 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "store_context.h" + +Signal::Store::Context::Context(signal_context *global) { + signal_protocol_store_context_create(&ctx, global); +} + +Signal::Store::Context::~Context() { + signal_protocol_store_context_destroy(ctx); +} diff --git a/qomemo/signal/stores/store_context.h b/qomemo/signal/stores/store_context.h new file mode 100644 index 0000000..204d9b9 --- /dev/null +++ b/qomemo/signal/stores/store_context.h @@ -0,0 +1,24 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include <signal/signal_protocol.h> + +namespace Signal::Store { + + class Context { + public: + explicit Context(signal_context *global); + ~Context(); + + Context(const Context &) = delete; + Context(Context &&) = delete; + Context &operator=(const Context &) = delete; + + private: + signal_protocol_store_context *ctx{nullptr}; + }; + +} // namespace Signal::Store From 574210f5d99948cb13c9124b3fd992e8a1020335 Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Thu, 13 May 2021 17:50:59 +0300 Subject: [PATCH 15/20] ref(omemo): bundle separate, add db --- qomemo/bundle.cpp | 7 +- qomemo/bundle.h | 2 +- qomemo/database.cpp | 126 +++++++++++++++++++++++++++++++++ qomemo/database.h | 40 +++++++++++ qomemo/device_service.h | 2 +- qomemo/qxmpp_omemo_manager.cpp | 13 ++-- qomemo/sce.h | 2 +- qomemo/user_device_list.h | 1 + qomemo/variant/omemo_base.h | 10 +-- 9 files changed, 189 insertions(+), 14 deletions(-) create mode 100644 qomemo/database.cpp create mode 100644 qomemo/database.h diff --git a/qomemo/bundle.cpp b/qomemo/bundle.cpp index aec605a..91f630d 100644 --- a/qomemo/bundle.cpp +++ b/qomemo/bundle.cpp @@ -64,7 +64,9 @@ QXmppIq QXmpp::Omemo::Bundle::toIq(int deviceId) const { item.appendChild(toXml()); auto publish = createElement("publish"); - publish.setAttribute("node", QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId)); + publish.setAttribute( + "node", + QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId)); publish.appendChild(item); auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); @@ -116,7 +118,8 @@ void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) { QXmppPubSubIq QXmpp::Omemo::Bundle::fetchDeviceBundleIq(int deviceId) { QXmppPubSubIq iq{}; iq.setType(QXmppIq::Get); - iq.setQueryNode(QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId)); + iq.setQueryNode( + QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId)); QXmppPubSubItem item{}; item.setId(QString::number(deviceId)); diff --git a/qomemo/bundle.h b/qomemo/bundle.h index d29bf02..bfa9fff 100644 --- a/qomemo/bundle.h +++ b/qomemo/bundle.h @@ -38,4 +38,4 @@ public: QList<PreKey> prekeys; }; -} +} // namespace QXmpp::Omemo diff --git a/qomemo/database.cpp b/qomemo/database.cpp new file mode 100644 index 0000000..3644522 --- /dev/null +++ b/qomemo/database.cpp @@ -0,0 +1,126 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "database.h" + +#include <QDebug> +#include <QDir> +#include <QException> +#include <QStandardPaths> +#include <QString> + +using namespace QXmpp::Omemo; + +Database::Database(QString jid) : jid(std::move(jid)) { + auto cacheLocation = + QStandardPaths::writableLocation(QStandardPaths::CacheLocation); + auto path = QString("%1/.omemo/%2").arg(cacheLocation, jid); + QDir cache(path); + + if (!cache.exists() && !cache.mkpath(path)) { + qWarning() << "Could not create:" << path; + throw QException(); + } + + mdb_env_create(&env); + + mdb_env_set_maxdbs(env, 5); + mdb_env_set_mapsize(env, 512UL * 1024UL * 1024UL); + mdb_env_open(env, path.toStdString().c_str(), 0, 0664); + + MDB_txn *txn; + mdb_txn_begin(env, nullptr, 0, &txn); + mdb_dbi_open(txn, "keys", MDB_CREATE, &dbiKeys); + mdb_dbi_open(txn, "devices", MDB_CREATE, &dbiDevices); + mdb_dbi_open(txn, "identity_keys", MDB_CREATE, &dbiIdentityKeys); + mdb_txn_commit(txn); +} + +Database::~Database() { + mdb_dbi_close(env, dbiKeys); + mdb_dbi_close(env, dbiDevices); + mdb_dbi_close(env, dbiIdentityKeys); + mdb_env_close(env); +} + +QBuffer Database::loadIdentityKeySecret(int deviceId) { return QBuffer(); } + +bool Database::saveIdentityKeySecret(int deviceId, + const QBuffer &identityKeySecret) { + MDB_val mdbKey, mdbValue; + auto key = QString("%1/secret").arg(QString::number(deviceId)).toStdString(); + + mdbKey.mv_data = key.data(); + mdbKey.mv_size = key.size(); + + mdbValue.mv_data = const_cast<char *>(identityKeySecret.data().data()); + mdbValue.mv_size = identityKeySecret.size(); + + MDB_txn *txn; + mdb_txn_begin(env, nullptr, 0, &txn); + auto err = mdb_put(txn, dbiIdentityKeys, &mdbKey, &mdbValue, MDB_NOOVERWRITE); + if (!err) { + mdb_txn_commit(txn); + return true; + } + + qWarning() << "could not save identity key secret:" << mdb_strerror(err); + mdb_txn_abort(txn); + + return false; +} + +int Database::loadActiveDeviceId() { + MDB_val key, value; + + key.mv_data = (void *)"active"; + key.mv_size = sizeof("active"); + + MDB_txn *txn; + mdb_txn_begin(env, nullptr, 0, &txn); + + auto err = mdb_get(txn, dbiIdentityKeys, &key, &value); + if (err) { + qWarning() << "could not load active device id:" << mdb_strerror(err); + return 0; + } + + if (value.mv_size != sizeof(int)) { + qWarning() << "mv_size is" << value.mv_size << "instead of" << sizeof(int); + return 0; + } + + auto id = *reinterpret_cast<int *>(value.mv_data); + + mdb_txn_abort(txn); + + return id; +} + +bool Database::saveActiveDeviceId(int deviceId) { + MDB_val key, value; + + key.mv_data = (void *)"active"; + key.mv_size = sizeof("active"); + + value.mv_data = &deviceId; + value.mv_size = sizeof(deviceId); + + MDB_txn *txn; + mdb_txn_begin(env, nullptr, 0, &txn); + + auto err = mdb_put(txn, dbiIdentityKeys, &key, &value, 0); + if (err) { + qWarning() << "could not save active device id" << mdb_strerror(err); + return false; + } + + err = mdb_txn_commit(txn); + if (err) { + qWarning() << "could not save active device id" << mdb_strerror(err); + return false; + } + + return true; +} diff --git a/qomemo/database.h b/qomemo/database.h new file mode 100644 index 0000000..4584b2e --- /dev/null +++ b/qomemo/database.h @@ -0,0 +1,40 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include <QBuffer> +#include <QString> +#include <lmdb.h> + +namespace QXmpp::Omemo { + +class Database { +public: + explicit Database(QString jid); + ~Database(); + Database(const Database &) = delete; + Database(Database &&) = delete; + Database &operator=(const Database &) = delete; + + QBuffer loadIdentityKey(); + bool saveIdentityKey(const QBuffer &identityKey); + + int loadActiveDeviceId(); + bool saveActiveDeviceId(int deviceId); + + QBuffer loadIdentityKeySecret(int deviceId); + bool saveIdentityKeySecret(int deviceId, const QBuffer &identityKeySecret); + + const QString jid; + +private: + MDB_env *env{}; + MDB_dbi dbiDevices{}; + MDB_dbi dbiKeys{}; + MDB_dbi dbiPreKeys{}; + MDB_dbi dbiIdentityKeys{}; +}; + +} // namespace QXmpp::Omemo diff --git a/qomemo/device_service.h b/qomemo/device_service.h index 774b7f4..c4aa04e 100644 --- a/qomemo/device_service.h +++ b/qomemo/device_service.h @@ -20,7 +20,7 @@ public: explicit DeviceService(QObject *parent); public slots: - void onDeviceListReceived(const QString& jid, const DeviceList& list); + void onDeviceListReceived(const QString &jid, const DeviceList &list); private: QMap<QString, UserDeviceList> device_lists{}; diff --git a/qomemo/qxmpp_omemo_manager.cpp b/qomemo/qxmpp_omemo_manager.cpp index f29ec98..062b934 100644 --- a/qomemo/qxmpp_omemo_manager.cpp +++ b/qomemo/qxmpp_omemo_manager.cpp @@ -15,8 +15,11 @@ using namespace QXmpp::Omemo; -Manager::Manager() : deviceService(new DeviceService(this)), omemoVariant(new Variant::Conversations) { - connect(this, &Manager::deviceListReceived, deviceService.get(), &DeviceService::onDeviceListReceived); +Manager::Manager() + : deviceService(new DeviceService(this)), + omemoVariant(new Variant::Conversations) { + connect(this, &Manager::deviceListReceived, deviceService.get(), + &DeviceService::onDeviceListReceived); } bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) { @@ -31,7 +34,8 @@ bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) { auto pubsub = stanza.firstChildElement("pubsub"); if (!pubsub.isNull()) { auto items = pubsub.firstChildElement("items"); - if (items.attribute("node") == "eu.siacs.conversations.axolotl.devicelist") { + if (items.attribute("node") == + "eu.siacs.conversations.axolotl.devicelist") { auto item = items.firstChildElement("item"); if (!item.isNull()) { auto list = item.firstChildElement("list"); @@ -56,7 +60,8 @@ void QXmpp::Omemo::Manager::setClient(QXmppClient *client) { if (!client) return; - QObject::connect(client, &QXmppClient::connected, this, &Manager::fetchOwnDevices); + QObject::connect(client, &QXmppClient::connected, this, + &Manager::fetchOwnDevices); } void QXmpp::Omemo::Manager::fetchOwnDevices() { diff --git a/qomemo/sce.h b/qomemo/sce.h index 2cea049..91a7b2c 100644 --- a/qomemo/sce.h +++ b/qomemo/sce.h @@ -4,8 +4,8 @@ #pragma once -#include <QXmppElement.h> #include <QDateTime> +#include <QXmppElement.h> namespace QXmpp::Sce { diff --git a/qomemo/user_device_list.h b/qomemo/user_device_list.h index 26a88ca..27e407a 100644 --- a/qomemo/user_device_list.h +++ b/qomemo/user_device_list.h @@ -5,6 +5,7 @@ #pragma once #include <QString> + namespace QXmpp::Omemo { class UserDeviceList { diff --git a/qomemo/variant/omemo_base.h b/qomemo/variant/omemo_base.h index f383a59..fd11f31 100644 --- a/qomemo/variant/omemo_base.h +++ b/qomemo/variant/omemo_base.h @@ -18,12 +18,12 @@ class Base { public: virtual ~Base() = default; - virtual QXmppElement deviceToXml(const Device& device) = 0; - virtual Device deviceFromXml(const QXmppElement& xml) = 0; + virtual QXmppElement deviceToXml(const Device &device) = 0; + virtual Device deviceFromXml(const QXmppElement &xml) = 0; - virtual QXmppElement deviceListToXml(const DeviceList& deviceList) = 0; - virtual DeviceList deviceListFromXml(const QXmppElement& xml) = 0; - virtual QXmppIq deviceListSetIq(const DeviceList& deviceList) = 0; + virtual QXmppElement deviceListToXml(const DeviceList &deviceList) = 0; + virtual DeviceList deviceListFromXml(const QXmppElement &xml) = 0; + virtual QXmppIq deviceListSetIq(const DeviceList &deviceList) = 0; }; } // namespace Variant From bbeeee4c8a119ce90bd840459a66077b84e9f1f0 Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Thu, 13 May 2021 17:54:37 +0300 Subject: [PATCH 16/20] ref(omemo): reformat qomemo/ --- qomemo/CMakeLists.txt | 38 ++--- qomemo/TODO | 12 -- qomemo/bundle.cpp | 156 +++++++++--------- qomemo/bundle.h | 42 ++--- qomemo/database.cpp | 146 ++++++++-------- qomemo/database.h | 42 ++--- qomemo/device.h | 17 +- qomemo/device_key_storage.cpp | 7 +- qomemo/device_key_storage.h | 12 +- qomemo/device_service.cpp | 9 +- qomemo/device_service.h | 20 +-- qomemo/qomemo.cpp | 98 +++++------ qomemo/qomemo.h | 42 ++--- qomemo/qxmpp_omemo_manager.cpp | 73 ++++---- qomemo/qxmpp_omemo_manager.h | 32 ++-- qomemo/sce.cpp | 20 ++- qomemo/sce.h | 2 +- qomemo/signal/stores/signed_pre_key_store.cpp | 3 +- qomemo/user_device_list.cpp | 3 +- qomemo/user_device_list.h | 10 +- qomemo/variant/conversations.cpp | 72 ++++---- qomemo/variant/conversations.h | 18 +- qomemo/variant/omemo_base.h | 28 ++-- 23 files changed, 447 insertions(+), 455 deletions(-) delete mode 100644 qomemo/TODO diff --git a/qomemo/CMakeLists.txt b/qomemo/CMakeLists.txt index 7391404..8b194be 100644 --- a/qomemo/CMakeLists.txt +++ b/qomemo/CMakeLists.txt @@ -1,23 +1,23 @@ target_sources(squawk PRIVATE - bundle.cpp - bundle.h - database.cpp - database.h - device.cpp - device.h - device_key_storage.cpp - device_key_storage.h - device_service.cpp - device_service.h - qomemo.cpp - qomemo.h - sce.cpp - sce.h - user_device_list.cpp - user_device_list.h - qxmpp_omemo_manager.cpp - qxmpp_omemo_manager.h - ) + bundle.cpp + bundle.h + database.cpp + database.h + device.cpp + device.h + device_key_storage.cpp + device_key_storage.h + device_service.cpp + device_service.h + qomemo.cpp + qomemo.h + sce.cpp + sce.h + user_device_list.cpp + user_device_list.h + qxmpp_omemo_manager.cpp + qxmpp_omemo_manager.h + ) add_subdirectory(signal) add_subdirectory(variant) diff --git a/qomemo/TODO b/qomemo/TODO deleted file mode 100644 index 6a5709d..0000000 --- a/qomemo/TODO +++ /dev/null @@ -1,12 +0,0 @@ -* Generate device w/ keys -* PubSub set urn:xmpp:omemo:1:devices to announce new device -* PubSub set urn:xmpp:omemo:1:bundles to announce new key bundles -* PubSub get urn:xmpp:omemo:1:bundles to get user bundles - -Sending a message: -* Create urn:xmpp:sce:0 with padding and content -* Add <encrypted xmlns='urn:xmpp:omemo:1'> with header (key list) and payload (b64) - -Receiving a message: -* Check <keys> => <key> -* Decrypt (TODO) \ No newline at end of file diff --git a/qomemo/bundle.cpp b/qomemo/bundle.cpp index 91f630d..691a65e 100644 --- a/qomemo/bundle.cpp +++ b/qomemo/bundle.cpp @@ -13,117 +13,117 @@ using namespace QXmpp::Factories; QXmppElement QXmpp::Omemo::PreKey::toXml() const { - auto pk = createElement("preKeyPublic"); - pk.setAttribute("preKeyId", QString::number(id)); - // TODO: Base64 - pk.setValue(data); + auto pk = createElement("preKeyPublic"); + pk.setAttribute("preKeyId", QString::number(id)); + // TODO: Base64 + pk.setValue(data); - return pk; + return pk; } void QXmpp::Omemo::PreKey::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "preKeyPublic")) - return; + if (!elementMatches(element, "preKeyPublic")) + return; - id = element.attribute("preKeyId").toInt(); - // TODO: Base64 - data = element.value(); + id = element.attribute("preKeyId").toInt(); + // TODO: Base64 + data = element.value(); } QXmppElement QXmpp::Omemo::Bundle::toXml() const { - auto spkNode = createElement("signedPreKeyPublic"); - spkNode.setAttribute("signedPreKeyId", QString::number(spkId)); - spkNode.setValue(spk); + auto spkNode = createElement("signedPreKeyPublic"); + spkNode.setAttribute("signedPreKeyId", QString::number(spkId)); + spkNode.setValue(spk); - auto spksNode = createElement("signedPreKeySignature"); - spksNode.setValue(spks); + auto spksNode = createElement("signedPreKeySignature"); + spksNode.setValue(spks); - auto ikNode = createElement("identityKey"); - ikNode.setValue(ik); + auto ikNode = createElement("identityKey"); + ikNode.setValue(ik); - auto prekeysNode = createElement("prekeys"); - for (const auto &pk : prekeys) { - prekeysNode.appendChild(pk.toXml()); - } + auto prekeysNode = createElement("prekeys"); + for (const auto &pk : prekeys) { + prekeysNode.appendChild(pk.toXml()); + } - auto result = createElement("bundle", "eu.siacs.conversations.axolotl"); - result.appendChild(spkNode); - result.appendChild(spksNode); - result.appendChild(ikNode); - result.appendChild(prekeysNode); + auto result = createElement("bundle", "eu.siacs.conversations.axolotl"); + result.appendChild(spkNode); + result.appendChild(spksNode); + result.appendChild(ikNode); + result.appendChild(prekeysNode); - return result; + return result; } QXmppIq QXmpp::Omemo::Bundle::toIq(int deviceId) const { - QXmppIq iq{}; + QXmppIq iq{}; - iq.setType(QXmppIq::Set); + iq.setType(QXmppIq::Set); - auto item = createElement("item"); - item.appendChild(toXml()); + auto item = createElement("item"); + item.appendChild(toXml()); - auto publish = createElement("publish"); - publish.setAttribute( - "node", - QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId)); - publish.appendChild(item); + auto publish = createElement("publish"); + publish.setAttribute( + "node", + QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId)); + publish.appendChild(item); - auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); - pubSub.appendChild(publish); - pubSub.appendChild(createOpenPublishOptions()); + auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); + pubSub.appendChild(publish); + pubSub.appendChild(createOpenPublishOptions()); - iq.extensions().push_back(pubSub); + iq.extensions().push_back(pubSub); - return iq; + return iq; } void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "bundle", "eu.siacs.conversations.axolotl")) - return; + if (!elementMatches(element, "bundle", "eu.siacs.conversations.axolotl")) + return; - auto spkNode = element.firstChildElement("spk"); - if (spkNode.isNull()) { - qWarning() << "'bundle': missing 'spk'"; - return; - } - spk = spkNode.value(); - spkId = spkNode.attribute("id").toInt(); + auto spkNode = element.firstChildElement("spk"); + if (spkNode.isNull()) { + qWarning() << "'bundle': missing 'spk'"; + return; + } + spk = spkNode.value(); + spkId = spkNode.attribute("id").toInt(); - auto spksNode = element.firstChildElement("spks"); - if (spksNode.isNull()) { - qWarning() << "'bundle': missing 'spks'"; - return; - } - spks = spksNode.value(); + auto spksNode = element.firstChildElement("spks"); + if (spksNode.isNull()) { + qWarning() << "'bundle': missing 'spks'"; + return; + } + spks = spksNode.value(); - auto ikNode = element.firstChildElement("ik"); - if (ikNode.isNull()) { - qWarning() << "'bundle': missing 'ik'"; - return; - } - ik = ikNode.value(); + auto ikNode = element.firstChildElement("ik"); + if (ikNode.isNull()) { + qWarning() << "'bundle': missing 'ik'"; + return; + } + ik = ikNode.value(); - auto prekeysNode = element.firstChildElement("prekeys"); - auto pkNode = prekeysNode.firstChildElement("pk"); + auto prekeysNode = element.firstChildElement("prekeys"); + auto pkNode = prekeysNode.firstChildElement("pk"); - prekeys.clear(); - while (!pkNode.isNull()) { - PreKey pk{}; - pk.fromXml(pkNode); - prekeys.push_back(pk); - } + prekeys.clear(); + while (!pkNode.isNull()) { + PreKey pk{}; + pk.fromXml(pkNode); + prekeys.push_back(pk); + } } QXmppPubSubIq QXmpp::Omemo::Bundle::fetchDeviceBundleIq(int deviceId) { - QXmppPubSubIq iq{}; - iq.setType(QXmppIq::Get); - iq.setQueryNode( - QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId)); + QXmppPubSubIq iq{}; + iq.setType(QXmppIq::Get); + iq.setQueryNode( + QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId)); - QXmppPubSubItem item{}; - item.setId(QString::number(deviceId)); - iq.setItems({item}); + QXmppPubSubItem item{}; + item.setId(QString::number(deviceId)); + iq.setItems({item}); - return iq; + return iq; } \ No newline at end of file diff --git a/qomemo/bundle.h b/qomemo/bundle.h index bfa9fff..428277b 100644 --- a/qomemo/bundle.h +++ b/qomemo/bundle.h @@ -8,34 +8,36 @@ #include <QString> class QXmppPubSubIq; + class QXmppElement; + class QXmppIq; namespace QXmpp::Omemo { -class PreKey { -public: - [[nodiscard]] QXmppElement toXml() const; - /// Expects a <pk> - void fromXml(const QXmppElement &element); + class PreKey { + public: + [[nodiscard]] QXmppElement toXml() const; + /// Expects a <pk> + void fromXml(const QXmppElement &element); - int id; - QString data; -}; + int id; + QString data; + }; -class Bundle { -public: - [[nodiscard]] static QXmppPubSubIq fetchDeviceBundleIq(int deviceId); + class Bundle { + public: + [[nodiscard]] static QXmppPubSubIq fetchDeviceBundleIq(int deviceId); - [[nodiscard]] QXmppElement toXml() const; - [[nodiscard]] QXmppIq toIq(int deviceId) const; - void fromXml(const QXmppElement &element); + [[nodiscard]] QXmppElement toXml() const; + [[nodiscard]] QXmppIq toIq(int deviceId) const; + void fromXml(const QXmppElement &element); - QString spk; - int spkId; - QString spks; - QString ik; - QList<PreKey> prekeys; -}; + QString spk; + int spkId; + QString spks; + QString ik; + QList<PreKey> prekeys; + }; } // namespace QXmpp::Omemo diff --git a/qomemo/database.cpp b/qomemo/database.cpp index 3644522..2314a80 100644 --- a/qomemo/database.cpp +++ b/qomemo/database.cpp @@ -13,114 +13,114 @@ using namespace QXmpp::Omemo; Database::Database(QString jid) : jid(std::move(jid)) { - auto cacheLocation = - QStandardPaths::writableLocation(QStandardPaths::CacheLocation); - auto path = QString("%1/.omemo/%2").arg(cacheLocation, jid); - QDir cache(path); + auto cacheLocation = + QStandardPaths::writableLocation(QStandardPaths::CacheLocation); + auto path = QString("%1/.omemo/%2").arg(cacheLocation, jid); + QDir cache(path); - if (!cache.exists() && !cache.mkpath(path)) { - qWarning() << "Could not create:" << path; - throw QException(); - } + if (!cache.exists() && !cache.mkpath(path)) { + qWarning() << "Could not create:" << path; + throw QException(); + } - mdb_env_create(&env); + mdb_env_create(&env); - mdb_env_set_maxdbs(env, 5); - mdb_env_set_mapsize(env, 512UL * 1024UL * 1024UL); - mdb_env_open(env, path.toStdString().c_str(), 0, 0664); + mdb_env_set_maxdbs(env, 5); + mdb_env_set_mapsize(env, 512UL * 1024UL * 1024UL); + mdb_env_open(env, path.toStdString().c_str(), 0, 0664); - MDB_txn *txn; - mdb_txn_begin(env, nullptr, 0, &txn); - mdb_dbi_open(txn, "keys", MDB_CREATE, &dbiKeys); - mdb_dbi_open(txn, "devices", MDB_CREATE, &dbiDevices); - mdb_dbi_open(txn, "identity_keys", MDB_CREATE, &dbiIdentityKeys); - mdb_txn_commit(txn); + MDB_txn *txn; + mdb_txn_begin(env, nullptr, 0, &txn); + mdb_dbi_open(txn, "keys", MDB_CREATE, &dbiKeys); + mdb_dbi_open(txn, "devices", MDB_CREATE, &dbiDevices); + mdb_dbi_open(txn, "identity_keys", MDB_CREATE, &dbiIdentityKeys); + mdb_txn_commit(txn); } Database::~Database() { - mdb_dbi_close(env, dbiKeys); - mdb_dbi_close(env, dbiDevices); - mdb_dbi_close(env, dbiIdentityKeys); - mdb_env_close(env); + mdb_dbi_close(env, dbiKeys); + mdb_dbi_close(env, dbiDevices); + mdb_dbi_close(env, dbiIdentityKeys); + mdb_env_close(env); } QBuffer Database::loadIdentityKeySecret(int deviceId) { return QBuffer(); } bool Database::saveIdentityKeySecret(int deviceId, const QBuffer &identityKeySecret) { - MDB_val mdbKey, mdbValue; - auto key = QString("%1/secret").arg(QString::number(deviceId)).toStdString(); + MDB_val mdbKey, mdbValue; + auto key = QString("%1/secret").arg(QString::number(deviceId)).toStdString(); - mdbKey.mv_data = key.data(); - mdbKey.mv_size = key.size(); + mdbKey.mv_data = key.data(); + mdbKey.mv_size = key.size(); - mdbValue.mv_data = const_cast<char *>(identityKeySecret.data().data()); - mdbValue.mv_size = identityKeySecret.size(); + mdbValue.mv_data = const_cast<char *>(identityKeySecret.data().data()); + mdbValue.mv_size = identityKeySecret.size(); - MDB_txn *txn; - mdb_txn_begin(env, nullptr, 0, &txn); - auto err = mdb_put(txn, dbiIdentityKeys, &mdbKey, &mdbValue, MDB_NOOVERWRITE); - if (!err) { - mdb_txn_commit(txn); - return true; - } + MDB_txn *txn; + mdb_txn_begin(env, nullptr, 0, &txn); + auto err = mdb_put(txn, dbiIdentityKeys, &mdbKey, &mdbValue, MDB_NOOVERWRITE); + if (!err) { + mdb_txn_commit(txn); + return true; + } - qWarning() << "could not save identity key secret:" << mdb_strerror(err); - mdb_txn_abort(txn); + qWarning() << "could not save identity key secret:" << mdb_strerror(err); + mdb_txn_abort(txn); - return false; + return false; } int Database::loadActiveDeviceId() { - MDB_val key, value; + MDB_val key, value; - key.mv_data = (void *)"active"; - key.mv_size = sizeof("active"); + key.mv_data = (void *) "active"; + key.mv_size = sizeof("active"); - MDB_txn *txn; - mdb_txn_begin(env, nullptr, 0, &txn); + MDB_txn *txn; + mdb_txn_begin(env, nullptr, 0, &txn); - auto err = mdb_get(txn, dbiIdentityKeys, &key, &value); - if (err) { - qWarning() << "could not load active device id:" << mdb_strerror(err); - return 0; - } + auto err = mdb_get(txn, dbiIdentityKeys, &key, &value); + if (err) { + qWarning() << "could not load active device id:" << mdb_strerror(err); + return 0; + } - if (value.mv_size != sizeof(int)) { - qWarning() << "mv_size is" << value.mv_size << "instead of" << sizeof(int); - return 0; - } + if (value.mv_size != sizeof(int)) { + qWarning() << "mv_size is" << value.mv_size << "instead of" << sizeof(int); + return 0; + } - auto id = *reinterpret_cast<int *>(value.mv_data); + auto id = *reinterpret_cast<int *>(value.mv_data); - mdb_txn_abort(txn); + mdb_txn_abort(txn); - return id; + return id; } bool Database::saveActiveDeviceId(int deviceId) { - MDB_val key, value; + MDB_val key, value; - key.mv_data = (void *)"active"; - key.mv_size = sizeof("active"); + key.mv_data = (void *) "active"; + key.mv_size = sizeof("active"); - value.mv_data = &deviceId; - value.mv_size = sizeof(deviceId); + value.mv_data = &deviceId; + value.mv_size = sizeof(deviceId); - MDB_txn *txn; - mdb_txn_begin(env, nullptr, 0, &txn); + MDB_txn *txn; + mdb_txn_begin(env, nullptr, 0, &txn); - auto err = mdb_put(txn, dbiIdentityKeys, &key, &value, 0); - if (err) { - qWarning() << "could not save active device id" << mdb_strerror(err); - return false; - } + auto err = mdb_put(txn, dbiIdentityKeys, &key, &value, 0); + if (err) { + qWarning() << "could not save active device id" << mdb_strerror(err); + return false; + } - err = mdb_txn_commit(txn); - if (err) { - qWarning() << "could not save active device id" << mdb_strerror(err); - return false; - } + err = mdb_txn_commit(txn); + if (err) { + qWarning() << "could not save active device id" << mdb_strerror(err); + return false; + } - return true; + return true; } diff --git a/qomemo/database.h b/qomemo/database.h index 4584b2e..3f9b933 100644 --- a/qomemo/database.h +++ b/qomemo/database.h @@ -10,31 +10,31 @@ namespace QXmpp::Omemo { -class Database { -public: - explicit Database(QString jid); - ~Database(); - Database(const Database &) = delete; - Database(Database &&) = delete; - Database &operator=(const Database &) = delete; + class Database { + public: + explicit Database(QString jid); + ~Database(); + Database(const Database &) = delete; + Database(Database &&) = delete; + Database &operator=(const Database &) = delete; - QBuffer loadIdentityKey(); - bool saveIdentityKey(const QBuffer &identityKey); + QBuffer loadIdentityKey(); + bool saveIdentityKey(const QBuffer &identityKey); - int loadActiveDeviceId(); - bool saveActiveDeviceId(int deviceId); + int loadActiveDeviceId(); + bool saveActiveDeviceId(int deviceId); - QBuffer loadIdentityKeySecret(int deviceId); - bool saveIdentityKeySecret(int deviceId, const QBuffer &identityKeySecret); + QBuffer loadIdentityKeySecret(int deviceId); + bool saveIdentityKeySecret(int deviceId, const QBuffer &identityKeySecret); - const QString jid; + const QString jid; -private: - MDB_env *env{}; - MDB_dbi dbiDevices{}; - MDB_dbi dbiKeys{}; - MDB_dbi dbiPreKeys{}; - MDB_dbi dbiIdentityKeys{}; -}; + private: + MDB_env *env{}; + MDB_dbi dbiDevices{}; + MDB_dbi dbiKeys{}; + MDB_dbi dbiPreKeys{}; + MDB_dbi dbiIdentityKeys{}; + }; } // namespace QXmpp::Omemo diff --git a/qomemo/device.h b/qomemo/device.h index 4af6ada..6a67a19 100644 --- a/qomemo/device.h +++ b/qomemo/device.h @@ -7,18 +7,19 @@ #include <QList> class QXmppElement; + class QXmppIq; namespace QXmpp::Omemo { -class Device { -public: - int id; -}; + class Device { + public: + int id; + }; -class DeviceList { -public: - QList<Device> devices; -}; + class DeviceList { + public: + QList<Device> devices; + }; } // namespace QXmpp::Omemo diff --git a/qomemo/device_key_storage.cpp b/qomemo/device_key_storage.cpp index 1ba017e..d8c74df 100644 --- a/qomemo/device_key_storage.cpp +++ b/qomemo/device_key_storage.cpp @@ -6,10 +6,9 @@ #include <QRandomGenerator> int QXmpp::Omemo::DeviceKeyStorage::generateDeviceId() { - QRandomGenerator random{}; + QRandomGenerator random{}; - return 1 + random.bounded(INT32_MAX - 1); + return 1 + random.bounded(INT32_MAX - 1); } -QXmpp::Omemo::DeviceKeyStorage::DeviceKeyStorage(int deviceId) - : deviceId(deviceId) {} +QXmpp::Omemo::DeviceKeyStorage::DeviceKeyStorage(int deviceId) : deviceId(deviceId) {} diff --git a/qomemo/device_key_storage.h b/qomemo/device_key_storage.h index a0d8ce2..efeade9 100644 --- a/qomemo/device_key_storage.h +++ b/qomemo/device_key_storage.h @@ -6,13 +6,13 @@ namespace QXmpp::Omemo { -class DeviceKeyStorage { -public: - static int generateDeviceId(); + class DeviceKeyStorage { + public: + static int generateDeviceId(); - explicit DeviceKeyStorage(int deviceId); + explicit DeviceKeyStorage(int deviceId); - const int deviceId; -}; + const int deviceId; + }; } // namespace QXmpp::Omemo diff --git a/qomemo/device_service.cpp b/qomemo/device_service.cpp index fcc6b72..306f383 100644 --- a/qomemo/device_service.cpp +++ b/qomemo/device_service.cpp @@ -7,10 +7,9 @@ QXmpp::Omemo::DeviceService::DeviceService(QObject *parent) : QObject(parent) {} -void QXmpp::Omemo::DeviceService::onDeviceListReceived( - const QString &jid, const QXmpp::Omemo::DeviceList &list) { +void QXmpp::Omemo::DeviceService::onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list) { - for (const auto &device : list.devices) { - qInfo() << "Got device for" << jid << ":" << device.id; - } + for (const auto &device : list.devices) { + qInfo() << "Got device for" << jid << ":" << device.id; + } } diff --git a/qomemo/device_service.h b/qomemo/device_service.h index c4aa04e..a9529c4 100644 --- a/qomemo/device_service.h +++ b/qomemo/device_service.h @@ -11,19 +11,19 @@ namespace QXmpp::Omemo { -class DeviceList; + class DeviceList; -class DeviceService : public QObject { - Q_OBJECT + class DeviceService : public QObject { + Q_OBJECT -public: - explicit DeviceService(QObject *parent); + public: + explicit DeviceService(QObject *parent); -public slots: - void onDeviceListReceived(const QString &jid, const DeviceList &list); + public slots: + void onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list); -private: - QMap<QString, UserDeviceList> device_lists{}; -}; + private: + QMap<QString, UserDeviceList> device_lists{}; + }; } // namespace QXmpp::Omemo diff --git a/qomemo/qomemo.cpp b/qomemo/qomemo.cpp index d078f37..2b67119 100644 --- a/qomemo/qomemo.cpp +++ b/qomemo/qomemo.cpp @@ -13,82 +13,82 @@ using namespace QXmpp::Factories; QXmppElement QXmpp::Omemo::EncryptedMessage::header() const { - auto result = createElement("header"); - result.setAttribute("sid", QString::number(fromDeviceId)); + auto result = createElement("header"); + result.setAttribute("sid", QString::number(fromDeviceId)); - auto ivNode = createElement("iv"); - ivNode.setValue(iv); + auto ivNode = createElement("iv"); + ivNode.setValue(iv); - for (const auto &key : keys) { - result.appendChild(key.toXml()); - } + for (const auto &key : keys) { + result.appendChild(key.toXml()); + } - return result; + return result; } QXmppElement QXmpp::Omemo::EncryptedMessage::toXml() const { - auto result = createElement("encrypted", "eu.siacs.conversations.axolotl"); + auto result = createElement("encrypted", "eu.siacs.conversations.axolotl"); - result.appendChild(header()); - // TODO: Payload is optional - result.appendChild(payload()); + result.appendChild(header()); + // TODO: Payload is optional + result.appendChild(payload()); - return result; + return result; } QXmppElement QXmpp::Omemo::EncryptedMessage::payload() const { - QBuffer buffer; - buffer.open(QIODevice::ReadWrite); - QXmlStreamWriter writer(&buffer); - message.toXml(&writer); + QBuffer buffer; + buffer.open(QIODevice::ReadWrite); + QXmlStreamWriter writer(&buffer); + message.toXml(&writer); - QDomDocument doc; - doc.setContent(buffer.data(), true); + QDomDocument doc; + doc.setContent(buffer.data(), true); - QXmppElement root(doc.documentElement()); - root.setTagName("payload"); + QXmppElement root(doc.documentElement()); + root.setTagName("payload"); - return root; + return root; } QXmppElement QXmpp::Omemo::EncryptedMessage::content() const { - auto envelope = createElement("content", "urn:xmpp:sce:0"); + auto envelope = createElement("content", "urn:xmpp:sce:0"); - envelope.appendChild(payload()); + envelope.appendChild(payload()); - if (!from.isEmpty()) { - auto fromNode = createElement("from"); - fromNode.setAttribute("jid", from); - envelope.appendChild(fromNode); - } + if (!from.isEmpty()) { + auto fromNode = createElement("from"); + fromNode.setAttribute("jid", from); + envelope.appendChild(fromNode); + } - if (!to.isEmpty()) { - auto toNode = createElement("to"); - toNode.setAttribute("jid", to); - envelope.appendChild(toNode); - } + if (!to.isEmpty()) { + auto toNode = createElement("to"); + toNode.setAttribute("jid", to); + envelope.appendChild(toNode); + } - if (!timestamp.isNull()) { - auto timeNode = createElement("time"); - timeNode.setAttribute("stamp", timestamp.toString(Qt::DateFormat::ISODate)); - envelope.appendChild(timeNode); - } + if (!timestamp.isNull()) { + auto timeNode = createElement("time"); + timeNode.setAttribute("stamp", timestamp.toString(Qt::DateFormat::ISODate)); + envelope.appendChild(timeNode); + } - auto rpad = createElement("rpad"); - rpad.setValue(QXmpp::Sce::generatePadding()); - envelope.appendChild(rpad); + auto rpad = createElement("rpad"); + rpad.setValue(QXmpp::Sce::generatePadding()); + envelope.appendChild(rpad); - return envelope; + return envelope; } QXmppElement QXmpp::Omemo::MessageKey::toXml() const { - auto result = createElement("key"); + auto result = createElement("key"); - result.setAttribute("rid", QString::number(receivingDeviceId)); - if (prekey) - result.setAttribute("prekey", "true"); + result.setAttribute("rid", QString::number(receivingDeviceId)); + if (prekey) + result.setAttribute("prekey", "true"); - result.setValue(key); + result.setValue(key); - return result; + return result; } diff --git a/qomemo/qomemo.h b/qomemo/qomemo.h index b79ef1b..1e0c71a 100644 --- a/qomemo/qomemo.h +++ b/qomemo/qomemo.h @@ -11,32 +11,32 @@ namespace QXmpp::Omemo { -class MessageKey { -public: - [[nodiscard]] QXmppElement toXml() const; + class MessageKey { + public: + [[nodiscard]] QXmppElement toXml() const; - int receivingDeviceId{}; - bool prekey{}; - QString key{}; -}; + int receivingDeviceId{}; + bool prekey{}; + QString key{}; + }; -class EncryptedMessage { -public: - [[nodiscard]] QXmppElement header() const; - [[nodiscard]] QXmppElement content() const; - [[nodiscard]] QXmppElement toXml() const; - [[nodiscard]] QXmppElement payload() const; + class EncryptedMessage { + public: + [[nodiscard]] QXmppElement header() const; + [[nodiscard]] QXmppElement content() const; + [[nodiscard]] QXmppElement toXml() const; + [[nodiscard]] QXmppElement payload() const; - int fromDeviceId{}; + int fromDeviceId{}; - QList<MessageKey> keys{}; - QString from{}; - QString to{}; - QDateTime timestamp{}; + QList<MessageKey> keys{}; + QString from{}; + QString to{}; + QDateTime timestamp{}; - QString iv{}; + QString iv{}; - QXmppMessage message{}; -}; + QXmppMessage message{}; + }; } // namespace QXmpp::Omemo diff --git a/qomemo/qxmpp_omemo_manager.cpp b/qomemo/qxmpp_omemo_manager.cpp index 062b934..844a189 100644 --- a/qomemo/qxmpp_omemo_manager.cpp +++ b/qomemo/qxmpp_omemo_manager.cpp @@ -16,60 +16,59 @@ using namespace QXmpp::Omemo; Manager::Manager() - : deviceService(new DeviceService(this)), - omemoVariant(new Variant::Conversations) { - connect(this, &Manager::deviceListReceived, deviceService.get(), - &DeviceService::onDeviceListReceived); + : deviceService(new DeviceService(this)), + omemoVariant(new Variant::Conversations) { + connect(this, &Manager::deviceListReceived, deviceService.get(), &DeviceService::onDeviceListReceived); } bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) { - QString str{}; - QTextStream info(&str); - stanza.save(info, 4); + QString str{}; + QTextStream info(&str); + stanza.save(info, 4); - std::cout << str.toStdString(); + std::cout << str.toStdString(); - if (stanza.tagName() == "iq") { - if (stanza.attribute("type") == "result") { - auto pubsub = stanza.firstChildElement("pubsub"); - if (!pubsub.isNull()) { - auto items = pubsub.firstChildElement("items"); - if (items.attribute("node") == - "eu.siacs.conversations.axolotl.devicelist") { - auto item = items.firstChildElement("item"); - if (!item.isNull()) { - auto list = item.firstChildElement("list"); - if (!list.isNull()) { - DeviceList deviceList = omemoVariant->deviceListFromXml(list); - emit deviceListReceived(stanza.attribute("from"), deviceList); + if (stanza.tagName() == "iq") { + if (stanza.attribute("type") == "result") { + auto pubsub = stanza.firstChildElement("pubsub"); + if (!pubsub.isNull()) { + auto items = pubsub.firstChildElement("items"); + if (items.attribute("node") == + "eu.siacs.conversations.axolotl.devicelist") { + auto item = items.firstChildElement("item"); + if (!item.isNull()) { + auto list = item.firstChildElement("list"); + if (!list.isNull()) { + DeviceList deviceList = omemoVariant->deviceListFromXml(list); + emit deviceListReceived(stanza.attribute("from"), deviceList); - return true; + return true; + } + } + } } - } } - } } - } - return false; + return false; } void QXmpp::Omemo::Manager::setClient(QXmppClient *client) { - QXmppClientExtension::setClient(client); + QXmppClientExtension::setClient(client); - if (!client) - return; + if (!client) + return; - QObject::connect(client, &QXmppClient::connected, this, - &Manager::fetchOwnDevices); + QObject::connect(client, &QXmppClient::connected, this, + &Manager::fetchOwnDevices); } void QXmpp::Omemo::Manager::fetchOwnDevices() { - QXmppPubSubIq iq{}; - iq.setFrom(client()->configuration().jid()); - iq.setTo(client()->configuration().jidBare()); - iq.setType(QXmppIq::Get); - iq.setQueryNode("eu.siacs.conversations.axolotl.devicelist"); + QXmppPubSubIq iq{}; + iq.setFrom(client()->configuration().jid()); + iq.setTo(client()->configuration().jidBare()); + iq.setType(QXmppIq::Get); + iq.setQueryNode("eu.siacs.conversations.axolotl.devicelist"); - client()->sendPacket(iq); + client()->sendPacket(iq); } diff --git a/qomemo/qxmpp_omemo_manager.h b/qomemo/qxmpp_omemo_manager.h index 377c77d..aae7467 100644 --- a/qomemo/qxmpp_omemo_manager.h +++ b/qomemo/qxmpp_omemo_manager.h @@ -12,27 +12,27 @@ namespace QXmpp::Omemo { -class Manager : public QXmppClientExtension { - Q_OBJECT; + class Manager : public QXmppClientExtension { + Q_OBJECT; -public: - Manager(); - ~Manager() override = default; + public: + Manager(); + ~Manager() override = default; - bool handleStanza(const QDomElement &stanza) override; + bool handleStanza(const QDomElement &stanza) override; -public slots: - void fetchOwnDevices(); + public slots: + void fetchOwnDevices(); -signals: - void deviceListReceived(const QString &jid, const DeviceList &list); + signals: + void deviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list); -protected: - void setClient(QXmppClient *client) override; + protected: + void setClient(QXmppClient *client) override; -private: - QScopedPointer<DeviceService> deviceService; - QScopedPointer<Variant::Base> omemoVariant; -}; + private: + QScopedPointer<DeviceService> deviceService; + QScopedPointer<Variant::Base> omemoVariant; + }; } // namespace QXmpp::Omemo diff --git a/qomemo/sce.cpp b/qomemo/sce.cpp index decb8b3..b94cede 100644 --- a/qomemo/sce.cpp +++ b/qomemo/sce.cpp @@ -6,19 +6,21 @@ #include <QRandomGenerator> +#define RPAD_ALPHABET "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + constexpr int RPAD_MAX_LENGTH = 200; QString QXmpp::Sce::generatePadding() { - QRandomGenerator random{}; - QString result{}; - QString alphabet{ QStringLiteral("!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~") }; + QRandomGenerator random{}; + QString result{}; + QString alphabet{QStringLiteral(RPAD_ALPHABET)}; - auto length = random.bounded(RPAD_MAX_LENGTH); - result.resize(length); + auto length = random.bounded(RPAD_MAX_LENGTH); + result.resize(length); - for (auto i = 0; i < length; ++i) { - result[i] = alphabet[random.bounded(alphabet.length())]; - } + for (auto i = 0; i < length; ++i) { + result[i] = alphabet[random.bounded(alphabet.length())]; + } - return result; + return result; } diff --git a/qomemo/sce.h b/qomemo/sce.h index 91a7b2c..497aa3d 100644 --- a/qomemo/sce.h +++ b/qomemo/sce.h @@ -9,6 +9,6 @@ namespace QXmpp::Sce { -QString generatePadding(); + QString generatePadding(); } diff --git a/qomemo/signal/stores/signed_pre_key_store.cpp b/qomemo/signal/stores/signed_pre_key_store.cpp index 0156894..43bae1e 100644 --- a/qomemo/signal/stores/signed_pre_key_store.cpp +++ b/qomemo/signal/stores/signed_pre_key_store.cpp @@ -35,7 +35,8 @@ int Signal::Store::SignedPreKeyStore::loadSignedPreKey(signal_buffer **record, u return 0; } -int Signal::Store::SignedPreKeyStore::storeSignedPreKey(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len) { +int +Signal::Store::SignedPreKeyStore::storeSignedPreKey(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len) { return 0; } diff --git a/qomemo/user_device_list.cpp b/qomemo/user_device_list.cpp index 9a3f18a..5910ed2 100644 --- a/qomemo/user_device_list.cpp +++ b/qomemo/user_device_list.cpp @@ -6,5 +6,4 @@ #include <utility> -QXmpp::Omemo::UserDeviceList::UserDeviceList(QString jid) - : jid(std::move(jid)) {} +QXmpp::Omemo::UserDeviceList::UserDeviceList(QString jid) : jid(std::move(jid)) {} diff --git a/qomemo/user_device_list.h b/qomemo/user_device_list.h index 27e407a..00465aa 100644 --- a/qomemo/user_device_list.h +++ b/qomemo/user_device_list.h @@ -8,11 +8,11 @@ namespace QXmpp::Omemo { -class UserDeviceList { -public: - explicit UserDeviceList(QString jid); + class UserDeviceList { + public: + explicit UserDeviceList(QString jid); - const QString jid; -}; + const QString jid; + }; } // namespace QXmpp::Omemo diff --git a/qomemo/variant/conversations.cpp b/qomemo/variant/conversations.cpp index b2f85c4..4fc6af6 100644 --- a/qomemo/variant/conversations.cpp +++ b/qomemo/variant/conversations.cpp @@ -13,66 +13,66 @@ using namespace QXmpp::Omemo; using namespace QXmpp::Factories; QXmppElement Variant::Conversations::deviceToXml(const Device &device) { - auto result = createElement("device"); - result.setAttribute("id", QString::number(device.id)); + auto result = createElement("device"); + result.setAttribute("id", QString::number(device.id)); - return result; + return result; } Device Variant::Conversations::deviceFromXml(const QXmppElement &xml) { - Device result{}; + Device result{}; + + if (!elementMatches(xml, "device")) + return result; + + result.id = xml.attribute("id").toInt(); - if (!elementMatches(xml, "device")) return result; - - result.id = xml.attribute("id").toInt(); - - return result; } QXmppElement Variant::Conversations::deviceListToXml(const DeviceList &deviceList) { - auto element = createElement("list", "eu.siacs.conversations.axolotl"); + auto element = createElement("list", "eu.siacs.conversations.axolotl"); - for (const auto &device : deviceList.devices) { - element.appendChild(deviceToXml(device)); - } + for (const auto &device : deviceList.devices) { + element.appendChild(deviceToXml(device)); + } - return element; + return element; } DeviceList Variant::Conversations::deviceListFromXml(const QXmppElement &xml) { - DeviceList result{}; + DeviceList result{}; + + if (!elementMatches(xml, "list", "eu.siacs.conversations.axolotl")) + return result; + + auto deviceElement = xml.firstChildElement("device"); + while (!deviceElement.isNull()) { + result.devices.push_back(deviceFromXml(deviceElement)); + deviceElement = deviceElement.nextSiblingElement("device"); + } - if (!elementMatches(xml, "list", "eu.siacs.conversations.axolotl")) return result; - - auto deviceElement = xml.firstChildElement("device"); - while (!deviceElement.isNull()) { - result.devices.push_back(deviceFromXml(deviceElement)); - deviceElement = deviceElement.nextSiblingElement("device"); - } - - return result; } QXmppIq Variant::Conversations::deviceListSetIq(const DeviceList &deviceList) { - QXmppIq iq{}; + QXmppIq iq{}; - iq.setType(QXmppIq::Set); + iq.setType(QXmppIq::Set); - auto item = createElement("item"); - item.appendChild(deviceListToXml(deviceList)); + auto item = createElement("item"); + item.appendChild(deviceListToXml(deviceList)); - auto publish = createElement("publish"); - publish.setAttribute("node", "eu.siacs.conversations.axolotl.devicelist"); - publish.appendChild(item); + auto publish = createElement("publish"); + publish.setAttribute("node", "eu.siacs.conversations.axolotl.devicelist"); + publish.appendChild(item); - auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); - pubSub.appendChild(publish); - pubSub.appendChild(createOpenPublishOptions()); + auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); + pubSub.appendChild(publish); + pubSub.appendChild(createOpenPublishOptions()); - iq.extensions().push_back(pubSub); + iq.extensions().push_back(pubSub); - return iq; + return iq; } diff --git a/qomemo/variant/conversations.h b/qomemo/variant/conversations.h index 917dbcd..923e29b 100644 --- a/qomemo/variant/conversations.h +++ b/qomemo/variant/conversations.h @@ -8,16 +8,16 @@ namespace QXmpp::Omemo::Variant { -class Conversations : public Base { -public: - ~Conversations() override = default; + class Conversations : public Base { + public: + ~Conversations() override = default; - QXmppElement deviceToXml(const Device &device) override; - Device deviceFromXml(const QXmppElement &xml) override; + QXmppElement deviceToXml(const Device &device) override; + Device deviceFromXml(const QXmppElement &xml) override; - QXmppElement deviceListToXml(const DeviceList &deviceList) override; - DeviceList deviceListFromXml(const QXmppElement &xml) override; - QXmppIq deviceListSetIq(const DeviceList &deviceList) override; -}; + QXmppElement deviceListToXml(const DeviceList &deviceList) override; + DeviceList deviceListFromXml(const QXmppElement &xml) override; + QXmppIq deviceListSetIq(const DeviceList &deviceList) override; + }; } // namespace QXmpp::Omemo::Variant diff --git a/qomemo/variant/omemo_base.h b/qomemo/variant/omemo_base.h index fd11f31..58910fe 100644 --- a/qomemo/variant/omemo_base.h +++ b/qomemo/variant/omemo_base.h @@ -5,27 +5,29 @@ #pragma once class QXmppElement; + class QXmppIq; namespace QXmpp::Omemo { -class Device; -class DeviceList; + class Device; -namespace Variant { + class DeviceList; -class Base { -public: - virtual ~Base() = default; + namespace Variant { - virtual QXmppElement deviceToXml(const Device &device) = 0; - virtual Device deviceFromXml(const QXmppElement &xml) = 0; + class Base { + public: + virtual ~Base() = default; - virtual QXmppElement deviceListToXml(const DeviceList &deviceList) = 0; - virtual DeviceList deviceListFromXml(const QXmppElement &xml) = 0; - virtual QXmppIq deviceListSetIq(const DeviceList &deviceList) = 0; -}; + virtual QXmppElement deviceToXml(const Device &device) = 0; + virtual Device deviceFromXml(const QXmppElement &xml) = 0; -} // namespace Variant + virtual QXmppElement deviceListToXml(const DeviceList &deviceList) = 0; + virtual DeviceList deviceListFromXml(const QXmppElement &xml) = 0; + virtual QXmppIq deviceListSetIq(const DeviceList &deviceList) = 0; + }; + + } // namespace Variant } // namespace QXmpp::Omemo From bb2ce750c87aff7cd5846c2c61b28f9794374524 Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Sat, 15 May 2021 00:48:32 +0300 Subject: [PATCH 17/20] build: add OpenSSL dependency --- CMakeLists.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aafc412..9ca13cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ option(WITH_KIO "Build KIO support module" ON) # Dependencies ## Qt find_package(Qt5 COMPONENTS Widgets DBus Gui Xml Network Core REQUIRED) +target_link_libraries(squawk PRIVATE Qt5::Core Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Gui Qt5::Xml) ## QXmpp if (SYSTEM_QXMPP) @@ -81,10 +82,13 @@ endif () ## LMDB find_package(LMDB REQUIRED) - -# Linking -target_link_libraries(squawk PRIVATE Qt5::Core Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Gui Qt5::Xml) target_link_libraries(squawk PRIVATE lmdb) + +# OpenSSL +find_package(OpenSSL REQUIRED) +target_link_libraries(squawk PRIVATE OpenSSL::Crypto) + +# Misc target_link_libraries(squawk PRIVATE simpleCrypt) target_link_libraries(squawk PRIVATE uuid) From 08fe37bfb283f2f2673a9b11ab8e7ae3cfc70a8f Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Sat, 15 May 2021 12:28:43 +0300 Subject: [PATCH 18/20] feat(omemo): basic UI --- ui/omemo/CMakeLists.txt | 11 ++- ui/omemo/contactsettings.cpp | 23 ++++++ ui/omemo/contactsettings.h | 24 ++++++ ui/omemo/contactsettings.ui | 144 +++++++++++++++++++++++++++++++++++ ui/omemo/omemodevices.cpp | 4 +- ui/omemo/omemodevices.h | 10 +-- ui/widgets/conversation.cpp | 11 ++- ui/widgets/conversation.h | 1 + ui/widgets/conversation.ui | 7 +- 9 files changed, 220 insertions(+), 15 deletions(-) create mode 100644 ui/omemo/contactsettings.cpp create mode 100644 ui/omemo/contactsettings.h create mode 100644 ui/omemo/contactsettings.ui diff --git a/ui/omemo/CMakeLists.txt b/ui/omemo/CMakeLists.txt index 41afa04..b775e83 100644 --- a/ui/omemo/CMakeLists.txt +++ b/ui/omemo/CMakeLists.txt @@ -1,5 +1,8 @@ target_sources(squawk PRIVATE - omemodevices.cpp - omemodevices.h - omemodevices.ui - ) \ No newline at end of file + contactsettings.cpp + contactsettings.h + contactsettings.ui + omemodevices.cpp + omemodevices.h + omemodevices.ui + ) \ No newline at end of file diff --git a/ui/omemo/contactsettings.cpp b/ui/omemo/contactsettings.cpp new file mode 100644 index 0000000..c4d20b9 --- /dev/null +++ b/ui/omemo/contactsettings.cpp @@ -0,0 +1,23 @@ +/* + * Created by victoria on 2021-05-15. +*/ + +#include "contactsettings.h" +#include "ui_contactsettings.h" +#include "omemodevices.h" + +ContactSettings::ContactSettings(QWidget *parent) + : QDialog(parent), m_ui(new Ui::ContactSettings()) { + m_ui->setupUi(this); + + connect(m_ui->devicesButton, &QPushButton::clicked, this, &ContactSettings::openDeviceList); +} + +ContactSettings::~ContactSettings() {} + +void ContactSettings::openDeviceList() { + auto devices = new OMEMODevices(this); + + devices->setAttribute(Qt::WA_DeleteOnClose); + devices->show(); +} diff --git a/ui/omemo/contactsettings.h b/ui/omemo/contactsettings.h new file mode 100644 index 0000000..4720c1f --- /dev/null +++ b/ui/omemo/contactsettings.h @@ -0,0 +1,24 @@ +/* + * Created by victoria on 2021-05-15. +*/ + +#pragma once + +#include <QDialog> + +namespace Ui { + class ContactSettings; +} + +class ContactSettings : public QDialog { + Q_OBJECT +public: + explicit ContactSettings(QWidget *parent = nullptr); + ~ContactSettings() override; + +private slots: + void openDeviceList(); + +private: + QScopedPointer<Ui::ContactSettings> m_ui; +}; diff --git a/ui/omemo/contactsettings.ui b/ui/omemo/contactsettings.ui new file mode 100644 index 0000000..5f7e77a --- /dev/null +++ b/ui/omemo/contactsettings.ui @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ContactSettings</class> + <widget class="QDialog" name="ContactSettings"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Contact Settings</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Settings for foo@example.com</string> + </property> + </widget> + </item> + <item> + <widget class="QFrame" name="frame"> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QWidget" name="widget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>0</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="QCheckBox" name="useOmemoCheckBox"> + <property name="text"> + <string>Use OMEMO</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="devicesButton"> + <property name="text"> + <string>Devices...</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ContactSettings</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ContactSettings</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/ui/omemo/omemodevices.cpp b/ui/omemo/omemodevices.cpp index e746f5b..877ff27 100644 --- a/ui/omemo/omemodevices.cpp +++ b/ui/omemo/omemodevices.cpp @@ -6,8 +6,8 @@ #include "ui_omemodevices.h" OMEMODevices::OMEMODevices(QWidget *parent) - : QDialog(parent), m_ui(new Ui::OMEMODevices()) { - m_ui->setupUi(this); + : QDialog(parent), m_ui(new Ui::OMEMODevices()) { + m_ui->setupUi(this); } OMEMODevices::~OMEMODevices() {} diff --git a/ui/omemo/omemodevices.h b/ui/omemo/omemodevices.h index b107baa..70dbb0f 100644 --- a/ui/omemo/omemodevices.h +++ b/ui/omemo/omemodevices.h @@ -7,15 +7,15 @@ #include <QDialog> namespace Ui { -class OMEMODevices; + class OMEMODevices; } class OMEMODevices : public QDialog { - Q_OBJECT + Q_OBJECT public: - explicit OMEMODevices(QWidget *parent = nullptr); - ~OMEMODevices() override; + explicit OMEMODevices(QWidget *parent = nullptr); + ~OMEMODevices() override; private: - QScopedPointer<Ui::OMEMODevices> m_ui; + QScopedPointer<Ui::OMEMODevices> m_ui; }; diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 45ce2c5..2b2547b 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -27,6 +27,7 @@ #include <unistd.h> #include <QAbstractTextDocumentLayout> #include <QCoreApplication> +#include <ui/omemo/contactsettings.h> Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent): QWidget(parent), @@ -80,7 +81,8 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::onClearButton); connect(m_ui->messageEditor->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged, this, &Conversation::onTextEditDocSizeChanged); - + connect(m_ui->encryptionButton, &QToolButton::clicked, this, &Conversation::openEncryptionSettings); + m_ui->messageEditor->installEventFilter(&ker); @@ -433,3 +435,10 @@ void Conversation::onFeedContext(const QPoint& pos) } } } + +void Conversation::openEncryptionSettings() { + auto cs = new ContactSettings(this); + + cs->setAttribute(Qt::WA_DeleteOnClose); + cs->show(); +} diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 0b0dcb2..5ec7acc 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -107,6 +107,7 @@ protected slots: void onFeedMessage(const Shared::Message& msg); void positionShadow(); void onFeedContext(const QPoint &pos); + void openEncryptionSettings(); public: const bool isMuc; diff --git a/ui/widgets/conversation.ui b/ui/widgets/conversation.ui index 36b74a4..e9bcbf8 100644 --- a/ui/widgets/conversation.ui +++ b/ui/widgets/conversation.ui @@ -293,14 +293,15 @@ </spacer> </item> <item> - <widget class="QPushButton" name="encryptionButton"> + <widget class="QToolButton" name="encryptionButton"> <property name="text"> <string/> </property> <property name="icon"> - <iconset theme="security-low"/> + <iconset theme="security-low"> + <normaloff>.</normaloff>.</iconset> </property> - <property name="flat"> + <property name="autoRaise"> <bool>true</bool> </property> </widget> From 442ad37300d3112ece31a005ba4689f21957ae9c Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Thu, 22 Jul 2021 20:45:39 +0300 Subject: [PATCH 19/20] feat: omemo signal lib wip --- qomemo/CMakeLists.txt | 2 + qomemo/bundle.cpp | 124 ---------- qomemo/bundle.h | 19 +- qomemo/database.cpp | 66 +++++- qomemo/database.h | 28 ++- qomemo/device_service.cpp | 24 +- qomemo/device_service.h | 11 +- qomemo/key.cpp | 6 + qomemo/key.h | 27 +++ qomemo/qxmpp_omemo_manager.cpp | 236 ++++++++++++++++++-- qomemo/qxmpp_omemo_manager.h | 25 ++- qomemo/signal/CMakeLists.txt | 2 + qomemo/signal/context.cpp | 17 +- qomemo/signal/context.h | 8 + qomemo/signal/crypto/CMakeLists.txt | 2 + qomemo/signal/crypto/ec.cpp | 5 + qomemo/signal/crypto/ec.h | 20 ++ qomemo/signal/stores/identity_key_store.cpp | 73 +++--- qomemo/signal/stores/identity_key_store.h | 13 +- qomemo/signal/stores/pre_key_store.cpp | 41 ++-- qomemo/signal/stores/pre_key_store.h | 9 +- qomemo/signal/util.cpp | 14 ++ qomemo/signal/util.h | 18 ++ qomemo/variant/conversations.cpp | 139 +++++++++++- qomemo/variant/conversations.h | 11 +- qomemo/variant/omemo_base.h | 18 +- ui/omemo/contactsettings.cpp | 8 +- ui/omemo/contactsettings.h | 4 +- ui/omemo/omemodevices.cpp | 6 +- ui/omemo/omemodevices.h | 4 +- ui/squawk.cpp | 10 +- ui/squawk.h | 2 +- ui/squawk.ui | 13 +- ui/widgets/conversation.cpp | 2 +- 34 files changed, 745 insertions(+), 262 deletions(-) create mode 100644 qomemo/key.cpp create mode 100644 qomemo/key.h create mode 100644 qomemo/signal/crypto/ec.cpp create mode 100644 qomemo/signal/crypto/ec.h create mode 100644 qomemo/signal/util.cpp create mode 100644 qomemo/signal/util.h diff --git a/qomemo/CMakeLists.txt b/qomemo/CMakeLists.txt index 8b194be..7c71af1 100644 --- a/qomemo/CMakeLists.txt +++ b/qomemo/CMakeLists.txt @@ -9,6 +9,8 @@ target_sources(squawk PRIVATE device_key_storage.h device_service.cpp device_service.h + key.cpp + key.h qomemo.cpp qomemo.h sce.cpp diff --git a/qomemo/bundle.cpp b/qomemo/bundle.cpp index 691a65e..d27cd1e 100644 --- a/qomemo/bundle.cpp +++ b/qomemo/bundle.cpp @@ -3,127 +3,3 @@ */ #include "bundle.h" - -#include <QXmppPubSubIq.h> - -#include <QDebug> - -#include "shared/qxmppfactories.h" - -using namespace QXmpp::Factories; - -QXmppElement QXmpp::Omemo::PreKey::toXml() const { - auto pk = createElement("preKeyPublic"); - pk.setAttribute("preKeyId", QString::number(id)); - // TODO: Base64 - pk.setValue(data); - - return pk; -} - -void QXmpp::Omemo::PreKey::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "preKeyPublic")) - return; - - id = element.attribute("preKeyId").toInt(); - // TODO: Base64 - data = element.value(); -} - -QXmppElement QXmpp::Omemo::Bundle::toXml() const { - auto spkNode = createElement("signedPreKeyPublic"); - spkNode.setAttribute("signedPreKeyId", QString::number(spkId)); - spkNode.setValue(spk); - - auto spksNode = createElement("signedPreKeySignature"); - spksNode.setValue(spks); - - auto ikNode = createElement("identityKey"); - ikNode.setValue(ik); - - auto prekeysNode = createElement("prekeys"); - for (const auto &pk : prekeys) { - prekeysNode.appendChild(pk.toXml()); - } - - auto result = createElement("bundle", "eu.siacs.conversations.axolotl"); - result.appendChild(spkNode); - result.appendChild(spksNode); - result.appendChild(ikNode); - result.appendChild(prekeysNode); - - return result; -} - -QXmppIq QXmpp::Omemo::Bundle::toIq(int deviceId) const { - QXmppIq iq{}; - - iq.setType(QXmppIq::Set); - - auto item = createElement("item"); - item.appendChild(toXml()); - - auto publish = createElement("publish"); - publish.setAttribute( - "node", - QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId)); - publish.appendChild(item); - - auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); - pubSub.appendChild(publish); - pubSub.appendChild(createOpenPublishOptions()); - - iq.extensions().push_back(pubSub); - - return iq; -} - -void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "bundle", "eu.siacs.conversations.axolotl")) - return; - - auto spkNode = element.firstChildElement("spk"); - if (spkNode.isNull()) { - qWarning() << "'bundle': missing 'spk'"; - return; - } - spk = spkNode.value(); - spkId = spkNode.attribute("id").toInt(); - - auto spksNode = element.firstChildElement("spks"); - if (spksNode.isNull()) { - qWarning() << "'bundle': missing 'spks'"; - return; - } - spks = spksNode.value(); - - auto ikNode = element.firstChildElement("ik"); - if (ikNode.isNull()) { - qWarning() << "'bundle': missing 'ik'"; - return; - } - ik = ikNode.value(); - - auto prekeysNode = element.firstChildElement("prekeys"); - auto pkNode = prekeysNode.firstChildElement("pk"); - - prekeys.clear(); - while (!pkNode.isNull()) { - PreKey pk{}; - pk.fromXml(pkNode); - prekeys.push_back(pk); - } -} - -QXmppPubSubIq QXmpp::Omemo::Bundle::fetchDeviceBundleIq(int deviceId) { - QXmppPubSubIq iq{}; - iq.setType(QXmppIq::Get); - iq.setQueryNode( - QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId)); - - QXmppPubSubItem item{}; - item.setId(QString::number(deviceId)); - iq.setItems({item}); - - return iq; -} \ No newline at end of file diff --git a/qomemo/bundle.h b/qomemo/bundle.h index 428277b..61e8185 100644 --- a/qomemo/bundle.h +++ b/qomemo/bundle.h @@ -6,6 +6,7 @@ #include <QList> #include <QString> +#include <QBuffer> class QXmppPubSubIq; @@ -17,26 +18,16 @@ namespace QXmpp::Omemo { class PreKey { public: - [[nodiscard]] QXmppElement toXml() const; - /// Expects a <pk> - void fromXml(const QXmppElement &element); - int id; - QString data; + QByteArray data; }; class Bundle { public: - [[nodiscard]] static QXmppPubSubIq fetchDeviceBundleIq(int deviceId); - - [[nodiscard]] QXmppElement toXml() const; - [[nodiscard]] QXmppIq toIq(int deviceId) const; - void fromXml(const QXmppElement &element); - - QString spk; + QByteArray spk; int spkId; - QString spks; - QString ik; + QByteArray spks; + QByteArray ik; QList<PreKey> prekeys; }; diff --git a/qomemo/database.cpp b/qomemo/database.cpp index 2314a80..2923634 100644 --- a/qomemo/database.cpp +++ b/qomemo/database.cpp @@ -4,6 +4,8 @@ #include "database.h" +#include "bundle.h" + #include <QDebug> #include <QDir> #include <QException> @@ -44,17 +46,17 @@ Database::~Database() { mdb_env_close(env); } -QBuffer Database::loadIdentityKeySecret(int deviceId) { return QBuffer(); } +std::optional<QByteArray> Database::loadIdentityKeySecret(int deviceId) { return std::nullopt; } bool Database::saveIdentityKeySecret(int deviceId, - const QBuffer &identityKeySecret) { + const QByteArray &identityKeySecret) { MDB_val mdbKey, mdbValue; auto key = QString("%1/secret").arg(QString::number(deviceId)).toStdString(); mdbKey.mv_data = key.data(); mdbKey.mv_size = key.size(); - mdbValue.mv_data = const_cast<char *>(identityKeySecret.data().data()); + mdbValue.mv_data = const_cast<char *>(identityKeySecret.data()); mdbValue.mv_size = identityKeySecret.size(); MDB_txn *txn; @@ -71,7 +73,7 @@ bool Database::saveIdentityKeySecret(int deviceId, return false; } -int Database::loadActiveDeviceId() { +std::optional<int> Database::loadActiveDeviceId() { MDB_val key, value; key.mv_data = (void *) "active"; @@ -83,12 +85,14 @@ int Database::loadActiveDeviceId() { auto err = mdb_get(txn, dbiIdentityKeys, &key, &value); if (err) { qWarning() << "could not load active device id:" << mdb_strerror(err); - return 0; + mdb_txn_abort(txn); + return std::nullopt; } if (value.mv_size != sizeof(int)) { qWarning() << "mv_size is" << value.mv_size << "instead of" << sizeof(int); - return 0; + mdb_txn_abort(txn); + return std::nullopt; } auto id = *reinterpret_cast<int *>(value.mv_data); @@ -124,3 +128,53 @@ bool Database::saveActiveDeviceId(int deviceId) { return true; } + +bool Database::saveIdentityKey(int deviceId, const QByteArray &identityKey) { + return false; +} + +std::optional<QByteArray> Database::loadIdentityKey(int deviceId) { + return std::nullopt; +} + +bool Database::containsPreKey() { + return false; +} + +std::optional<KeyPair> Database::loadPreKey(int deviceId, int id) { + return std::nullopt; +} + +bool Database::savePreKey(int deviceId, int id, const KeyPair &preKey) { + return false; +} + +std::optional<Bundle> Database::loadBundle(int deviceId) { + Bundle result{}; + + auto ik = loadIdentityKey(deviceId); + + result.ik = ik.value(); + + auto spk = loadSignedPreKey(deviceId); + + result.spk = spk->key.publicKey; + result.spks = spk->signature; + result.spkId = spk->id; + + // PreKeys + + return result; +} + +bool Database::saveBundle(int deviceId, const Bundle &bundle) { + return false; +} + +std::optional<SignedPreKey> Database::loadSignedPreKey(int deviceId) { + return std::optional<SignedPreKey>(); +} + +bool Database::saveSignedPreKey(int deviceId, const SignedPreKey &signedPreKey) { + return false; +} diff --git a/qomemo/database.h b/qomemo/database.h index 3f9b933..1edeea6 100644 --- a/qomemo/database.h +++ b/qomemo/database.h @@ -8,8 +8,12 @@ #include <QString> #include <lmdb.h> +#include "key.h" + namespace QXmpp::Omemo { + class Bundle; + class Database { public: explicit Database(QString jid); @@ -18,14 +22,26 @@ namespace QXmpp::Omemo { Database(Database &&) = delete; Database &operator=(const Database &) = delete; - QBuffer loadIdentityKey(); - bool saveIdentityKey(const QBuffer &identityKey); - - int loadActiveDeviceId(); + // For local user + std::optional<int> loadActiveDeviceId(); bool saveActiveDeviceId(int deviceId); - QBuffer loadIdentityKeySecret(int deviceId); - bool saveIdentityKeySecret(int deviceId, const QBuffer &identityKeySecret); + std::optional<QByteArray> loadIdentityKeySecret(int deviceId); + bool saveIdentityKeySecret(int deviceId, const QByteArray &identityKeySecret); + + std::optional<Bundle> loadBundle(int deviceId); + bool saveBundle(int deviceId, const Bundle& bundle); + + // For any user + std::optional<QByteArray> loadIdentityKey(int deviceId); + bool saveIdentityKey(int deviceId, const QByteArray &identityKey); + + bool containsPreKey(); + std::optional<KeyPair> loadPreKey(int deviceId, int id); + bool savePreKey(int deviceId, int id, const KeyPair &preKey); + + std::optional<SignedPreKey> loadSignedPreKey(int deviceId); + bool saveSignedPreKey(int deviceId, const SignedPreKey& signedPreKey); const QString jid; diff --git a/qomemo/device_service.cpp b/qomemo/device_service.cpp index 306f383..653a3a6 100644 --- a/qomemo/device_service.cpp +++ b/qomemo/device_service.cpp @@ -5,11 +5,31 @@ #include "device_service.h" #include "device.h" -QXmpp::Omemo::DeviceService::DeviceService(QObject *parent) : QObject(parent) {} +using namespace QXmpp::Omemo; -void QXmpp::Omemo::DeviceService::onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list) { +DeviceService::DeviceService(QObject *parent) : QObject(parent) {} + +void DeviceService::onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list) { for (const auto &device : list.devices) { qInfo() << "Got device for" << jid << ":" << device.id; } } + +QSharedPointer<Database> DeviceService::getDatabase(const QString &jid) { + if (!databases.contains(jid)) { + databases.insert(jid, QSharedPointer<Database>::create(jid)); + } + + return databases[jid]; +} + +void DeviceService::addIdentity(const QString &jid, int deviceId, const QByteArray& publicKey) { + auto db = getDatabase(jid); + + db->saveIdentityKey(deviceId, publicKey); +} + +void DeviceService::onDeviceListNotFound(const QString &jid) { + qInfo() << "Device list not found:" << jid; +} diff --git a/qomemo/device_service.h b/qomemo/device_service.h index a9529c4..2442486 100644 --- a/qomemo/device_service.h +++ b/qomemo/device_service.h @@ -4,8 +4,11 @@ #pragma once +#include <QBuffer> + #include "qomemo.h" #include "user_device_list.h" +#include "database.h" #include <QXmppClient.h> @@ -19,11 +22,17 @@ namespace QXmpp::Omemo { public: explicit DeviceService(QObject *parent); + QSharedPointer<Database> getDatabase(const QString& jid); + public slots: + void addIdentity(const QString& jid, int deviceId, const QByteArray& publicKey); + void onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list); + void onDeviceListNotFound(const QString &jid); + private: - QMap<QString, UserDeviceList> device_lists{}; + QMap<QString, QSharedPointer<Database>> databases{}; }; } // namespace QXmpp::Omemo diff --git a/qomemo/key.cpp b/qomemo/key.cpp new file mode 100644 index 0000000..7e9cc30 --- /dev/null +++ b/qomemo/key.cpp @@ -0,0 +1,6 @@ +/* + * Created by victoria on 2021-05-15. +*/ + +#include "key.h" + \ No newline at end of file diff --git a/qomemo/key.h b/qomemo/key.h new file mode 100644 index 0000000..a0910c7 --- /dev/null +++ b/qomemo/key.h @@ -0,0 +1,27 @@ +/* + * Created by victoria on 2021-05-15. +*/ + +#pragma once + +#include <optional> + +#include <QByteArray> + +namespace QXmpp::Omemo { + + class KeyPair { + public: + QByteArray publicKey{}; + std::optional<QByteArray> secretKey{ std::nullopt }; + }; + + class SignedPreKey { + public: + int id{ 0 }; + + KeyPair key{}; + QByteArray signature{}; + }; + +} diff --git a/qomemo/qxmpp_omemo_manager.cpp b/qomemo/qxmpp_omemo_manager.cpp index 844a189..788fc96 100644 --- a/qomemo/qxmpp_omemo_manager.cpp +++ b/qomemo/qxmpp_omemo_manager.cpp @@ -4,6 +4,9 @@ #include "qxmpp_omemo_manager.h" +#include "qomemo/signal/context.h" + +#include "bundle.h" #include "device.h" #include "variant/conversations.h" @@ -12,13 +15,24 @@ #include <QXmppClient.h> #include <QXmppPubSubIq.h> #include <iostream> +#include <QRandomGenerator64> +#include <external/signal-protocol-c/src/signal_protocol_internal.h> using namespace QXmpp::Omemo; Manager::Manager() - : deviceService(new DeviceService(this)), + : deviceService{new DeviceService(this)}, omemoVariant(new Variant::Conversations) { connect(this, &Manager::deviceListReceived, deviceService.get(), &DeviceService::onDeviceListReceived); + connect(this, &Manager::deviceListNotFound, deviceService.get(), &DeviceService::onDeviceListNotFound); + connect(this, &Manager::deviceListNotFound, this, [this](const QString &jid) { + if (jid == client()->configuration().jidBare()) + generateDeviceListForSelf(); + }); + connect(this, &Manager::deviceListReceived, this, [this](const QString &jid, const DeviceList ¤tList) { + if (jid == client()->configuration().jidBare()) + generateDeviceForSelfIfNeeded(currentList); + }); } bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) { @@ -28,31 +42,62 @@ bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) { std::cout << str.toStdString(); - if (stanza.tagName() == "iq") { - if (stanza.attribute("type") == "result") { - auto pubsub = stanza.firstChildElement("pubsub"); - if (!pubsub.isNull()) { - auto items = pubsub.firstChildElement("items"); - if (items.attribute("node") == - "eu.siacs.conversations.axolotl.devicelist") { - auto item = items.firstChildElement("item"); - if (!item.isNull()) { - auto list = item.firstChildElement("list"); - if (!list.isNull()) { - DeviceList deviceList = omemoVariant->deviceListFromXml(list); - emit deviceListReceived(stanza.attribute("from"), deviceList); - - return true; - } - } - } - } - } - } + if (handleDeviceList(stanza) || handleMissingDeviceList(stanza)) + return true; return false; } +bool Manager::handleDeviceList(const QDomElement &stanza) { + if (!(stanza.tagName() == "iq")) + return false; + + if (!(stanza.attribute("type") == "result")) + return false; + + auto pubsub = stanza.firstChildElement("pubsub"); + if (pubsub.isNull()) + return false; + + auto items = pubsub.firstChildElement("items"); + if (!(items.attribute("node") == omemoVariant->getDeviceListNode())) + return false; + + auto deviceList = omemoVariant->latestDeviceListFromPubSubNode(items); + if (!deviceList.has_value()) + return false; + + emit deviceListReceived(stanza.attribute("from"), deviceList.value()); + + return true; +} + +bool Manager::handleMissingDeviceList(const QDomElement &stanza) { + if (stanza.tagName() != "iq") + return false; + + if (stanza.attribute("type") != "error") + return false; + + auto pubsub = stanza.firstChildElement("pubsub"); + if (pubsub.isNull()) + return false; + + auto items = pubsub.firstChildElement("items"); + if (items.attribute("node") != omemoVariant->getDeviceListNode()) + return false; + + auto error = stanza.firstChildElement("error"); + + if (error.namespaceURI() != "jabber:client" || error.attribute("code") != "404") + return false; + + qDebug() << "Got 404 deviceList for" << stanza.attribute("from"); + emit deviceListNotFound(stanza.attribute("from")); + + return true; +} + void QXmpp::Omemo::Manager::setClient(QXmppClient *client) { QXmppClientExtension::setClient(client); @@ -68,7 +113,152 @@ void QXmpp::Omemo::Manager::fetchOwnDevices() { iq.setFrom(client()->configuration().jid()); iq.setTo(client()->configuration().jidBare()); iq.setType(QXmppIq::Get); - iq.setQueryNode("eu.siacs.conversations.axolotl.devicelist"); + iq.setQueryNode(omemoVariant->getDeviceListNode()); client()->sendPacket(iq); } + +QSharedPointer<DeviceService> Manager::getDeviceService() { + return deviceService; +} + +void Manager::publishDeviceList(const DeviceList &deviceList) { +// QXmppPubSubIq iq{}; +// iq.setFrom(client()->configuration().jid()); +// iq.setType(QXmppIq::Set); +// iq.setQueryNode(omemoVariant->getDeviceListNode()); +// iq.setItems() + + auto iq = omemoVariant->deviceListSetIq(deviceList); + iq.setFrom(client()->configuration().jid()); + + QString str{}; + QXmlStreamWriter info(&str); + iq.toXml(&info); + qInfo() << str; + + client()->sendPacket(iq); +} + +void Manager::generateDeviceListForSelf() { + qInfo() << "Generate device for self..."; + + generateDeviceForSelfIfNeeded(DeviceList()); +} + +void Manager::publishBundle(int deviceId, const Bundle &bundle) { + auto iq = omemoVariant->bundleSetIq(deviceId, bundle); + iq.setFrom(client()->configuration().jid()); + + QString str{}; + QXmlStreamWriter info(&str); + iq.toXml(&info); + qInfo() << str; + + client()->sendPacket(iq); +} + +void Manager::generateDeviceForSelfIfNeeded(const DeviceList ¤tList) { + auto db = deviceService->getDatabase(client()->configuration().jidBare()); + auto activeId = db->loadActiveDeviceId(); + + if (activeId.has_value()) { + qInfo() << "Current device:" << *activeId; + + bool found = false; + for (const auto &d : currentList.devices) + if (d.id == *activeId) + found = true; + + if (found) + return; + + qInfo() << "Could not find device" << *activeId << ", generating new one"; + } + + qInfo() << "Generating device"; + + auto deviceId = QRandomGenerator64{}.bounded(INT32_MAX); + db->saveActiveDeviceId(deviceId); + + Device device{}; + device.id = deviceId; + + auto updatedList = currentList; + + auto bundle = Bundle(); + + updatedList.devices.push_back(device); + + publishDeviceList(updatedList); + publishBundle(deviceId, bundle); +} + +Bundle Manager::generateAndSaveBundle(int deviceId) { + auto database = deviceService->getDatabase(client()->configuration().jidBare()); + Bundle result{}; + + ec_key_pair *ecKeyPair; + curve_generate_key_pair(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), &ecKeyPair); + + signal_buffer *ecPublic; + ec_public_key_serialize(&ecPublic, ec_key_pair_get_public(ecKeyPair)); + + signal_buffer *ecSecret; + ec_private_key_serialize(&ecSecret, ec_key_pair_get_private(ecKeyPair)); + + QByteArray identityKey((const char *) signal_buffer_const_data(ecPublic), (int) signal_buffer_len(ecPublic)); + QByteArray identityKeySecret((const char *) signal_buffer_const_data(ecSecret), (int) signal_buffer_len(ecSecret)); + + database->saveIdentityKey(deviceId, identityKey); + database->saveIdentityKeySecret(deviceId, identityKeySecret); + + // Generate SPK + ec_key_pair *ecSpk; + curve_generate_key_pair(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), &ecSpk); + signal_buffer *spkPublic, *spkSecret; + ec_public_key_serialize(&spkPublic, ec_key_pair_get_public(ecSpk)); + ec_private_key_serialize(&spkSecret, ec_key_pair_get_private(ecSpk)); + + // Generate SPKs + signal_buffer *signature; + curve_calculate_signature(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), &signature, + ec_key_pair_get_private(ecKeyPair), signal_buffer_const_data(spkPublic), + signal_buffer_len(spkPublic)); + + SignedPreKey spk{}; + spk.id = 0; + spk.key.publicKey = QByteArray((const char *) signal_buffer_const_data(spkPublic), (int) signal_buffer_len(spkPublic)); + spk.key.secretKey = QByteArray((const char *) signal_buffer_const_data(spkSecret), (int) signal_buffer_len(spkSecret)); + spk.signature = QByteArray((const char *) signal_buffer_const_data(signature), (int) signal_buffer_len(signature)); + + // Generate 100 PK + for (auto i = 1; i <= 100; ++i) { + ec_key_pair *currentPreKey; + curve_generate_key_pair(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), ¤tPreKey); + + signal_buffer *pkPublic, *pkSecret; + ec_public_key_serialize(&pkPublic, ec_key_pair_get_public(currentPreKey)); + ec_private_key_serialize(&pkSecret, ec_key_pair_get_private(currentPreKey)); + + KeyPair preKey{}; + preKey.publicKey = QByteArray((const char *) signal_buffer_const_data(pkPublic), (int) signal_buffer_len(pkPublic)); + preKey.secretKey = QByteArray((const char *) signal_buffer_const_data(pkSecret), (int) signal_buffer_len(pkSecret)); + + database->savePreKey(deviceId, i, preKey); + + signal_buffer_free(pkPublic); + signal_buffer_free(pkSecret); + SIGNAL_UNREF(currentPreKey); + } + + signal_buffer_free(signature); + + signal_buffer_free(ecPublic); + signal_buffer_free(ecSecret); + SIGNAL_UNREF(ecKeyPair); + + signal_buffer_free(spkPublic); + signal_buffer_free(spkSecret); + SIGNAL_UNREF(ecSpk); +} diff --git a/qomemo/qxmpp_omemo_manager.h b/qomemo/qxmpp_omemo_manager.h index aae7467..7f04026 100644 --- a/qomemo/qxmpp_omemo_manager.h +++ b/qomemo/qxmpp_omemo_manager.h @@ -10,6 +10,10 @@ #include <QXmppClientExtension.h> +namespace Signal { + class Context; +} + namespace QXmpp::Omemo { class Manager : public QXmppClientExtension { @@ -21,18 +25,37 @@ namespace QXmpp::Omemo { bool handleStanza(const QDomElement &stanza) override; + bool handleDeviceList(const QDomElement& stanza); + + bool handleMissingDeviceList(const QDomElement& stanza); + + QSharedPointer<DeviceService> getDeviceService(); + + Bundle generateAndSaveBundle(int deviceId); + public slots: void fetchOwnDevices(); + void publishDeviceList(const QXmpp::Omemo::DeviceList& deviceList); + + void generateDeviceListForSelf(); + + void generateDeviceForSelfIfNeeded(const QXmpp::Omemo::DeviceList ¤tList); + + void publishBundle(int deviceId, const QXmpp::Omemo::Bundle& bundle); + signals: void deviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list); + void deviceListNotFound(const QString &jid); + protected: void setClient(QXmppClient *client) override; private: - QScopedPointer<DeviceService> deviceService; + QSharedPointer<DeviceService> deviceService; QScopedPointer<Variant::Base> omemoVariant; + QScopedPointer<Signal::Context> signalContext; }; } // namespace QXmpp::Omemo diff --git a/qomemo/signal/CMakeLists.txt b/qomemo/signal/CMakeLists.txt index 60cfdd8..7223904 100644 --- a/qomemo/signal/CMakeLists.txt +++ b/qomemo/signal/CMakeLists.txt @@ -1,6 +1,8 @@ target_sources(squawk PRIVATE context.cpp context.h + util.cpp + util.h ) add_subdirectory(crypto) diff --git a/qomemo/signal/context.cpp b/qomemo/signal/context.cpp index bc2322e..6253faa 100644 --- a/qomemo/signal/context.cpp +++ b/qomemo/signal/context.cpp @@ -4,6 +4,19 @@ #include "context.h" -Signal::Context::Context() {} +using namespace Signal; -Signal::Context::~Context() {} +Context::Context() {} + +Context::~Context() {} + +std::unique_ptr<Crypto::ECKeyPair> Context::generateCurveKeyPair() { + auto result = std::unique_ptr<Crypto::ECKeyPair>(); + // TODO + + return result; +} + +signal_context *Context::temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped() { + return ctx; +} diff --git a/qomemo/signal/context.h b/qomemo/signal/context.h index c398105..b829aca 100644 --- a/qomemo/signal/context.h +++ b/qomemo/signal/context.h @@ -4,6 +4,10 @@ #pragma once +#include "crypto/ec.h" + +#include <memory> + #include <signal/signal_protocol.h> namespace Signal { @@ -16,6 +20,10 @@ namespace Signal { Context(Context &&) = delete; Context &operator=(const Context &) = delete; + std::unique_ptr<Crypto::ECKeyPair> generateCurveKeyPair(); + + signal_context *temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(); + private: signal_context *ctx{nullptr}; }; diff --git a/qomemo/signal/crypto/CMakeLists.txt b/qomemo/signal/crypto/CMakeLists.txt index 6359510..8f43895 100644 --- a/qomemo/signal/crypto/CMakeLists.txt +++ b/qomemo/signal/crypto/CMakeLists.txt @@ -7,4 +7,6 @@ target_sources(squawk PRIVATE hmac_sha256_openssl.h sha512_digest_openssl.cpp sha512_digest_openssl.h + ec.cpp + ec.h ) diff --git a/qomemo/signal/crypto/ec.cpp b/qomemo/signal/crypto/ec.cpp new file mode 100644 index 0000000..875d2ee --- /dev/null +++ b/qomemo/signal/crypto/ec.cpp @@ -0,0 +1,5 @@ +/* + * Created by victoria on 2021-06-17. +*/ + +#include "ec.h" diff --git a/qomemo/signal/crypto/ec.h b/qomemo/signal/crypto/ec.h new file mode 100644 index 0000000..280d00a --- /dev/null +++ b/qomemo/signal/crypto/ec.h @@ -0,0 +1,20 @@ +/* + * Created by victoria on 2021-06-17. +*/ + +#pragma once + +#include <signal/signal_protocol.h> + +namespace Signal::Crypto { + + class ECKeyPair { + public: + ECKeyPair(); + ~ECKeyPair(); + + private: + ec_key_pair *ec; + }; + +} diff --git a/qomemo/signal/stores/identity_key_store.cpp b/qomemo/signal/stores/identity_key_store.cpp index a8b28df..e80b7d3 100644 --- a/qomemo/signal/stores/identity_key_store.cpp +++ b/qomemo/signal/stores/identity_key_store.cpp @@ -4,13 +4,56 @@ #include "identity_key_store.h" -void Signal::Store::IdentityKeyStore::boundToContext( - signal_protocol_store_context *ctx) { - signal_protocol_identity_key_store store{}; +#include "qomemo/signal/util.h" +#include <utility> - store.user_data = nullptr; - store.destroy_func = nullptr; +Signal::Store::IdentityKeyStore::IdentityKeyStore(QXmpp::Omemo::DeviceService &deviceService, QString jid, int deviceId) + : jid(std::move(jid)), deviceId(deviceId), deviceService(deviceService) { + database = deviceService.getDatabase(jid); +} +int Signal::Store::IdentityKeyStore::getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data) { + auto pk = database->loadIdentityKey(deviceId); + auto sk = database->loadIdentityKeySecret(deviceId); + + *public_data = signal_buffer_create(reinterpret_cast<const uint8_t *>(pk->data()), pk->size()); + *private_data = signal_buffer_create(reinterpret_cast<const uint8_t *>(sk->data()), sk->size()); + + return 0; +} + +int Signal::Store::IdentityKeyStore::getLocalRegistrationId(uint32_t *registration_id) { + // TODO: Figure out what registration id is used for + *registration_id = 1; + + return 0; +} + +int Signal::Store::IdentityKeyStore::saveIdentity(const signal_protocol_address *address, uint8_t *key_data, + size_t key_len) { + auto identityJid = Signal::Util::jidFromAddress(address); + auto identityKey = Signal::Util::byteArray(key_data, key_len); + + deviceService.getDatabase(identityJid)->saveIdentityKey(address->device_id, identityKey); + + return 0; +} + +int Signal::Store::IdentityKeyStore::isTrustedIdentity(const signal_protocol_address *address, uint8_t *key_data, + size_t key_len) { + auto identityJid = Signal::Util::jidFromAddress(address); + auto actualIdentityKey = deviceService.getDatabase(identityJid)->loadIdentityKey(address->device_id); + + if (!actualIdentityKey.has_value()) { + return 1; + } + + auto givenIdentityKey = Signal::Util::byteArray(key_data, key_len); + + return givenIdentityKey == actualIdentityKey ? 1 : 0; +} + +void Signal::Store::IdentityKeyStore::fillCallbacks(signal_protocol_identity_key_store &store) { store.get_identity_key_pair = [](signal_buffer **public_data, signal_buffer **private_data, void *ptr) { return static_cast<IdentityKeyStore *>(ptr)->getIdentityKeyPair(public_data, private_data); }; @@ -24,24 +67,4 @@ void Signal::Store::IdentityKeyStore::boundToContext( store.save_identity = [](const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *ptr) { return static_cast<IdentityKeyStore *>(ptr)->saveIdentity(address, key_data, key_len); }; - - signal_protocol_store_context_set_identity_key_store(ctx, &store); -} - -int Signal::Store::IdentityKeyStore::getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data) { - return 0; -} - -int Signal::Store::IdentityKeyStore::getLocalRegistrationId(uint32_t *registration_id) { - return 0; -} - -int Signal::Store::IdentityKeyStore::saveIdentity(const signal_protocol_address *address, uint8_t *key_data, - size_t key_len) { - return 0; -} - -int Signal::Store::IdentityKeyStore::isTrustedIdentity(const signal_protocol_address *address, uint8_t *key_data, - size_t key_len) { - return 0; } diff --git a/qomemo/signal/stores/identity_key_store.h b/qomemo/signal/stores/identity_key_store.h index 4179430..017ca42 100644 --- a/qomemo/signal/stores/identity_key_store.h +++ b/qomemo/signal/stores/identity_key_store.h @@ -6,16 +6,27 @@ #include <signal/signal_protocol.h> +#include "qomemo/device_service.h" + namespace Signal::Store { class IdentityKeyStore { public: - static void boundToContext(signal_protocol_store_context *ctx); + IdentityKeyStore(QXmpp::Omemo::DeviceService &deviceService, QString jid, int deviceId); int getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data); int getLocalRegistrationId(uint32_t *registration_id); int saveIdentity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len); int isTrustedIdentity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len); + + void fillCallbacks(signal_protocol_identity_key_store &store); + + const QString jid; + const int deviceId; + + private: + QXmpp::Omemo::DeviceService &deviceService; + QSharedPointer<QXmpp::Omemo::Database> database; }; } // namespace Signal::Store diff --git a/qomemo/signal/stores/pre_key_store.cpp b/qomemo/signal/stores/pre_key_store.cpp index fe4cd60..72bfe72 100644 --- a/qomemo/signal/stores/pre_key_store.cpp +++ b/qomemo/signal/stores/pre_key_store.cpp @@ -4,29 +4,8 @@ #include "pre_key_store.h" -void Signal::Store::PreKeyStore::boundToContext( - signal_protocol_store_context *ctx) { - signal_protocol_pre_key_store store{}; - - store.destroy_func = nullptr; - store.user_data = nullptr; - - store.contains_pre_key = [](uint32_t id, void *ptr) { - return static_cast<PreKeyStore *>(ptr)->containsPreKey(id); - }; - store.load_pre_key = [](signal_buffer **record, uint32_t id, void *ptr) { - return static_cast<PreKeyStore *>(ptr)->loadPreKey(record, id); - }; - store.remove_pre_key = [](uint32_t id, void *ptr) { - return static_cast<PreKeyStore *>(ptr)->removePreKey(id); - }; - store.store_pre_key = [](uint32_t id, uint8_t *record, size_t size, - void *ptr) { - return static_cast<PreKeyStore *>(ptr)->storePreKey(id, record, size); - }; - - signal_protocol_store_context_set_pre_key_store(ctx, &store); -} +Signal::Store::PreKeyStore::PreKeyStore(QSharedPointer<QXmpp::Omemo::Database> database) : database( + std::move(database)) {} int Signal::Store::PreKeyStore::containsPreKey(uint32_t pre_key_id) { return 0; @@ -41,3 +20,19 @@ int Signal::Store::PreKeyStore::storePreKey(uint32_t pre_key_id, uint8_t *record } int Signal::Store::PreKeyStore::removePreKey(uint32_t pre_key_id) { return 0; } + +void Signal::Store::PreKeyStore::fillCallbacks(signal_protocol_pre_key_store &store) { + store.contains_pre_key = [](uint32_t id, void *ptr) { + return static_cast<PreKeyStore *>(ptr)->containsPreKey(id); + }; + store.load_pre_key = [](signal_buffer **record, uint32_t id, void *ptr) { + return static_cast<PreKeyStore *>(ptr)->loadPreKey(record, id); + }; + store.remove_pre_key = [](uint32_t id, void *ptr) { + return static_cast<PreKeyStore *>(ptr)->removePreKey(id); + }; + store.store_pre_key = [](uint32_t id, uint8_t *record, size_t size, + void *ptr) { + return static_cast<PreKeyStore *>(ptr)->storePreKey(id, record, size); + }; +} diff --git a/qomemo/signal/stores/pre_key_store.h b/qomemo/signal/stores/pre_key_store.h index eb70786..0ec1a79 100644 --- a/qomemo/signal/stores/pre_key_store.h +++ b/qomemo/signal/stores/pre_key_store.h @@ -6,16 +6,23 @@ #include <signal/signal_protocol.h> +#include "qomemo/device_service.h" + namespace Signal::Store { class PreKeyStore { public: - static void boundToContext(signal_protocol_store_context *ctx); + explicit PreKeyStore(QSharedPointer<QXmpp::Omemo::Database> database); int containsPreKey(uint32_t pre_key_id); int loadPreKey(signal_buffer **record, uint32_t pre_key_id); int storePreKey(uint32_t pre_key_id, uint8_t *record, size_t record_len); int removePreKey(uint32_t pre_key_id); + + void fillCallbacks(signal_protocol_pre_key_store &store); + + private: + QSharedPointer<QXmpp::Omemo::Database> database; }; } // namespace Signal::Store diff --git a/qomemo/signal/util.cpp b/qomemo/signal/util.cpp new file mode 100644 index 0000000..9c2ed08 --- /dev/null +++ b/qomemo/signal/util.cpp @@ -0,0 +1,14 @@ +/* + * Created by victoria on 2021-05-15. +*/ + +#include "util.h" + +QString Signal::Util::jidFromAddress(const signal_protocol_address *address) { + // TODO: Validate this + return QString::fromRawData(reinterpret_cast<const QChar *>(address->name), static_cast<int>(address->name_len)); +} + +QByteArray Signal::Util::byteArray(const uint8_t *data, size_t len) { + return QByteArray::fromRawData(reinterpret_cast<const char *>(data), static_cast<int>(len)); +} diff --git a/qomemo/signal/util.h b/qomemo/signal/util.h new file mode 100644 index 0000000..e1145ef --- /dev/null +++ b/qomemo/signal/util.h @@ -0,0 +1,18 @@ +/* + * Created by victoria on 2021-05-15. +*/ + +#pragma once + +#include <signal/signal_protocol.h> + +#include <QString> +#include <QByteArray> + +namespace Signal::Util { + + QString jidFromAddress(const signal_protocol_address *address); + + QByteArray byteArray(const uint8_t *data, size_t len); + +} diff --git a/qomemo/variant/conversations.cpp b/qomemo/variant/conversations.cpp index 4fc6af6..84926cb 100644 --- a/qomemo/variant/conversations.cpp +++ b/qomemo/variant/conversations.cpp @@ -4,10 +4,13 @@ #include "conversations.h" +#include "qomemo/bundle.h" #include "qomemo/device.h" #include "shared/qxmppfactories.h" +#include <QDebug> #include <QXmppIq.h> +#include <QXmppPubSubIq.h> using namespace QXmpp::Omemo; using namespace QXmpp::Factories; @@ -41,13 +44,21 @@ Variant::Conversations::deviceListToXml(const DeviceList &deviceList) { return element; } -DeviceList Variant::Conversations::deviceListFromXml(const QXmppElement &xml) { +std::optional<DeviceList> Variant::Conversations::latestDeviceListFromPubSubNode(const QXmppElement &items) { + auto item = items.firstChildElement("item"); + if (item.isNull()) + return std::nullopt; + + auto list = item.firstChildElement("list"); + if (list.isNull()) + return std::nullopt; + + if (!elementMatches(list, "list", "eu.siacs.conversations.axolotl")) + return std::nullopt; + DeviceList result{}; - if (!elementMatches(xml, "list", "eu.siacs.conversations.axolotl")) - return result; - - auto deviceElement = xml.firstChildElement("device"); + auto deviceElement = list.firstChildElement("device"); while (!deviceElement.isNull()) { result.devices.push_back(deviceFromXml(deviceElement)); deviceElement = deviceElement.nextSiblingElement("device"); @@ -65,7 +76,34 @@ QXmppIq Variant::Conversations::deviceListSetIq(const DeviceList &deviceList) { item.appendChild(deviceListToXml(deviceList)); auto publish = createElement("publish"); - publish.setAttribute("node", "eu.siacs.conversations.axolotl.devicelist"); + publish.setAttribute("node", getDeviceListNode()); + publish.appendChild(item); + + auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); + pubSub.appendChild(publish); + pubSub.appendChild(createOpenPublishOptions()); + + iq.setExtensions({ pubSub }); + + return iq; +} + +QString Variant::Conversations::getDeviceListNode() const { + return "eu.siacs.conversations.axolotl.devicelist"; +} + +QXmppIq Variant::Conversations::bundleSetIq(int deviceId, const Bundle &bundle) { + QXmppIq iq{}; + + iq.setType(QXmppIq::Set); + + auto item = createElement("item"); + item.appendChild(bundleToXml(bundle)); + + auto publish = createElement("publish"); + publish.setAttribute( + "node", + QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId)); publish.appendChild(item); auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); @@ -76,3 +114,92 @@ QXmppIq Variant::Conversations::deviceListSetIq(const DeviceList &deviceList) { return iq; } + +QXmppElement Variant::Conversations::bundleToXml(const Bundle &bundle) { + auto spkNode = createElement("signedPreKeyPublic"); + spkNode.setAttribute("signedPreKeyId", QString::number(bundle.spkId)); + spkNode.setValue(bundle.spk.toBase64()); + + auto spksNode = createElement("signedPreKeySignature"); + spksNode.setValue(bundle.spks.toBase64()); + + auto ikNode = createElement("identityKey"); + ikNode.setValue(bundle.ik.toBase64()); + + auto prekeysNode = createElement("prekeys"); + for (const auto &pk : bundle.prekeys) { + prekeysNode.appendChild(preKeyToXml(pk)); + } + + auto result = createElement("bundle", "eu.siacs.conversations.axolotl"); + result.appendChild(spkNode); + result.appendChild(spksNode); + result.appendChild(ikNode); + result.appendChild(prekeysNode); + + return result; +} + +QXmppElement Variant::Conversations::preKeyToXml(const PreKey &pk) { + auto x = createElement("preKeyPublic"); + x.setAttribute("preKeyId", QString::number(pk.id)); + x.setValue(pk.data.toBase64()); + + return x; +} + +std::optional<PreKey> Variant::Conversations::preKeyFromXml(const QXmppElement &xml) { + if (!elementMatches(xml, "preKeyPublic")) + return std::nullopt; + + auto pk = PreKey(); + pk.id = xml.attribute("preKeyId").toInt(); + pk.data = QByteArray::fromBase64Encoding(xml.value().toUtf8()).decoded; + + return pk; +} + +std::optional<Bundle> Variant::Conversations::bundleFromXml(const QXmppElement &xml) { + if (!elementMatches(xml, "bundle", "eu.siacs.conversations.axolotl")) + return std::nullopt; + + auto spkNode = xml.firstChildElement("signedPreKeyPublic"); + if (spkNode.isNull()) { + qWarning() << "'bundle': missing 'signedPreKeyPublic'"; + return std::nullopt; + } + + Bundle result{}; + + result.spk = QByteArray::fromBase64Encoding(spkNode.value().toUtf8()).decoded; + result.spkId = spkNode.attribute("signedPreKeyId").toInt(); + + auto spksNode = xml.firstChildElement("signedPreKeySignature"); + if (spksNode.isNull()) { + qWarning() << "'bundle': missing 'signedPreKeySignature'"; + return std::nullopt; + } + + result.spks = QByteArray::fromBase64Encoding(spksNode.value().toUtf8()).decoded; + + auto ikNode = xml.firstChildElement("identityKey"); + if (ikNode.isNull()) { + qWarning() << "'bundle': missing 'identityKey'"; + return std::nullopt; + } + + result.ik = QByteArray::fromBase64Encoding(ikNode.value().toUtf8()).decoded; + + auto prekeysNode = xml.firstChildElement("prekeys"); + auto pkNode = prekeysNode.firstChildElement("preKeyPublic"); + + result.prekeys.clear(); + while (!pkNode.isNull()) { + auto maybePk = preKeyFromXml(pkNode); + if (maybePk) + result.prekeys.push_back(*maybePk); + pkNode = pkNode.nextSiblingElement("preKeyPublic"); + } + + return result; +} diff --git a/qomemo/variant/conversations.h b/qomemo/variant/conversations.h index 923e29b..5a467f0 100644 --- a/qomemo/variant/conversations.h +++ b/qomemo/variant/conversations.h @@ -15,9 +15,18 @@ namespace QXmpp::Omemo::Variant { QXmppElement deviceToXml(const Device &device) override; Device deviceFromXml(const QXmppElement &xml) override; + [[nodiscard]] QString getDeviceListNode() const override; QXmppElement deviceListToXml(const DeviceList &deviceList) override; - DeviceList deviceListFromXml(const QXmppElement &xml) override; + std::optional<DeviceList> latestDeviceListFromPubSubNode(const QXmppElement &items) override; QXmppIq deviceListSetIq(const DeviceList &deviceList) override; + + QXmppElement preKeyToXml(const PreKey &pk) override; + std::optional<PreKey> preKeyFromXml(const QXmppElement &xml) override; + + QXmppElement bundleToXml(const Bundle &bundle) override; + std::optional<Bundle> bundleFromXml(const QXmppElement &xml) override; + + QXmppIq bundleSetIq(int deviceId, const Bundle &bundle) override; }; } // namespace QXmpp::Omemo::Variant diff --git a/qomemo/variant/omemo_base.h b/qomemo/variant/omemo_base.h index 58910fe..88d2a9f 100644 --- a/qomemo/variant/omemo_base.h +++ b/qomemo/variant/omemo_base.h @@ -4,14 +4,17 @@ #pragma once -class QXmppElement; +#include <optional> +class QString; +class QXmppElement; class QXmppIq; namespace QXmpp::Omemo { + class PreKey; + class Bundle; class Device; - class DeviceList; namespace Variant { @@ -23,9 +26,18 @@ namespace QXmpp::Omemo { virtual QXmppElement deviceToXml(const Device &device) = 0; virtual Device deviceFromXml(const QXmppElement &xml) = 0; + [[nodiscard]] virtual QString getDeviceListNode() const = 0; virtual QXmppElement deviceListToXml(const DeviceList &deviceList) = 0; - virtual DeviceList deviceListFromXml(const QXmppElement &xml) = 0; + virtual std::optional<DeviceList> latestDeviceListFromPubSubNode(const QXmppElement &xml) = 0; virtual QXmppIq deviceListSetIq(const DeviceList &deviceList) = 0; + + virtual QXmppElement preKeyToXml(const PreKey &pk) = 0; + virtual std::optional<PreKey> preKeyFromXml(const QXmppElement &xml) = 0; + + virtual QXmppElement bundleToXml(const Bundle& bundle) = 0; + virtual std::optional<Bundle> bundleFromXml(const QXmppElement& xml) = 0; + + virtual QXmppIq bundleSetIq(int deviceId, const Bundle& bundle) = 0; }; } // namespace Variant diff --git a/ui/omemo/contactsettings.cpp b/ui/omemo/contactsettings.cpp index c4d20b9..ba9704f 100644 --- a/ui/omemo/contactsettings.cpp +++ b/ui/omemo/contactsettings.cpp @@ -6,17 +6,19 @@ #include "ui_contactsettings.h" #include "omemodevices.h" -ContactSettings::ContactSettings(QWidget *parent) - : QDialog(parent), m_ui(new Ui::ContactSettings()) { +ContactSettings::ContactSettings(QString jid, QWidget *parent) + : QDialog(parent), jid(std::move(jid)), m_ui(new Ui::ContactSettings()) { m_ui->setupUi(this); connect(m_ui->devicesButton, &QPushButton::clicked, this, &ContactSettings::openDeviceList); + + setWindowTitle(QString("Encryption settings for %1").arg(this->jid)); } ContactSettings::~ContactSettings() {} void ContactSettings::openDeviceList() { - auto devices = new OMEMODevices(this); + auto devices = new OMEMODevices(jid, this); devices->setAttribute(Qt::WA_DeleteOnClose); devices->show(); diff --git a/ui/omemo/contactsettings.h b/ui/omemo/contactsettings.h index 4720c1f..37dc4c1 100644 --- a/ui/omemo/contactsettings.h +++ b/ui/omemo/contactsettings.h @@ -13,9 +13,11 @@ namespace Ui { class ContactSettings : public QDialog { Q_OBJECT public: - explicit ContactSettings(QWidget *parent = nullptr); + explicit ContactSettings(QString jid, QWidget *parent = nullptr); ~ContactSettings() override; + const QString jid; + private slots: void openDeviceList(); diff --git a/ui/omemo/omemodevices.cpp b/ui/omemo/omemodevices.cpp index 877ff27..5b08565 100644 --- a/ui/omemo/omemodevices.cpp +++ b/ui/omemo/omemodevices.cpp @@ -5,9 +5,11 @@ #include "omemodevices.h" #include "ui_omemodevices.h" -OMEMODevices::OMEMODevices(QWidget *parent) - : QDialog(parent), m_ui(new Ui::OMEMODevices()) { +OMEMODevices::OMEMODevices(QString jid, QWidget *parent) + : QDialog(parent), jid(std::move(jid)), m_ui(new Ui::OMEMODevices()) { m_ui->setupUi(this); + + setWindowTitle(QString("%1's OMEMO devices").arg(this->jid)); } OMEMODevices::~OMEMODevices() {} diff --git a/ui/omemo/omemodevices.h b/ui/omemo/omemodevices.h index 70dbb0f..529711e 100644 --- a/ui/omemo/omemodevices.h +++ b/ui/omemo/omemodevices.h @@ -13,9 +13,11 @@ namespace Ui { class OMEMODevices : public QDialog { Q_OBJECT public: - explicit OMEMODevices(QWidget *parent = nullptr); + explicit OMEMODevices(QString jid, QWidget *parent = nullptr); ~OMEMODevices() override; + const QString jid; + private: QScopedPointer<Ui::OMEMODevices> m_ui; }; diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 13c452a..1cd9929 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -20,6 +20,7 @@ #include "ui_squawk.h" #include <QDebug> #include <QIcon> +#include <utility> #include <ui/omemo/omemodevices.h> Squawk::Squawk(QWidget *parent) : @@ -61,7 +62,6 @@ Squawk::Squawk(QWidget *parent) : connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed); connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged); connect(&rosterModel, &Models::Roster::unnoticedMessage, this, &Squawk::onUnnoticedMessage); - connect(m_ui->actionDeviceList, &QAction::triggered, this, &Squawk::onOMEMODevices); connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged); connect(&rosterModel, &Models::Roster::requestArchive, this, &Squawk::onRequestArchive); @@ -136,8 +136,8 @@ void Squawk::onNewContact() nc->exec(); } -void Squawk::onOMEMODevices() { - auto od = new OMEMODevices(this); +void Squawk::openDeviceList(QString bareJid) { + auto od = new OMEMODevices(std::move(bareJid), this); od->setAttribute(Qt::WA_DeleteOnClose); od->show(); @@ -539,6 +539,10 @@ void Squawk::onRosterContextMenu(const QPoint& point) emit connectAccount(name); }); } + + QAction* devices = contextMenu->addAction(Shared::icon("security-high"), tr("Devices")); + devices->setEnabled(active); + connect(devices, &QAction::triggered, [this, acc]() { openDeviceList(acc->getBareJid()); }); QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard")); card->setEnabled(active); diff --git a/ui/squawk.h b/ui/squawk.h index a4c130e..a7318de 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -155,7 +155,7 @@ private slots: void onPasswordPromptRejected(); void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous); void onContextAboutToHide(); - void onOMEMODevices(); + void openDeviceList(QString bareJid); void onUnnoticedMessage(const QString& account, const Shared::Message& msg); diff --git a/ui/squawk.ui b/ui/squawk.ui index cb48de6..a029a44 100644 --- a/ui/squawk.ui +++ b/ui/squawk.ui @@ -169,17 +169,7 @@ <property name="title"> <string>Settings</string> </property> - <widget class="QMenu" name="menuOMEMO"> - <property name="title"> - <string>OMEMO</string> - </property> - <property name="icon"> - <iconset theme="security-high"/> - </property> - <addaction name="actionDeviceList"/> - </widget> <addaction name="actionAccounts"/> - <addaction name="menuOMEMO"/> </widget> <widget class="QMenu" name="menuFile"> <property name="title"> @@ -236,7 +226,8 @@ </action> <action name="actionDeviceList"> <property name="icon"> - <iconset theme="computer-laptop"/> + <iconset theme="computer-laptop"> + <normaloff>.</normaloff>.</iconset> </property> <property name="text"> <string>Device list</string> diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 2b2547b..518847f 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -437,7 +437,7 @@ void Conversation::onFeedContext(const QPoint& pos) } void Conversation::openEncryptionSettings() { - auto cs = new ContactSettings(this); + auto cs = new ContactSettings(palJid, this); cs->setAttribute(Qt::WA_DeleteOnClose); cs->show(); From 6c9f1ab964776bf4f26a5fd9761f08a3faa06701 Mon Sep 17 00:00:00 2001 From: vae <vae@programming.socks.town> Date: Thu, 22 Jul 2021 23:01:30 +0300 Subject: [PATCH 20/20] feat: wip omemo + key publish --- qomemo/qxmpp_omemo_manager.cpp | 40 +++++++++++-- qomemo/qxmpp_omemo_manager.h | 5 +- qomemo/signal/context.cpp | 10 +++- qomemo/signal/context.h | 1 + qomemo/signal/stores/sender_key_store.cpp | 34 +++++------ qomemo/signal/stores/sender_key_store.h | 4 +- qomemo/signal/stores/session_store.cpp | 58 +++++++++---------- qomemo/signal/stores/session_store.h | 4 +- qomemo/signal/stores/signed_pre_key_store.cpp | 48 +++++++-------- qomemo/signal/stores/signed_pre_key_store.h | 3 +- qomemo/signal/stores/store_context.cpp | 14 ++++- qomemo/signal/stores/store_context.h | 18 ++++++ qomemo/variant/conversations.cpp | 4 +- 13 files changed, 147 insertions(+), 96 deletions(-) diff --git a/qomemo/qxmpp_omemo_manager.cpp b/qomemo/qxmpp_omemo_manager.cpp index 788fc96..e70754c 100644 --- a/qomemo/qxmpp_omemo_manager.cpp +++ b/qomemo/qxmpp_omemo_manager.cpp @@ -22,7 +22,8 @@ using namespace QXmpp::Omemo; Manager::Manager() : deviceService{new DeviceService(this)}, - omemoVariant(new Variant::Conversations) { + omemoVariant(new Variant::Conversations), + signalContext(new Signal::Context) { connect(this, &Manager::deviceListReceived, deviceService.get(), &DeviceService::onDeviceListReceived); connect(this, &Manager::deviceListNotFound, deviceService.get(), &DeviceService::onDeviceListNotFound); connect(this, &Manager::deviceListNotFound, this, [this](const QString &jid) { @@ -42,7 +43,7 @@ bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) { std::cout << str.toStdString(); - if (handleDeviceList(stanza) || handleMissingDeviceList(stanza)) + if (handleDeviceList(stanza) || handleMissingDeviceList(stanza) || handleEncryptedMessage(stanza)) return true; return false; @@ -98,6 +99,19 @@ bool Manager::handleMissingDeviceList(const QDomElement &stanza) { return true; } +bool Manager::handleEncryptedMessage(const QDomElement &stanza) { + if (stanza.tagName() != "message") + return false; + + auto encrypted = stanza.firstChildElement("encrypted"); + if (encrypted.isNull()) + return false; + + qDebug() << "!!!! Got encrypted message!!"; + + return true; +} + void QXmpp::Omemo::Manager::setClient(QXmppClient *client) { QXmppClientExtension::setClient(client); @@ -178,7 +192,7 @@ void Manager::generateDeviceForSelfIfNeeded(const DeviceList ¤tList) { qInfo() << "Generating device"; - auto deviceId = QRandomGenerator64{}.bounded(INT32_MAX); + auto deviceId = QRandomGenerator64::system()->bounded(INT32_MAX); db->saveActiveDeviceId(deviceId); Device device{}; @@ -186,11 +200,11 @@ void Manager::generateDeviceForSelfIfNeeded(const DeviceList ¤tList) { auto updatedList = currentList; - auto bundle = Bundle(); updatedList.devices.push_back(device); - publishDeviceList(updatedList); + + auto bundle = generateAndSaveBundle(deviceId); publishBundle(deviceId, bundle); } @@ -213,6 +227,8 @@ Bundle Manager::generateAndSaveBundle(int deviceId) { database->saveIdentityKey(deviceId, identityKey); database->saveIdentityKeySecret(deviceId, identityKeySecret); + result.ik = identityKey; + // Generate SPK ec_key_pair *ecSpk; curve_generate_key_pair(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), &ecSpk); @@ -227,11 +243,15 @@ Bundle Manager::generateAndSaveBundle(int deviceId) { signal_buffer_len(spkPublic)); SignedPreKey spk{}; - spk.id = 0; + spk.id = 1; spk.key.publicKey = QByteArray((const char *) signal_buffer_const_data(spkPublic), (int) signal_buffer_len(spkPublic)); spk.key.secretKey = QByteArray((const char *) signal_buffer_const_data(spkSecret), (int) signal_buffer_len(spkSecret)); spk.signature = QByteArray((const char *) signal_buffer_const_data(signature), (int) signal_buffer_len(signature)); + result.spk = spk.key.publicKey; + result.spks = spk.signature; + result.spkId = 1; + // Generate 100 PK for (auto i = 1; i <= 100; ++i) { ec_key_pair *currentPreKey; @@ -247,6 +267,12 @@ Bundle Manager::generateAndSaveBundle(int deviceId) { database->savePreKey(deviceId, i, preKey); + PreKey pk{}; + pk.data = preKey.publicKey; + pk.id = i; + + result.prekeys.append(pk); + signal_buffer_free(pkPublic); signal_buffer_free(pkSecret); SIGNAL_UNREF(currentPreKey); @@ -261,4 +287,6 @@ Bundle Manager::generateAndSaveBundle(int deviceId) { signal_buffer_free(spkPublic); signal_buffer_free(spkSecret); SIGNAL_UNREF(ecSpk); + + return result; } diff --git a/qomemo/qxmpp_omemo_manager.h b/qomemo/qxmpp_omemo_manager.h index 7f04026..03c2aa2 100644 --- a/qomemo/qxmpp_omemo_manager.h +++ b/qomemo/qxmpp_omemo_manager.h @@ -8,6 +8,7 @@ #include "qomemo.h" #include "variant/omemo_base.h" +#include <memory> #include <QXmppClientExtension.h> namespace Signal { @@ -29,6 +30,8 @@ namespace QXmpp::Omemo { bool handleMissingDeviceList(const QDomElement& stanza); + bool handleEncryptedMessage(const QDomElement& stanza); + QSharedPointer<DeviceService> getDeviceService(); Bundle generateAndSaveBundle(int deviceId); @@ -55,7 +58,7 @@ namespace QXmpp::Omemo { private: QSharedPointer<DeviceService> deviceService; QScopedPointer<Variant::Base> omemoVariant; - QScopedPointer<Signal::Context> signalContext; + std::shared_ptr<Signal::Context> signalContext; }; } // namespace QXmpp::Omemo diff --git a/qomemo/signal/context.cpp b/qomemo/signal/context.cpp index 6253faa..75d3557 100644 --- a/qomemo/signal/context.cpp +++ b/qomemo/signal/context.cpp @@ -3,12 +3,18 @@ */ #include "context.h" +#include "crypto/crypto.h" using namespace Signal; -Context::Context() {} +Context::Context() : cryptoProvider{ Signal::Crypto::createProvider() } { + signal_context_create(&ctx, nullptr); + signal_context_set_crypto_provider(ctx, &cryptoProvider); +} -Context::~Context() {} +Context::~Context() { + signal_context_destroy(ctx); +} std::unique_ptr<Crypto::ECKeyPair> Context::generateCurveKeyPair() { auto result = std::unique_ptr<Crypto::ECKeyPair>(); diff --git a/qomemo/signal/context.h b/qomemo/signal/context.h index b829aca..30941d9 100644 --- a/qomemo/signal/context.h +++ b/qomemo/signal/context.h @@ -25,6 +25,7 @@ namespace Signal { signal_context *temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(); private: + signal_crypto_provider cryptoProvider{}; signal_context *ctx{nullptr}; }; diff --git a/qomemo/signal/stores/sender_key_store.cpp b/qomemo/signal/stores/sender_key_store.cpp index a1cb687..2db5231 100644 --- a/qomemo/signal/stores/sender_key_store.cpp +++ b/qomemo/signal/stores/sender_key_store.cpp @@ -4,26 +4,6 @@ #include "sender_key_store.h" -void Signal::Store::SenderKeyStore::boundToContext( - signal_protocol_store_context *ctx) { - signal_protocol_sender_key_store store{}; - - store.user_data = nullptr; - store.destroy_func = nullptr; - - store.load_sender_key = [](signal_buffer **record, signal_buffer **user_record, - const signal_protocol_sender_key_name *sender_key_name, void *ptr) { - return static_cast<SenderKeyStore *>(ptr)->loadSenderKey(record, user_record, sender_key_name); - }; - store.store_sender_key = [](const signal_protocol_sender_key_name *sender_key_name, uint8_t *record, - size_t record_len, uint8_t *user_record, size_t user_record_len, void *ptr) { - return static_cast<SenderKeyStore *>(ptr)->storeSenderKey(sender_key_name, record, record_len, user_record, - user_record_len); - }; - - signal_protocol_store_context_set_sender_key_store(ctx, &store); -} - int Signal::Store::SenderKeyStore::loadSenderKey(signal_buffer **record, signal_buffer **user_record, const signal_protocol_sender_key_name *sender_key_name) { return 0; @@ -34,3 +14,17 @@ int Signal::Store::SenderKeyStore::storeSenderKey(const signal_protocol_sender_k size_t user_record_len) { return 0; } + +void Signal::Store::SenderKeyStore::fillCallbacks(signal_protocol_sender_key_store &store) { + store.user_data = nullptr; + store.destroy_func = nullptr; + store.load_sender_key = [](signal_buffer **record, signal_buffer **user_record, + const signal_protocol_sender_key_name *sender_key_name, void *ptr) { + return static_cast<SenderKeyStore *>(ptr)->loadSenderKey(record, user_record, sender_key_name); + }; + store.store_sender_key = [](const signal_protocol_sender_key_name *sender_key_name, uint8_t *record, + size_t record_len, uint8_t *user_record, size_t user_record_len, void *ptr) { + return static_cast<SenderKeyStore *>(ptr)->storeSenderKey(sender_key_name, record, record_len, user_record, + user_record_len); + }; +} diff --git a/qomemo/signal/stores/sender_key_store.h b/qomemo/signal/stores/sender_key_store.h index 904377e..a5abcc6 100644 --- a/qomemo/signal/stores/sender_key_store.h +++ b/qomemo/signal/stores/sender_key_store.h @@ -10,12 +10,12 @@ namespace Signal::Store { class SenderKeyStore { public: - static void boundToContext(signal_protocol_store_context *ctx); - int storeSenderKey(const signal_protocol_sender_key_name *sender_key_name, uint8_t *record, size_t record_len, uint8_t *user_record, size_t user_record_len); int loadSenderKey(signal_buffer **record, signal_buffer **user_record, const signal_protocol_sender_key_name *sender_key_name); + + void fillCallbacks(signal_protocol_sender_key_store &store); }; } // namespace Signal::Store diff --git a/qomemo/signal/stores/session_store.cpp b/qomemo/signal/stores/session_store.cpp index 311b3e6..4b9a5f9 100644 --- a/qomemo/signal/stores/session_store.cpp +++ b/qomemo/signal/stores/session_store.cpp @@ -4,38 +4,6 @@ #include "session_store.h" -void Signal::Store::SessionStore::boundToContext( - signal_protocol_store_context *ctx) { - signal_protocol_session_store store{}; - - store.user_data = nullptr; - store.destroy_func = nullptr; - - store.load_session_func = [](signal_buffer **record, signal_buffer **user_record, - const signal_protocol_address *address, void *ptr) { - return static_cast<SessionStore *>(ptr)->loadSession(record, user_record, address); - }; - store.get_sub_device_sessions_func = [](signal_int_list **sessions, const char *name, size_t name_len, void *ptr) { - return static_cast<SessionStore *>(ptr)->getSubDeviceSessions(sessions, name, name_len); - }; - store.store_session_func = [](const signal_protocol_address *address, uint8_t *record, size_t record_len, - uint8_t *user_record, size_t user_record_len, void *ptr) { - return static_cast<SessionStore *>(ptr)->storeSession(address, record, record_len, user_record, - user_record_len); - }; - store.contains_session_func = [](const signal_protocol_address *address, void *ptr) { - return static_cast<SessionStore *>(ptr)->containsSession(address); - }; - store.delete_session_func = [](const signal_protocol_address *address, void *ptr) { - return static_cast<SessionStore *>(ptr)->deleteSession(address); - }; - store.delete_all_sessions_func = [](const char *name, size_t name_len, void *ptr) { - return static_cast<SessionStore *>(ptr)->deleteAllSessions(name, name_len); - }; - - signal_protocol_store_context_set_session_store(ctx, &store); -} - int Signal::Store::SessionStore::loadSession(signal_buffer **record, signal_buffer **user_record, const signal_protocol_address *address) { return 0; @@ -61,3 +29,29 @@ int Signal::Store::SessionStore::deleteSession(const signal_protocol_address *ad int Signal::Store::SessionStore::deleteAllSessions(const char *name, size_t name_len) { return 0; } + +void Signal::Store::SessionStore::fillCallbacks(signal_protocol_session_store &store) { + store.user_data = nullptr; + store.destroy_func = nullptr; + store.load_session_func = [](signal_buffer **record, signal_buffer **user_record, + const signal_protocol_address *address, void *ptr) { + return static_cast<SessionStore *>(ptr)->loadSession(record, user_record, address); + }; + store.get_sub_device_sessions_func = [](signal_int_list **sessions, const char *name, size_t name_len, void *ptr) { + return static_cast<SessionStore *>(ptr)->getSubDeviceSessions(sessions, name, name_len); + }; + store.store_session_func = [](const signal_protocol_address *address, uint8_t *record, size_t record_len, + uint8_t *user_record, size_t user_record_len, void *ptr) { + return static_cast<SessionStore *>(ptr)->storeSession(address, record, record_len, user_record, + user_record_len); + }; + store.contains_session_func = [](const signal_protocol_address *address, void *ptr) { + return static_cast<SessionStore *>(ptr)->containsSession(address); + }; + store.delete_session_func = [](const signal_protocol_address *address, void *ptr) { + return static_cast<SessionStore *>(ptr)->deleteSession(address); + }; + store.delete_all_sessions_func = [](const char *name, size_t name_len, void *ptr) { + return static_cast<SessionStore *>(ptr)->deleteAllSessions(name, name_len); + }; +} diff --git a/qomemo/signal/stores/session_store.h b/qomemo/signal/stores/session_store.h index 848e5b8..c773c83 100644 --- a/qomemo/signal/stores/session_store.h +++ b/qomemo/signal/stores/session_store.h @@ -10,8 +10,6 @@ namespace Signal::Store { class SessionStore { public: - static void boundToContext(signal_protocol_store_context *ctx); - int loadSession(signal_buffer **record, signal_buffer **user_record, const signal_protocol_address *address); int getSubDeviceSessions(signal_int_list **sessions, const char *name, size_t name_len); int storeSession(const signal_protocol_address *address, uint8_t *record, size_t record_len, @@ -19,6 +17,8 @@ namespace Signal::Store { int containsSession(const signal_protocol_address *address); int deleteSession(const signal_protocol_address *address); int deleteAllSessions(const char *name, size_t name_len); + + void fillCallbacks(signal_protocol_session_store &store); }; } // namespace Signal::Store diff --git a/qomemo/signal/stores/signed_pre_key_store.cpp b/qomemo/signal/stores/signed_pre_key_store.cpp index 43bae1e..483465d 100644 --- a/qomemo/signal/stores/signed_pre_key_store.cpp +++ b/qomemo/signal/stores/signed_pre_key_store.cpp @@ -4,33 +4,6 @@ #include "signed_pre_key_store.h" -void Signal::Store::SignedPreKeyStore::boundToContext( - signal_protocol_store_context *ctx) { - signal_protocol_signed_pre_key_store store{}; - - store.user_data = nullptr; - store.destroy_func = nullptr; - - store.load_signed_pre_key = [](signal_buffer **record, uint32_t signed_pre_key_id, void *ptr) { - return static_cast<SignedPreKeyStore *>(ptr)->loadSignedPreKey( - record, signed_pre_key_id); - }; - store.store_signed_pre_key = [](uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *ptr) { - return static_cast<SignedPreKeyStore *>(ptr)->storeSignedPreKey( - signed_pre_key_id, record, record_len); - }; - store.contains_signed_pre_key = [](uint32_t signed_pre_key_id, void *ptr) { - return static_cast<SignedPreKeyStore *>(ptr)->containsSignedPreKey( - signed_pre_key_id); - }; - store.remove_signed_pre_key = [](uint32_t signed_pre_key_id, void *ptr) { - return static_cast<SignedPreKeyStore *>(ptr)->removeSignedPreKey( - signed_pre_key_id); - }; - - signal_protocol_store_context_set_signed_pre_key_store(ctx, &store); -} - int Signal::Store::SignedPreKeyStore::loadSignedPreKey(signal_buffer **record, uint32_t signed_pre_key_id) { return 0; } @@ -47,3 +20,24 @@ int Signal::Store::SignedPreKeyStore::containsSignedPreKey(uint32_t signed_pre_k int Signal::Store::SignedPreKeyStore::removeSignedPreKey(uint32_t signed_pre_key_id) { return 0; } + +void Signal::Store::SignedPreKeyStore::fillCallbacks(signal_protocol_signed_pre_key_store &store) { + store.user_data = nullptr; + store.destroy_func = nullptr; + store.load_signed_pre_key = [](signal_buffer **record, uint32_t signed_pre_key_id, void *ptr) { + return static_cast<SignedPreKeyStore *>(ptr)->loadSignedPreKey( + record, signed_pre_key_id); + }; + store.store_signed_pre_key = [](uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *ptr) { + return static_cast<SignedPreKeyStore *>(ptr)->storeSignedPreKey( + signed_pre_key_id, record, record_len); + }; + store.contains_signed_pre_key = [](uint32_t signed_pre_key_id, void *ptr) { + return static_cast<SignedPreKeyStore *>(ptr)->containsSignedPreKey( + signed_pre_key_id); + }; + store.remove_signed_pre_key = [](uint32_t signed_pre_key_id, void *ptr) { + return static_cast<SignedPreKeyStore *>(ptr)->removeSignedPreKey( + signed_pre_key_id); + }; +} diff --git a/qomemo/signal/stores/signed_pre_key_store.h b/qomemo/signal/stores/signed_pre_key_store.h index 3f58851..2544741 100644 --- a/qomemo/signal/stores/signed_pre_key_store.h +++ b/qomemo/signal/stores/signed_pre_key_store.h @@ -10,11 +10,12 @@ namespace Signal::Store { class SignedPreKeyStore { public: - static void boundToContext(signal_protocol_store_context *ctx); int loadSignedPreKey(signal_buffer **record, uint32_t signed_pre_key_id); int storeSignedPreKey(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len); int containsSignedPreKey(uint32_t signed_pre_key_id); int removeSignedPreKey(uint32_t signed_pre_key_id); + + void fillCallbacks(signal_protocol_signed_pre_key_store &store); }; } // namespace Signal::Store diff --git a/qomemo/signal/stores/store_context.cpp b/qomemo/signal/stores/store_context.cpp index dd5bb5e..e4dd58a 100644 --- a/qomemo/signal/stores/store_context.cpp +++ b/qomemo/signal/stores/store_context.cpp @@ -4,8 +4,20 @@ #include "store_context.h" -Signal::Store::Context::Context(signal_context *global) { +Signal::Store::Context::Context(signal_context *global) : identityKeyStore(), preKeyStore(), senderKeyStore(), sessionStore(), signedPreKeyStore() { signal_protocol_store_context_create(&ctx, global); + + identityKeyStore->fillCallbacks(iks); + preKeyStore->fillCallbacks(pks); + senderKeyStore->fillCallbacks(sks); + sessionStore->fillCallbacks(ss); + signedPreKeyStore->fillCallbacks(spks); + + signal_protocol_store_context_set_identity_key_store(ctx, &iks); + signal_protocol_store_context_set_pre_key_store(ctx, &pks); + signal_protocol_store_context_set_sender_key_store(ctx, &sks); + signal_protocol_store_context_set_session_store(ctx, &ss); + signal_protocol_store_context_set_signed_pre_key_store(ctx, &spks); } Signal::Store::Context::~Context() { diff --git a/qomemo/signal/stores/store_context.h b/qomemo/signal/stores/store_context.h index 204d9b9..7c1ea55 100644 --- a/qomemo/signal/stores/store_context.h +++ b/qomemo/signal/stores/store_context.h @@ -6,6 +6,12 @@ #include <signal/signal_protocol.h> +#include "identity_key_store.h" +#include "pre_key_store.h" +#include "sender_key_store.h" +#include "session_store.h" +#include "signed_pre_key_store.h" + namespace Signal::Store { class Context { @@ -19,6 +25,18 @@ namespace Signal::Store { private: signal_protocol_store_context *ctx{nullptr}; + + signal_protocol_identity_key_store iks{}; + signal_protocol_pre_key_store pks{}; + signal_protocol_sender_key_store sks{}; + signal_protocol_session_store ss{}; + signal_protocol_signed_pre_key_store spks{}; + + std::unique_ptr<IdentityKeyStore> identityKeyStore; + std::unique_ptr<PreKeyStore> preKeyStore; + std::unique_ptr<SenderKeyStore> senderKeyStore; + std::unique_ptr<SessionStore> sessionStore; + std::unique_ptr<SignedPreKeyStore> signedPreKeyStore; }; } // namespace Signal::Store diff --git a/qomemo/variant/conversations.cpp b/qomemo/variant/conversations.cpp index 84926cb..1658c37 100644 --- a/qomemo/variant/conversations.cpp +++ b/qomemo/variant/conversations.cpp @@ -103,14 +103,14 @@ QXmppIq Variant::Conversations::bundleSetIq(int deviceId, const Bundle &bundle) auto publish = createElement("publish"); publish.setAttribute( "node", - QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId)); + QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId)); publish.appendChild(item); auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); pubSub.appendChild(publish); pubSub.appendChild(createOpenPublishOptions()); - iq.extensions().push_back(pubSub); + iq.setExtensions({ pubSub }); return iq; }