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
|
// 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();
QByteArray cipherText(plainText.size(), plainText.size() + EVP_MAX_BLOCK_LENGTH - 1);
EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), nullptr, reinterpret_cast<const unsigned char*>(k.data()),reinterpret_cast<const unsigned char*>(iv.data()));
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);
}
|