diff options
Diffstat (limited to 'src/client/OmemoCryptoProvider.cpp')
| -rw-r--r-- | src/client/OmemoCryptoProvider.cpp | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/src/client/OmemoCryptoProvider.cpp b/src/client/OmemoCryptoProvider.cpp new file mode 100644 index 00000000..e39124d4 --- /dev/null +++ b/src/client/OmemoCryptoProvider.cpp @@ -0,0 +1,239 @@ +// SPDX-FileCopyrightText: 2021 Linus Jahn <lnj@kaidan.im> +// SPDX-FileCopyrightText: 2022 Melvin Keskin <melvo@olomono.de> +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "OmemoCryptoProvider.h" + +#include "QXmppOmemoManager_p.h" +#include "QXmppUtils_p.h" + +#include <QStringBuilder> +#include <QtCrypto> + +using namespace QXmpp::Private; + +inline QXmppOmemoManagerPrivate *managerPrivate(void *ptr) +{ + return reinterpret_cast<QXmppOmemoManagerPrivate *>(ptr); +} + +static int random_func(uint8_t *data, size_t len, void *) +{ + generateRandomBytes(data, len); + return 0; +} + +int hmac_sha256_init_func(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data) +{ + auto *d = managerPrivate(user_data); + + if (!QCA::MessageAuthenticationCode::supportedTypes().contains(PAYLOAD_MESSAGE_AUTHENTICATION_CODE_TYPE)) { + d->warning("Message authentication code type '" % QString(PAYLOAD_MESSAGE_AUTHENTICATION_CODE_TYPE) % "' is not supported by this system"); + return -1; + } + + QCA::SymmetricKey authenticationKey(QByteArray(reinterpret_cast<const char *>(key), key_len)); + *hmac_context = new QCA::MessageAuthenticationCode(PAYLOAD_MESSAGE_AUTHENTICATION_CODE_TYPE, authenticationKey); + return 0; +} + +int hmac_sha256_update_func(void *hmac_context, const uint8_t *data, size_t data_len, void *) +{ + auto *messageAuthenticationCodeGenerator = reinterpret_cast<QCA::MessageAuthenticationCode *>(hmac_context); + messageAuthenticationCodeGenerator->update(QCA::MemoryRegion(QByteArray(reinterpret_cast<const char *>(data), data_len))); + return 0; +} + +int hmac_sha256_final_func(void *hmac_context, signal_buffer **output, void *user_data) +{ + auto *d = managerPrivate(user_data); + auto *messageAuthenticationCodeGenerator = reinterpret_cast<QCA::MessageAuthenticationCode *>(hmac_context); + + auto messageAuthenticationCode = messageAuthenticationCodeGenerator->final(); + if (!(*output = signal_buffer_create(reinterpret_cast<const uint8_t *>(messageAuthenticationCode.constData()), messageAuthenticationCode.size()))) { + d->warning("Message authentication code could not be loaded"); + return -1; + } + + return 0; +} + +void hmac_sha256_cleanup_func(void *hmac_context, void *) +{ + auto *messageAuthenticationCodeGenerator = reinterpret_cast<QCA::MessageAuthenticationCode *>(hmac_context); + delete messageAuthenticationCodeGenerator; +} + +int sha512_digest_init_func(void **digest_context, void *) +{ + *digest_context = new QCryptographicHash(QCryptographicHash::Sha512); + return 0; +} + +int sha512_digest_update_func(void *digest_context, const uint8_t *data, size_t data_len, void *) +{ + auto *hashGenerator = reinterpret_cast<QCryptographicHash *>(digest_context); + hashGenerator->addData(reinterpret_cast<const char *>(data), data_len); + return 0; +} + +int sha512_digest_final_func(void *digest_context, signal_buffer **output, void *user_data) +{ + auto *d = managerPrivate(user_data); + auto *hashGenerator = reinterpret_cast<QCryptographicHash *>(digest_context); + + auto hash = hashGenerator->result(); + if (!(*output = signal_buffer_create(reinterpret_cast<const uint8_t *>(hash.constData()), hash.size()))) { + d->warning("Hash could not be loaded"); + return -1; + } + + return 0; +} + +void sha512_digest_cleanup_func(void *digest_context, void *) +{ + auto *hashGenerator = reinterpret_cast<QCryptographicHash *>(digest_context); + delete hashGenerator; +} + +int encrypt_func(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) +{ + auto *d = managerPrivate(user_data); + + QString cipherName; + + switch (key_len) { + case 128 / 8: + cipherName = QStringLiteral("aes128"); + break; + case 192 / 8: + cipherName = QStringLiteral("aes192"); + break; + case 256 / 8: + cipherName = QStringLiteral("aes256"); + break; + default: + return -1; + } + + QCA::Cipher::Mode mode; + QCA::Cipher::Padding padding; + + switch (cipher) { + case SG_CIPHER_AES_CTR_NOPADDING: + mode = QCA::Cipher::CTR; + padding = QCA::Cipher::NoPadding; + break; + case SG_CIPHER_AES_CBC_PKCS5: + mode = QCA::Cipher::CBC; + padding = QCA::Cipher::PKCS7; + break; + default: + return -2; + } + + const auto encryptionKey = QCA::SymmetricKey(QByteArray(reinterpret_cast<const char *>(key), key_len)); + const auto initializationVector = QCA::InitializationVector(QByteArray(reinterpret_cast<const char *>(iv), iv_len)); + QCA::Cipher encryptionCipher(cipherName, mode, padding, QCA::Encode, encryptionKey, initializationVector); + + auto encryptedData = encryptionCipher.process(QCA::MemoryRegion(QByteArray(reinterpret_cast<const char *>(plaintext), plaintext_len))); + + if (encryptedData.isEmpty()) { + return -3; + } + + if (!(*output = signal_buffer_create(reinterpret_cast<const uint8_t *>(encryptedData.constData()), encryptedData.size()))) { + d->warning("Encrypted data could not be loaded"); + return -4; + } + + return 0; +} + +int decrypt_func(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) +{ + auto *d = managerPrivate(user_data); + + QString cipherName; + + switch (key_len) { + case 128 / 8: + cipherName = QStringLiteral("aes128"); + break; + case 192 / 8: + cipherName = QStringLiteral("aes192"); + break; + case 256 / 8: + cipherName = QStringLiteral("aes256"); + break; + default: + return -1; + } + + QCA::Cipher::Mode mode; + QCA::Cipher::Padding padding; + + switch (cipher) { + case SG_CIPHER_AES_CTR_NOPADDING: + mode = QCA::Cipher::CTR; + padding = QCA::Cipher::NoPadding; + break; + case SG_CIPHER_AES_CBC_PKCS5: + mode = QCA::Cipher::CBC; + padding = QCA::Cipher::PKCS7; + break; + default: + return -2; + } + + const auto encryptionKey = QCA::SymmetricKey(QByteArray(reinterpret_cast<const char *>(key), key_len)); + const auto initializationVector = QCA::InitializationVector(QByteArray(reinterpret_cast<const char *>(iv), iv_len)); + QCA::Cipher decryptionCipher(cipherName, mode, padding, QCA::Decode, encryptionKey, initializationVector); + + auto decryptedData = decryptionCipher.process(QCA::MemoryRegion(QByteArray(reinterpret_cast<const char *>(ciphertext), ciphertext_len))); + + if (decryptedData.isEmpty()) { + return -3; + } + + if (!(*output = signal_buffer_create(reinterpret_cast<const uint8_t *>(decryptedData.constData()), decryptedData.size()))) { + d->warning("Decrypted data could not be loaded"); + return -4; + } + + return 0; +} + +namespace QXmpp::Omemo::Private { + +signal_crypto_provider createOmemoCryptoProvider(QXmppOmemoManagerPrivate *d) +{ + return { + random_func, + hmac_sha256_init_func, + hmac_sha256_update_func, + hmac_sha256_final_func, + hmac_sha256_cleanup_func, + sha512_digest_init_func, + sha512_digest_update_func, + sha512_digest_final_func, + sha512_digest_cleanup_func, + encrypt_func, + decrypt_func, + d, + }; +} + +} // namespace QXmpp::Omemo::Private |
