aboutsummaryrefslogtreecommitdiff
path: root/lib/events/encryptedfile.cpp
blob: 33ebb5142ea5f35c85b9bd2e5177a3241fa429cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

#include "encryptedfile.h"
#include "logging.h"

#ifdef Quotient_E2EE_ENABLED
#include <openssl/evp.h>
#include <QtCore/QCryptographicHash>
#include "e2ee/qolmutils.h"
#endif

using namespace Quotient;

QByteArray EncryptedFile::decryptFile(const QByteArray& ciphertext) const
{
#ifdef Quotient_E2EE_ENABLED
    auto _key = key.k;
    const auto keyBytes = QByteArray::fromBase64(
        _key.replace(u'_', u'/').replace(u'-', u'+').toLatin1());
    const auto sha256 = QByteArray::fromBase64(hashes["sha256"].toLatin1());
    if (sha256
        != QCryptographicHash::hash(ciphertext, QCryptographicHash::Sha256)) {
        qCWarning(E2EE) << "Hash verification failed for file";
        return {};
    }
    {
        int length;
        auto* ctx = EVP_CIPHER_CTX_new();
        QByteArray plaintext(ciphertext.size() + EVP_MAX_BLOCK_LENGTH
                                 - 1,
                             '\0');
        EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), nullptr,
                           reinterpret_cast<const unsigned char*>(
                               keyBytes.data()),
                           reinterpret_cast<const unsigned char*>(
                               QByteArray::fromBase64(iv.toLatin1()).data()));
        EVP_DecryptUpdate(
            ctx, reinterpret_cast<unsigned char*>(plaintext.data()), &length,
            reinterpret_cast<const unsigned char*>(ciphertext.data()),
            ciphertext.size());
        EVP_DecryptFinal_ex(ctx,
                            reinterpret_cast<unsigned char*>(plaintext.data())
                                + length,
                            &length);
        EVP_CIPHER_CTX_free(ctx);
        return plaintext.left(ciphertext.size());
    }
#else
    qWarning(MAIN) << "This build of libQuotient doesn't support E2EE, "
                      "cannot decrypt the file";
    return ciphertext;
#endif
}

std::pair<EncryptedFile, QByteArray> EncryptedFile::encryptFile(const QByteArray &plainText)
{
#ifdef Quotient_E2EE_ENABLED
    QByteArray k = getRandom(32);
    auto kBase64 = k.toBase64();
    QByteArray iv = getRandom(16);
    JWK key = {"oct"_ls, {"encrypt"_ls, "decrypt"_ls}, "A256CTR"_ls, QString(k.toBase64()).replace(u'/', u'_').replace(u'+', u'-').left(kBase64.indexOf('=')), true};

    int length;
    auto* ctx = EVP_CIPHER_CTX_new();
    EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), nullptr, reinterpret_cast<const unsigned char*>(k.data()),reinterpret_cast<const unsigned char*>(iv.data()));
    const auto blockSize = EVP_CIPHER_CTX_block_size(ctx);
    QByteArray cipherText(plainText.size() + blockSize - 1, '\0');
    EVP_EncryptUpdate(ctx, reinterpret_cast<unsigned char*>(cipherText.data()), &length, reinterpret_cast<const unsigned char*>(plainText.data()), plainText.size());
    EVP_EncryptFinal_ex(ctx, reinterpret_cast<unsigned char*>(cipherText.data()) + length, &length);
    EVP_CIPHER_CTX_free(ctx);

    auto hash = QCryptographicHash::hash(cipherText, QCryptographicHash::Sha256).toBase64();
    auto ivBase64 = iv.toBase64();
    EncryptedFile file = {{}, key, ivBase64.left(ivBase64.indexOf('=')), {{QStringLiteral("sha256"), hash.left(hash.indexOf('='))}}, "v2"_ls};
    return {file, cipherText};
#else
    return {};
#endif
}

void JsonObjectConverter<EncryptedFile>::dumpTo(QJsonObject& jo,
                                                const EncryptedFile& pod)
{
    addParam<>(jo, QStringLiteral("url"), pod.url);
    addParam<>(jo, QStringLiteral("key"), pod.key);
    addParam<>(jo, QStringLiteral("iv"), pod.iv);
    addParam<>(jo, QStringLiteral("hashes"), pod.hashes);
    addParam<>(jo, QStringLiteral("v"), pod.v);
}

void JsonObjectConverter<EncryptedFile>::fillFrom(const QJsonObject& jo,
                                                  EncryptedFile& pod)
{
    fromJson(jo.value("url"_ls), pod.url);
    fromJson(jo.value("key"_ls), pod.key);
    fromJson(jo.value("iv"_ls), pod.iv);
    fromJson(jo.value("hashes"_ls), pod.hashes);
    fromJson(jo.value("v"_ls), pod.v);
}

void JsonObjectConverter<JWK>::dumpTo(QJsonObject &jo, const JWK &pod)
{
    addParam<>(jo, QStringLiteral("kty"), pod.kty);
    addParam<>(jo, QStringLiteral("key_ops"), pod.keyOps);
    addParam<>(jo, QStringLiteral("alg"), pod.alg);
    addParam<>(jo, QStringLiteral("k"), pod.k);
    addParam<>(jo, QStringLiteral("ext"), pod.ext);
}

void JsonObjectConverter<JWK>::fillFrom(const QJsonObject &jo, JWK &pod)
{
    fromJson(jo.value("kty"_ls), pod.kty);
    fromJson(jo.value("key_ops"_ls), pod.keyOps);
    fromJson(jo.value("alg"_ls), pod.alg);
    fromJson(jo.value("k"_ls), pod.k);
    fromJson(jo.value("ext"_ls), pod.ext);
}