// SPDX-FileCopyrightText: 2021 Linus Jahn // SPDX-FileCopyrightText: 2022 Melvin Keskin // // SPDX-License-Identifier: LGPL-2.1-or-later #include "OmemoCryptoProvider.h" #include "QXmppOmemoManager_p.h" #include "QXmppUtils_p.h" #include #include using namespace QXmpp::Private; inline QXmppOmemoManagerPrivate *managerPrivate(void *ptr) { return reinterpret_cast(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(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(hmac_context); messageAuthenticationCodeGenerator->update(QCA::MemoryRegion(QByteArray(reinterpret_cast(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(hmac_context); auto messageAuthenticationCode = messageAuthenticationCodeGenerator->final(); if (!(*output = signal_buffer_create(reinterpret_cast(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(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(digest_context); hashGenerator->addData(reinterpret_cast(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(digest_context); auto hash = hashGenerator->result(); if (!(*output = signal_buffer_create(reinterpret_cast(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(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(key), key_len)); const auto initializationVector = QCA::InitializationVector(QByteArray(reinterpret_cast(iv), iv_len)); QCA::Cipher encryptionCipher(cipherName, mode, padding, QCA::Encode, encryptionKey, initializationVector); auto encryptedData = encryptionCipher.process(QCA::MemoryRegion(QByteArray(reinterpret_cast(plaintext), plaintext_len))); if (encryptedData.isEmpty()) { return -3; } if (!(*output = signal_buffer_create(reinterpret_cast(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(key), key_len)); const auto initializationVector = QCA::InitializationVector(QByteArray(reinterpret_cast(iv), iv_len)); QCA::Cipher decryptionCipher(cipherName, mode, padding, QCA::Decode, encryptionKey, initializationVector); auto decryptedData = decryptionCipher.process(QCA::MemoryRegion(QByteArray(reinterpret_cast(ciphertext), ciphertext_len))); if (decryptedData.isEmpty()) { return -3; } if (!(*output = signal_buffer_create(reinterpret_cast(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