aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Rusakov <Kitsune-Ral@users.sf.net>2022-08-25 19:58:48 +0200
committerAlexey Rusakov <Kitsune-Ral@users.sf.net>2022-08-25 19:59:29 +0200
commit6404b8cd4d57468b810538da04f8017fb13ccc37 (patch)
treea7ea65d971b24791368d9eae5b1db92551ef0ea8
parent376da43a29f3ebad807da2761e7a0c0b105587ec (diff)
downloadlibquotient-6404b8cd4d57468b810538da04f8017fb13ccc37.tar.gz
libquotient-6404b8cd4d57468b810538da04f8017fb13ccc37.zip
Refactor the code handling emoji
- Use a dedicated structure, EmojiEntry, instead of QVariantMap (it's Q_GADGET, should be readable from QML just fine) - While we're at it, QVector is better than QList in Qt 5.15 - Remove language from the session state - it's used in a single method - Modernise handleKey() code
-rw-r--r--lib/keyverificationsession.cpp128
-rw-r--r--lib/keyverificationsession.h19
2 files changed, 91 insertions, 56 deletions
diff --git a/lib/keyverificationsession.cpp b/lib/keyverificationsession.cpp
index caf5071a..2c468c3e 100644
--- a/lib/keyverificationsession.cpp
+++ b/lib/keyverificationsession.cpp
@@ -110,9 +110,6 @@ void KeyVerificationSession::init(milliseconds timeout)
auto randomSize = olm_create_sas_random_length(m_sas);
auto random = getRandom(randomSize);
olm_create_sas(m_sas, random.data(), randomSize);
-
- m_language = QLocale::system().uiLanguages()[0];
- m_language = m_language.left(m_language.indexOf('-'));
}
KeyVerificationSession::~KeyVerificationSession()
@@ -121,18 +118,57 @@ KeyVerificationSession::~KeyVerificationSession()
delete[] reinterpret_cast<std::byte*>(m_sas);
}
+struct EmojiStoreEntry : EmojiEntry {
+ QHash<QString, QString> translatedDescriptions;
+
+ explicit EmojiStoreEntry(const QJsonObject& json)
+ : EmojiEntry{ fromJson<QString>(json["emoji"]),
+ fromJson<QString>(json["description"]) }
+ , translatedDescriptions{ fromJson<QHash<QString, QString>>(
+ json["translated_descriptions"]) }
+ {}
+};
+
+using EmojiStore = QVector<EmojiStoreEntry>;
+
+EmojiStore loadEmojiStore()
+{
+ QFile dataFile(":/sas-emoji.json");
+ dataFile.open(QFile::ReadOnly);
+ return fromJson<EmojiStore>(
+ QJsonDocument::fromJson(dataFile.readAll()).array());
+}
+
+EmojiEntry emojiForCode(int code, const QString& language)
+{
+ static const EmojiStore emojiStore = loadEmojiStore();
+ const auto& entry = emojiStore[code];
+ if (!language.isEmpty())
+ if (const auto translatedDescription =
+ emojiStore[code].translatedDescriptions.value(language);
+ !translatedDescription.isNull())
+ return { entry.emoji, translatedDescription };
+
+ return SLICE(entry, EmojiEntry);
+}
+
void KeyVerificationSession::handleKey(const KeyVerificationKeyEvent& event)
{
if (state() != WAITINGFORKEY && state() != WAITINGFORVERIFICATION) {
cancelVerification(UNEXPECTED_MESSAGE);
return;
}
- olm_sas_set_their_key(m_sas, event.key().toLatin1().data(), event.key().toLatin1().size());
+ auto eventKey = event.key().toLatin1();
+ olm_sas_set_their_key(m_sas, eventKey.data(), eventKey.size());
if (startSentByUs) {
- auto commitment = QString(QCryptographicHash::hash((event.key() % m_startEvent).toLatin1(), QCryptographicHash::Sha256).toBase64());
- commitment = commitment.left(commitment.indexOf('='));
- if (commitment != m_commitment) {
+ const auto paddedCommitment =
+ QCryptographicHash::hash((eventKey % m_startEvent).toLatin1(),
+ QCryptographicHash::Sha256)
+ .toBase64();
+ const QLatin1String unpaddedCommitment(paddedCommitment.constData(),
+ paddedCommitment.indexOf('='));
+ if (unpaddedCommitment != m_commitment) {
qCWarning(E2EE) << "Commitment mismatch; aborting verification";
cancelVerification(MISMATCHED_COMMITMENT);
return;
@@ -142,34 +178,40 @@ void KeyVerificationSession::handleKey(const KeyVerificationKeyEvent& event)
}
setState(WAITINGFORVERIFICATION);
- QByteArray keyBytes(olm_sas_pubkey_length(m_sas), '\0');
- olm_sas_get_pubkey(m_sas, keyBytes.data(), keyBytes.size());
- QString key = QString(keyBytes);
-
- QByteArray output(6, '\0');
- QString infoTemplate = startSentByUs ? "MATRIX_KEY_VERIFICATION_SAS|%1|%2|%3|%4|%5|%6|%7"_ls : "MATRIX_KEY_VERIFICATION_SAS|%4|%5|%6|%1|%2|%3|%7"_ls;
-
- auto info = infoTemplate.arg(m_connection->userId()).arg(m_connection->deviceId()).arg(key).arg(m_remoteUserId).arg(m_remoteDeviceId).arg(event.key()).arg(m_transactionId);
- olm_sas_generate_bytes(m_sas, info.toLatin1().data(), info.toLatin1().size(), output.data(), output.size());
-
- QVector<uint8_t> code(7, 0);
- const auto& data = (uint8_t *) output.data();
-
- code[0] = data[0] >> 2;
- code[1] = (data[0] << 4 & 0x3f) | data[1] >> 4;
- code[2] = (data[1] << 2 & 0x3f) | data[2] >> 6;
- code[3] = data[2] & 0x3f;
- code[4] = data[3] >> 2;
- code[5] = (data[3] << 4 & 0x3f) | data[4] >> 4;
- code[6] = (data[4] << 2 & 0x3f) | data[5] >> 6;
-
- for (const auto& c : code) {
- auto [emoji, description] = emojiForCode(c);
- QVariantMap map;
- map["emoji"] = emoji;
- map["description"] = description;
- m_sasEmojis += map;
- }
+ std::string key(olm_sas_pubkey_length(m_sas), '\0');
+ olm_sas_get_pubkey(m_sas, key.data(), key.size());
+
+ std::array<std::byte, 6> output{};
+ const auto infoTemplate =
+ startSentByUs ? "MATRIX_KEY_VERIFICATION_SAS|%1|%2|%3|%4|%5|%6|%7"_ls
+ : "MATRIX_KEY_VERIFICATION_SAS|%4|%5|%6|%1|%2|%3|%7"_ls;
+
+ const auto info = infoTemplate
+ .arg(m_connection->userId(), m_connection->deviceId(),
+ key.data(), m_remoteUserId, m_remoteDeviceId,
+ eventKey, m_transactionId)
+ .toLatin1();
+ olm_sas_generate_bytes(m_sas, info.data(), info.size(), output.data(),
+ output.size());
+
+ static constexpr auto x3f = std::byte{ 0x3f };
+ const std::array<std::byte, 7> code{
+ output[0] >> 2,
+ (output[0] << 4 & x3f) | output[1] >> 4,
+ (output[1] << 2 & x3f) | output[2] >> 6,
+ output[2] & x3f,
+ output[3] >> 2,
+ (output[3] << 4 & x3f) | output[4] >> 4,
+ (output[4] << 2 & x3f) | output[5] >> 6
+ };
+
+ const auto uiLanguages = QLocale().uiLanguages();
+ const auto preferredLanguage = uiLanguages.isEmpty()
+ ? QString()
+ : uiLanguages.front().section('-', 0, 0);
+ for (const auto& c : code)
+ m_sasEmojis += emojiForCode(std::to_integer<int>(c), preferredLanguage);
+
emit sasEmojisChanged();
emit keyReceived();
}
@@ -369,21 +411,7 @@ void KeyVerificationSession::handleCancel(const KeyVerificationCancelEvent& even
setState(CANCELED);
}
-std::pair<QString, QString> KeyVerificationSession::emojiForCode(int code)
-{
- static QJsonArray data;
- if (data.isEmpty()) {
- QFile dataFile(":/sas-emoji.json");
- dataFile.open(QFile::ReadOnly);
- data = QJsonDocument::fromJson(dataFile.readAll()).array();
- }
- if (data[code].toObject()["translated_descriptions"].toObject().contains(m_language)) {
- return {data[code].toObject()["emoji"].toString(), data[code].toObject()["translated_descriptions"].toObject()[m_language].toString()};
- }
- return {data[code].toObject()["emoji"].toString(), data[code].toObject()["description"].toString()};
-}
-
-QList<QVariantMap> KeyVerificationSession::sasEmojis() const
+QVector<EmojiEntry> KeyVerificationSession::sasEmojis() const
{
return m_sasEmojis;
}
diff --git a/lib/keyverificationsession.h b/lib/keyverificationsession.h
index 73c9384e..aa0295cb 100644
--- a/lib/keyverificationsession.h
+++ b/lib/keyverificationsession.h
@@ -12,6 +12,15 @@ struct OlmSAS;
namespace Quotient {
class Connection;
+struct QUOTIENT_API EmojiEntry {
+ QString emoji;
+ QString description;
+
+ Q_GADGET
+ Q_PROPERTY(QString emoji MEMBER emoji CONSTANT)
+ Q_PROPERTY(QString description MEMBER description CONSTANT)
+};
+
/** A key verification session. Listen for incoming sessions by connecting to Connection::newKeyVerificationSession.
Start a new session using Connection::startKeyVerificationSession.
The object is delete after finished is emitted.
@@ -67,7 +76,7 @@ public:
Q_ENUM(Error)
Q_PROPERTY(QString remoteDeviceId MEMBER m_remoteDeviceId CONSTANT)
- Q_PROPERTY(QList<QVariantMap> sasEmojis READ sasEmojis NOTIFY sasEmojisChanged)
+ Q_PROPERTY(QVector<EmojiEntry> sasEmojis READ sasEmojis NOTIFY sasEmojisChanged)
Q_PROPERTY(State state READ state NOTIFY stateChanged)
Q_PROPERTY(Error error READ error NOTIFY errorChanged)
@@ -79,7 +88,7 @@ public:
~KeyVerificationSession() override;
Q_DISABLE_COPY_MOVE(KeyVerificationSession)
- QList<QVariantMap> sasEmojis() const;
+ QVector<EmojiEntry> sasEmojis() const;
State state() const;
Error error() const;
@@ -107,13 +116,12 @@ private:
const QString m_transactionId;
Connection* m_connection;
OlmSAS* m_sas = nullptr;
- QList<QVariantMap> m_sasEmojis;
+ QVector<EmojiEntry> m_sasEmojis;
bool startSentByUs = false;
State m_state = INCOMING;
Error m_error = NONE;
QString m_startEvent;
QString m_commitment;
- QString m_language;
bool macReceived = false;
bool m_encrypted;
QStringList m_remoteSupportedMethods;
@@ -133,8 +141,7 @@ private:
QByteArray macInfo(bool verifying, const QString& key = "KEY_IDS"_ls);
QString calculateMac(const QString& input, bool verifying, const QString& keyId= "KEY_IDS"_ls);
-
- std::pair<QString, QString> emojiForCode(int code);
};
} // namespace Quotient
+Q_DECLARE_METATYPE(Quotient::EmojiEntry)