Skip to content

Commit

Permalink
KeyVerificationSession: be more vigilant with the received commitment
Browse files Browse the repository at this point in the history
The commitment is now checked to actually be valid base64 as we receive
it. Also, it is stored as QByteArray now - thanks to QString and
QByteArray having the same layout (3 pointers + alignment) we can get
away with this without breaking ABI compat.
  • Loading branch information
KitsuneRal committed Nov 25, 2024
1 parent 529bf4e commit 6facc64
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 9 deletions.
13 changes: 8 additions & 5 deletions Quotient/keyverificationsession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,12 @@ void KeyVerificationSession::handleEvent(const KeyVerificationEvent& baseEvent)
cancelVerification(UNKNOWN_METHOD);
return false;
}
m_commitment = event.commitment();
m_commitment = event.commitment().toLatin1();
if (!QByteArray::fromBase64Encoding(m_commitment,
QByteArray::AbortOnBase64DecodingErrors)) {
cancelVerification(INVALID_MESSAGE);
return false;
}
sendKey();
setState(WAITINGFORKEY);
return true;
Expand Down Expand Up @@ -228,12 +233,10 @@ void KeyVerificationSession::handleKey(const KeyVerificationKeyEvent& event)
olm_sas_set_their_key(olmData, eventKey.data(), unsignedSize(eventKey));

if (startSentByUs) {
const auto paddedCommitment =
const auto unpaddedCommitment =
QCryptographicHash::hash((event.key() % m_startEvent).toLatin1(),
QCryptographicHash::Sha256)
.toBase64();
const QLatin1String unpaddedCommitment(paddedCommitment.constData(),
QString::fromLatin1(paddedCommitment).indexOf(u'='));
.toBase64(QByteArray::OmitTrailingEquals);
if (unpaddedCommitment != m_commitment) {
qCWarning(E2EE) << "Commitment mismatch; aborting verification";
cancelVerification(MISMATCHED_COMMITMENT);
Expand Down
2 changes: 1 addition & 1 deletion Quotient/keyverificationsession.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public Q_SLOTS:
State m_state = INCOMING;
Error m_error = NONE;
QString m_startEvent{};
QString m_commitment{};
QByteArray m_commitment{};
bool macReceived = false;
bool m_verified = false;
QString m_pendingEdKeyId{};
Expand Down
8 changes: 5 additions & 3 deletions autotests/testkeyverification.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <Quotient/connection.h>
#include <Quotient/e2ee/qolmaccount.h>
#include <Quotient/e2ee/cryptoutils.h>

using namespace Quotient;

Expand All @@ -26,7 +27,8 @@ private Q_SLOTS:
QVERIFY(session->state() == KeyVerificationSession::WAITINGFORREADY);
session->handleEvent(KeyVerificationReadyEvent(transactionId, "ABCDEF"_L1, {SasV1Method}));
QVERIFY(session->state() == KeyVerificationSession::WAITINGFORACCEPT);
session->handleEvent(KeyVerificationAcceptEvent(transactionId, "commitment_TODO"_L1));
session->handleEvent(KeyVerificationAcceptEvent(
transactionId, QString::fromLatin1("commitment_TODO"_ba.toBase64())));
QVERIFY(session->state() == KeyVerificationSession::WAITINGFORKEY);
// Since we can't get the events sent by the session, we're limited by what we can test. This means that continuing here would force us to test
// the exact same path as the other test, which is useless.
Expand All @@ -51,8 +53,8 @@ private Q_SLOTS:
auto sas = olm_sas(new std::byte[olm_sas_size()]);
const auto randomLength = olm_create_sas_random_length(sas);
olm_create_sas(sas, getRandom(randomLength).data(), randomLength);
QByteArray keyBytes(olm_sas_pubkey_length(sas), '\0');
olm_sas_get_pubkey(sas, keyBytes.data(), keyBytes.size());
auto keyBytes = byteArrayForOlm(olm_sas_pubkey_length(sas));
olm_sas_get_pubkey(sas, keyBytes.data(), unsignedSize(keyBytes));
session->handleEvent(KeyVerificationKeyEvent(transactionId, QString::fromLatin1(keyBytes)));
QVERIFY(session->state() == KeyVerificationSession::WAITINGFORVERIFICATION);
session->sendMac();
Expand Down

0 comments on commit 6facc64

Please sign in to comment.