omemo_crypto_qt

#include <cstdint>
#include <stddef.h>
#include <QCryptographicHash>
#include <Qca-qt5/QtCrypto/QtCrypto>

#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
#include <QRandomGenerator>
#endif

//#include <signal_protocol.h>
//#include <signal_protocol_types.h>

/**
 * Callback for a secure random number generator.
 * This function shall fill the provided buffer with random bytes.
 *
 * @param data pointer to the output buffer
 * @param len size of the output buffer
 * @return 0 on success, negative on failure
 */
int random_func(uint8_t *data, size_t len, void *user_data)
{
    static_assert(sizeof(uint8_t) == 1);

    // QRandomGenerator only generates uint32
    auto len32 = len / sizeof(uint32_t);
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
    auto *generator = QRandomGenerator::system();
    if (len32) {
        generator->fillRange(reinterpret_cast<uint32_t *>(data), len32);
    }

    // fill the rest
    for (size_t i = len - len % sizeof(uint32_t); i < len; i++) {
        data[i] = generator->generate() % (std::numeric_limits<uint8_t>::max() + 1);
    }
#else
    // fill range with 32 bit ints
    for (size_t i = 0; i < len32; i++) {
        reinterpret_cast<int *>(data)[i] = qrand();
    }
    // fill the rest
    for (size_t i = len - len % sizeof(uint32_t); i < len; i++) {
        auto rand = qrand();
        data[i] = *reinterpret_cast<uint *>(&rand) % (std::numeric_limits<uint8_t>::max() + 1);
    }
#endif

    return 0;
}

/**
 * Callback for an HMAC-SHA256 implementation.
 * This function shall initialize an HMAC context with the provided key.
 *
 * @param hmac_context private HMAC context pointer
 * @param key pointer to the key
 * @param key_len length of the key
 * @return 0 on success, negative on failure
 */
int hmac_sha256_init_func(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data)
{
    if (!QCA::MessageAuthenticationCode::supportedTypes().contains(QStringLiteral("hmac(sha256)"))) {
        return -1;
    }
    QCA::SymmetricKey symmKey(QByteArray(reinterpret_cast<const char *>(key), key_len));
    auto *hmac = new QCA::MessageAuthenticationCode(QStringLiteral("hmac(sha256)"), symmKey);
    *hmac_context = static_cast<void *>(hmac);
    return 0;
}

/**
 * Callback for an HMAC-SHA256 implementation.
 * This function shall update the HMAC context with the provided data
 *
 * @param hmac_context private HMAC context pointer
 * @param data pointer to the data
 * @param data_len length of the data
 * @return 0 on success, negative on failure
 */
int hmac_sha256_update_func(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data)
{
    auto *hmac = reinterpret_cast<QCA::MessageAuthenticationCode *>(hmac_context);
    hmac->process(QCA::MemoryRegion(QByteArray(reinterpret_cast<const char *>(data), data_len)));
    return 0;
}

/**
 * Callback for an HMAC-SHA256 implementation.
 * This function shall finalize an HMAC calculation and populate the output
 * buffer with the result.
 *
 * @param hmac_context private HMAC context pointer
 * @param output buffer to be allocated and populated with the result
 * @return 0 on success, negative on failure
 */
int hmac_sha256_final_func(void *hmac_context, signal_buffer **output, void *user_data)
{
    auto *hmac = reinterpret_cast<QCA::MessageAuthenticationCode *>(hmac_context);
    auto result = hmac->final();
    memcpy(output, result.constData(), result.size());
    return 0;
}

/**
 * Callback for an HMAC-SHA256 implementation.
 * This function shall free the private context allocated in
 * hmac_sha256_init_func.
 *
 * @param hmac_context private HMAC context pointer
 */
void hmac_sha256_cleanup_func(void *hmac_context, void *user_data)
{
    auto *hmac = reinterpret_cast<QCA::MessageAuthenticationCode *>(hmac_context);
    delete hmac;
}

/**
 * Callback for a SHA512 message digest implementation.
 * This function shall initialize a digest context.
 *
 * @param digest_context private digest context pointer
 * @return 0 on success, negative on failure
 */
int sha512_digest_init_func(void **digest_context, void *user_data)
{
    *digest_context = new QCryptographicHash(QCryptographicHash::Sha512);
    return 0;
}

/**
 * Callback for a SHA512 message digest implementation.
 * This function shall update the digest context with the provided data.
 *
 * @param digest_context private digest context pointer
 * @param data pointer to the data
 * @param data_len length of the data
 * @return 0 on success, negative on failure
 */
int sha512_digest_update_func(void *digest_context, const uint8_t *data, size_t data_len, void *user_data)
{
    auto *hash = reinterpret_cast<QCryptographicHash *>(digest_context);
    hash->addData(reinterpret_cast<const char *>(data), data_len);
    return 0;
}

/**
 * Callback for a SHA512 message digest implementation.
 * This function shall finalize the digest calculation, populate the
 * output buffer with the result, and prepare the context for reuse.
 *
 * @param digest_context private digest context pointer
 * @param output buffer to be allocated and populated with the result
 * @return 0 on success, negative on failure
 */
int sha512_digest_final_func(void *digest_context, signal_buffer **output, void *user_data)
{
    auto *hash = reinterpret_cast<QCryptographicHash *>(digest_context);
    auto result = hash->result();
    memcpy(output, result.constData(), result.size());
    return 0;
}

/**
 * Callback for a SHA512 message digest implementation.
 * This function shall free the private context allocated in
 * sha512_digest_init_func.
 *
 * @param digest_context private digest context pointer
 */
void sha512_digest_cleanup_func(void *digest_context, void *user_data)
{
    auto *hash = reinterpret_cast<QCryptographicHash *>(digest_context);
    delete hash;
}

/**
 * Callback for an AES encryption implementation.
 *
 * @param output buffer to be allocated and populated with the ciphertext
 * @param cipher specific cipher variant to use, either SG_CIPHER_AES_CTR_NOPADDING or SG_CIPHER_AES_CBC_PKCS5
 * @param key the encryption key
 * @param key_len length of the encryption key
 * @param iv the initialization vector
 * @param iv_len length of the initialization vector
 * @param plaintext the plaintext to encrypt
 * @param plaintext_len length of the plaintext
 * @return 0 on success, negative on failure
 */
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)
{
    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 symmKey = QCA::SymmetricKey(QByteArray(reinterpret_cast<const char *>(key), key_len));
    const auto initVec = QCA::InitializationVector(QByteArray(reinterpret_cast<const char *>(iv), iv_len));
    QCA::Cipher cipherProcessor(cipherName, mode, padding, QCA::Encode, symmKey);
    auto result = cipherProcessor.process(QCA::MemoryRegion(QByteArray(reinterpret_cast<const char *>(plaintext), plaintext_len)));
    if (result.isEmpty()) {
        return -3;
    }
    memcpy(output, result.constData(), result.size());
    return 0;
}

/**
 * Callback for an AES decryption implementation.
 *
 * @param output buffer to be allocated and populated with the plaintext
 * @param cipher specific cipher variant to use, either SG_CIPHER_AES_CTR_NOPADDING or SG_CIPHER_AES_CBC_PKCS5
 * @param key the encryption key
 * @param key_len length of the encryption key
 * @param iv the initialization vector
 * @param iv_len length of the initialization vector
 * @param ciphertext the ciphertext to decrypt
 * @param ciphertext_len length of the ciphertext
 * @return 0 on success, negative on failure
 */
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)
{
    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 symmKey = QCA::SymmetricKey(QByteArray(reinterpret_cast<const char *>(key), key_len));
    const auto initVec = QCA::InitializationVector(QByteArray(reinterpret_cast<const char *>(iv), iv_len));
    QCA::Cipher cipherProcessor(cipherName, mode, padding, QCA::Decode, symmKey);
    auto result = cipherProcessor.process(QCA::MemoryRegion(QByteArray(reinterpret_cast<const char *>(ciphertext), ciphertext_len)));
    if (result.isEmpty()) {
        return -3;
    }
    memcpy(output, result.constData(), result.size());
    return 0;
}