2017-09-11 16:00:01 +03:00
|
|
|
#include "clientsideencryption.h"
|
2017-09-11 17:52:57 +03:00
|
|
|
#include "account.h"
|
|
|
|
#include "capabilities.h"
|
2017-09-12 12:21:53 +03:00
|
|
|
#include "networkjobs.h"
|
2017-12-12 21:36:47 +03:00
|
|
|
#include "clientsideencryptionjobs.h"
|
2017-11-27 18:11:21 +03:00
|
|
|
#include "theme.h"
|
|
|
|
#include "creds/abstractcredentials.h"
|
2017-09-11 17:52:57 +03:00
|
|
|
|
2017-09-12 14:44:42 +03:00
|
|
|
#include <openssl/rsa.h>
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
#include <openssl/pem.h>
|
2017-09-14 23:45:12 +03:00
|
|
|
#include <openssl/err.h>
|
2017-10-30 21:05:55 +03:00
|
|
|
#include <openssl/engine.h>
|
2017-09-12 14:44:42 +03:00
|
|
|
|
2017-09-12 21:02:17 +03:00
|
|
|
#include <map>
|
|
|
|
|
2017-09-12 15:35:05 +03:00
|
|
|
#include <cstdio>
|
|
|
|
|
2017-09-11 17:52:57 +03:00
|
|
|
#include <QDebug>
|
|
|
|
#include <QLoggingCategory>
|
2017-09-12 12:21:53 +03:00
|
|
|
#include <QFileInfo>
|
|
|
|
#include <QDir>
|
2017-09-14 16:57:41 +03:00
|
|
|
#include <QJsonObject>
|
2017-11-20 23:38:17 +03:00
|
|
|
#include <QXmlStreamReader>
|
|
|
|
#include <QXmlStreamNamespaceDeclaration>
|
|
|
|
#include <QStack>
|
2017-11-28 14:36:35 +03:00
|
|
|
#include <QInputDialog>
|
|
|
|
#include <QLineEdit>
|
2017-09-11 17:52:57 +03:00
|
|
|
|
2017-11-27 18:11:21 +03:00
|
|
|
#include <keychain.h>
|
|
|
|
|
2017-09-14 19:39:18 +03:00
|
|
|
#include "wordlist.h"
|
|
|
|
|
2017-10-31 18:06:01 +03:00
|
|
|
QDebug operator<<(QDebug out, const std::string& str)
|
|
|
|
{
|
|
|
|
out << QString::fromStdString(str);
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2017-11-27 18:11:21 +03:00
|
|
|
using namespace QKeychain;
|
|
|
|
|
2017-09-11 17:52:57 +03:00
|
|
|
namespace OCC
|
|
|
|
{
|
|
|
|
|
2017-09-12 12:21:53 +03:00
|
|
|
Q_LOGGING_CATEGORY(lcCse, "sync.clientsideencryption", QtInfoMsg)
|
2017-09-11 17:52:57 +03:00
|
|
|
|
2017-10-18 21:21:35 +03:00
|
|
|
QString baseUrl(){
|
|
|
|
return QStringLiteral("ocs/v2.php/apps/end_to_end_encryption/api/v1/");
|
|
|
|
}
|
|
|
|
|
|
|
|
QString baseDirectory() {
|
|
|
|
return QDir::homePath() + QStringLiteral("/.nextcloud-keys/");
|
|
|
|
}
|
2017-09-11 17:52:57 +03:00
|
|
|
|
2017-09-14 23:45:12 +03:00
|
|
|
namespace {
|
|
|
|
void handleErrors(void)
|
|
|
|
{
|
2017-09-15 18:59:14 +03:00
|
|
|
ERR_print_errors_fp(stdout); // This line is not printing anything.
|
|
|
|
fflush(stdout);
|
2017-09-14 23:45:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-24 22:58:20 +03:00
|
|
|
class EncryptionHelper {
|
|
|
|
public:
|
2017-11-27 18:11:21 +03:00
|
|
|
using Password = QByteArray;
|
|
|
|
using Salt = QByteArray;
|
2017-11-27 17:21:29 +03:00
|
|
|
|
2017-11-25 00:10:28 +03:00
|
|
|
static QByteArray generateRandom(int size);
|
2017-11-27 17:21:29 +03:00
|
|
|
static QPair<Password, Salt> generatePassword(const QString &wordlist);
|
2017-11-25 00:10:28 +03:00
|
|
|
static QByteArray encryptPrivateKey(
|
2017-11-27 17:21:29 +03:00
|
|
|
const QByteArray& key,
|
|
|
|
const QByteArray& privateKey
|
2017-11-25 00:10:28 +03:00
|
|
|
);
|
2017-11-28 14:36:35 +03:00
|
|
|
static QByteArray decryptPrivateKey(
|
|
|
|
const QByteArray& key,
|
|
|
|
const QByteArray& data
|
|
|
|
);
|
|
|
|
static QByteArray encryptStringSymmetric(
|
|
|
|
const QByteArray& key,
|
|
|
|
const QByteArray& data
|
|
|
|
);
|
|
|
|
static QByteArray decryptStringSymmetric(
|
|
|
|
const QByteArray& key,
|
|
|
|
const QByteArray& data
|
|
|
|
);
|
|
|
|
static QByteArray encryptStringAsymmetric(
|
2017-12-12 22:14:31 +03:00
|
|
|
EVP_PKEY *publicKey,
|
2017-11-28 14:36:35 +03:00
|
|
|
const QByteArray& data
|
|
|
|
);
|
2017-11-27 18:11:21 +03:00
|
|
|
static QByteArray BIO2ByteArray(BIO *b);
|
2017-11-24 22:58:20 +03:00
|
|
|
};
|
|
|
|
|
2017-11-25 00:10:28 +03:00
|
|
|
QByteArray EncryptionHelper::generateRandom(int size) {
|
|
|
|
unsigned char *tmp = (unsigned char *)malloc(sizeof(unsigned char) * size);
|
|
|
|
|
|
|
|
int ret = RAND_bytes(tmp, size);
|
|
|
|
if (ret != 1) {
|
|
|
|
qCInfo(lcCse()) << "Random byte generation failed!";
|
|
|
|
// Error out?
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray result((const char *)tmp, size);
|
|
|
|
free(tmp);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-11-27 17:21:29 +03:00
|
|
|
QPair<EncryptionHelper::Password, EncryptionHelper::Salt> EncryptionHelper::generatePassword(const QString& wordlist) {
|
2017-11-24 22:58:20 +03:00
|
|
|
qCInfo(lcCse()) << "Start encryption key generation!";
|
|
|
|
|
|
|
|
// TODO generate salt
|
|
|
|
const unsigned char *_salt = (unsigned char *)"$4$YmBjm3hk$Qb74D5IUYwghUmzsMqeNFx5z0/8$";
|
|
|
|
const int saltLen = 40;
|
|
|
|
|
2017-11-28 14:36:35 +03:00
|
|
|
QByteArray salt((const char *)_salt, saltLen);
|
2017-11-24 22:58:20 +03:00
|
|
|
|
|
|
|
const int iterationCount = 1024;
|
|
|
|
const int keyStrength = 256;
|
|
|
|
const int keyLength = keyStrength/8;
|
|
|
|
|
|
|
|
unsigned char secretKey[keyLength];
|
|
|
|
|
|
|
|
int ret = PKCS5_PBKDF2_HMAC_SHA1(
|
|
|
|
wordlist.toLocal8Bit().constData(), // const char *password,
|
|
|
|
wordlist.size(), // int password length,
|
2017-11-25 00:10:28 +03:00
|
|
|
(const unsigned char *)salt.constData(), // const unsigned char *salt,
|
2017-11-24 22:58:20 +03:00
|
|
|
salt.size(), // int saltlen,
|
|
|
|
iterationCount, // int iterations,
|
|
|
|
keyLength, // int keylen,
|
|
|
|
secretKey // unsigned char *out
|
|
|
|
);
|
|
|
|
|
|
|
|
if (ret != 1) {
|
|
|
|
qCInfo(lcCse()) << "Failed to generate encryption key";
|
|
|
|
// Error out?
|
|
|
|
}
|
|
|
|
|
|
|
|
qCInfo(lcCse()) << "Encryption key generated!";
|
|
|
|
|
2017-11-27 17:21:29 +03:00
|
|
|
QByteArray password((const char *)secretKey, keyLength);
|
|
|
|
return {password, salt};
|
2017-11-25 00:10:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray EncryptionHelper::encryptPrivateKey(
|
2017-11-27 17:21:29 +03:00
|
|
|
const QByteArray& key,
|
|
|
|
const QByteArray& privateKey
|
2017-11-25 00:10:28 +03:00
|
|
|
) {
|
|
|
|
|
|
|
|
QByteArray iv = generateRandom(12);
|
|
|
|
|
|
|
|
EVP_CIPHER_CTX *ctx;
|
|
|
|
/* Create and initialise the context */
|
|
|
|
if(!(ctx = EVP_CIPHER_CTX_new())) {
|
|
|
|
qCInfo(lcCse()) << "Error creating cipher";
|
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialise the decryption operation. */
|
|
|
|
if(!EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)) {
|
|
|
|
qCInfo(lcCse()) << "Error initializing context with aes_256";
|
|
|
|
handleErrors();
|
2017-11-24 22:58:20 +03:00
|
|
|
}
|
|
|
|
|
2017-11-25 00:10:28 +03:00
|
|
|
// No padding
|
|
|
|
EVP_CIPHER_CTX_set_padding(ctx, 0);
|
|
|
|
|
|
|
|
/* Set IV length. */
|
|
|
|
if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), NULL)) {
|
|
|
|
qCInfo(lcCse()) << "Error setting iv length";
|
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialise key and IV */
|
|
|
|
if(!EVP_EncryptInit_ex(ctx, NULL, NULL, (unsigned char *)key.constData(), (unsigned char *)iv.constData())) {
|
|
|
|
qCInfo(lcCse()) << "Error initialising key and iv";
|
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
// We write the base64 encoded private key
|
|
|
|
QByteArray privateKeyB64 = privateKey.toBase64();
|
|
|
|
|
|
|
|
// Make sure we have enough room in the cipher text
|
|
|
|
unsigned char *ctext = (unsigned char *)malloc(sizeof(unsigned char) * (privateKeyB64.size() + 32));
|
|
|
|
|
|
|
|
// Do the actual encryption
|
|
|
|
int len = 0;
|
|
|
|
if(!EVP_EncryptUpdate(ctx, ctext, &len, (unsigned char *)privateKeyB64.constData(), privateKeyB64.size())) {
|
|
|
|
qCInfo(lcCse()) << "Error encrypting";
|
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
int clen = len;
|
|
|
|
|
|
|
|
/* Finalise the encryption. Normally ciphertext bytes may be written at
|
|
|
|
* this stage, but this does not occur in GCM mode
|
|
|
|
*/
|
|
|
|
if(1 != EVP_EncryptFinal_ex(ctx, ctext + len, &len)) {
|
|
|
|
qCInfo(lcCse()) << "Error finalizing encryption";
|
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
clen += len;
|
|
|
|
|
|
|
|
/* Get the tag */
|
|
|
|
unsigned char *tag = (unsigned char *)calloc(sizeof(unsigned char), 16);
|
|
|
|
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
|
|
|
|
qCInfo(lcCse()) << "Error getting the tag";
|
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray cipherTXT((char *)ctext, clen);
|
|
|
|
cipherTXT.append((char *)tag, 16);
|
|
|
|
|
|
|
|
QByteArray result = cipherTXT.toBase64();
|
|
|
|
result += "fA==";
|
|
|
|
result += iv.toBase64();
|
|
|
|
|
2017-11-24 22:58:20 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-11-28 14:36:35 +03:00
|
|
|
QByteArray EncryptionHelper::decryptPrivateKey(const QByteArray& key, const QByteArray& data) {
|
|
|
|
qCInfo(lcCse()) << "decryptStringSymmetric key: " << key;
|
|
|
|
qCInfo(lcCse()) << "decryptStringSymmetric data: " << data;
|
|
|
|
|
|
|
|
int sep = data.indexOf("fA==");
|
|
|
|
qCInfo(lcCse()) << "sep at" << sep;
|
|
|
|
|
|
|
|
QByteArray cipherTXT64 = data.left(sep);
|
|
|
|
QByteArray ivB64 = data.right(data.size() - sep - 4);
|
|
|
|
|
|
|
|
qCInfo(lcCse()) << "decryptStringSymmetric cipherTXT: " << cipherTXT64;
|
|
|
|
qCInfo(lcCse()) << "decryptStringSymmetric IV: " << ivB64;
|
|
|
|
|
|
|
|
QByteArray cipherTXT = QByteArray::fromBase64(cipherTXT64);
|
|
|
|
QByteArray iv = QByteArray::fromBase64(ivB64);
|
|
|
|
|
|
|
|
QByteArray tag = cipherTXT.right(16);
|
|
|
|
cipherTXT.chop(16);
|
|
|
|
|
|
|
|
// Init
|
|
|
|
EVP_CIPHER_CTX *ctx;
|
|
|
|
|
|
|
|
/* Create and initialise the context */
|
|
|
|
if(!(ctx = EVP_CIPHER_CTX_new())) {
|
|
|
|
qCInfo(lcCse()) << "Error creating cipher";
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialise the decryption operation. */
|
|
|
|
if(!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)) {
|
|
|
|
qCInfo(lcCse()) << "Error initialising context with aes 256";
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set IV length. Not necessary if this is 12 bytes (96 bits) */
|
|
|
|
if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), NULL)) {
|
|
|
|
qCInfo(lcCse()) << "Error setting IV size";
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialise key and IV */
|
|
|
|
if(!EVP_DecryptInit_ex(ctx, NULL, NULL, (unsigned char *)key.constData(), (unsigned char *)iv.constData())) {
|
|
|
|
qCInfo(lcCse()) << "Error initialising key and iv";
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char *ptext = (unsigned char *)calloc(cipherTXT.size() + 16, sizeof(unsigned char));
|
|
|
|
int plen;
|
|
|
|
|
|
|
|
/* Provide the message to be decrypted, and obtain the plaintext output.
|
|
|
|
* EVP_DecryptUpdate can be called multiple times if necessary
|
|
|
|
*/
|
|
|
|
if(!EVP_DecryptUpdate(ctx, ptext, &plen, (unsigned char *)cipherTXT.constData(), cipherTXT.size())) {
|
|
|
|
qCInfo(lcCse()) << "Could not decrypt";
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
free(ptext);
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set expected tag value. Works in OpenSSL 1.0.1d and later */
|
|
|
|
if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag.size(), (unsigned char *)tag.constData())) {
|
|
|
|
qCInfo(lcCse()) << "Could not set tag";
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
free(ptext);
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finalise the decryption. A positive return value indicates success,
|
|
|
|
* anything else is a failure - the plaintext is not trustworthy.
|
|
|
|
*/
|
|
|
|
int len = plen;
|
|
|
|
if (EVP_DecryptFinal_ex(ctx, ptext + plen, &len) == 0) {
|
|
|
|
qCInfo(lcCse()) << "Tag did not match!";
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
free(ptext);
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray result((char *)ptext, plen);
|
|
|
|
|
|
|
|
free(ptext);
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
|
|
|
|
return QByteArray::fromBase64(result);
|
|
|
|
}
|
|
|
|
|
2017-11-27 17:21:29 +03:00
|
|
|
QByteArray EncryptionHelper::decryptStringSymmetric(const QByteArray& key, const QByteArray& data) {
|
2017-11-28 14:36:35 +03:00
|
|
|
qCInfo(lcCse()) << "decryptStringSymmetric key: " << key;
|
|
|
|
qCInfo(lcCse()) << "decryptStringSymmetric data: " << data;
|
|
|
|
|
|
|
|
int sep = data.indexOf("fA==");
|
|
|
|
qCInfo(lcCse()) << "sep at" << sep;
|
|
|
|
|
|
|
|
QByteArray cipherTXT64 = data.left(sep);
|
|
|
|
QByteArray ivB64 = data.right(data.size() - sep - 4);
|
|
|
|
|
|
|
|
qCInfo(lcCse()) << "decryptStringSymmetric cipherTXT: " << cipherTXT64;
|
|
|
|
qCInfo(lcCse()) << "decryptStringSymmetric IV: " << ivB64;
|
|
|
|
|
|
|
|
QByteArray cipherTXT = QByteArray::fromBase64(cipherTXT64);
|
|
|
|
QByteArray iv = QByteArray::fromBase64(ivB64);
|
|
|
|
|
|
|
|
QByteArray tag = cipherTXT.right(16);
|
|
|
|
cipherTXT.chop(16);
|
|
|
|
|
|
|
|
// Init
|
|
|
|
EVP_CIPHER_CTX *ctx;
|
|
|
|
|
|
|
|
/* Create and initialise the context */
|
|
|
|
if(!(ctx = EVP_CIPHER_CTX_new())) {
|
|
|
|
qCInfo(lcCse()) << "Error creating cipher";
|
|
|
|
return QByteArray();
|
|
|
|
}
|
2017-11-25 23:43:15 +03:00
|
|
|
|
2017-11-28 14:36:35 +03:00
|
|
|
/* Initialise the decryption operation. */
|
|
|
|
if(!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL)) {
|
|
|
|
qCInfo(lcCse()) << "Error initialising context with aes 128";
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set IV length. Not necessary if this is 12 bytes (96 bits) */
|
|
|
|
if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), NULL)) {
|
|
|
|
qCInfo(lcCse()) << "Error setting IV size";
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialise key and IV */
|
|
|
|
if(!EVP_DecryptInit_ex(ctx, NULL, NULL, (unsigned char *)key.constData(), (unsigned char *)iv.constData())) {
|
|
|
|
qCInfo(lcCse()) << "Error initialising key and iv";
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char *ptext = (unsigned char *)calloc(cipherTXT.size() + 16, sizeof(unsigned char));
|
|
|
|
int plen;
|
|
|
|
|
|
|
|
/* Provide the message to be decrypted, and obtain the plaintext output.
|
|
|
|
* EVP_DecryptUpdate can be called multiple times if necessary
|
|
|
|
*/
|
|
|
|
if(!EVP_DecryptUpdate(ctx, ptext, &plen, (unsigned char *)cipherTXT.constData(), cipherTXT.size())) {
|
|
|
|
qCInfo(lcCse()) << "Could not decrypt";
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
free(ptext);
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set expected tag value. Works in OpenSSL 1.0.1d and later */
|
|
|
|
if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag.size(), (unsigned char *)tag.constData())) {
|
|
|
|
qCInfo(lcCse()) << "Could not set tag";
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
free(ptext);
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finalise the decryption. A positive return value indicates success,
|
|
|
|
* anything else is a failure - the plaintext is not trustworthy.
|
|
|
|
*/
|
|
|
|
int len = plen;
|
|
|
|
if (EVP_DecryptFinal_ex(ctx, ptext + plen, &len) == 0) {
|
|
|
|
qCInfo(lcCse()) << "Tag did not match!";
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
free(ptext);
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray result((char *)ptext, plen);
|
|
|
|
|
|
|
|
free(ptext);
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
|
|
|
|
return QByteArray();
|
2017-11-25 23:43:15 +03:00
|
|
|
}
|
|
|
|
|
2017-11-27 17:21:29 +03:00
|
|
|
QByteArray EncryptionHelper::encryptStringSymmetric(const QByteArray& key, const QByteArray& data) {
|
2017-11-25 23:43:15 +03:00
|
|
|
QByteArray iv = generateRandom(16);
|
|
|
|
|
|
|
|
EVP_CIPHER_CTX *ctx;
|
|
|
|
/* Create and initialise the context */
|
|
|
|
if(!(ctx = EVP_CIPHER_CTX_new())) {
|
|
|
|
qCInfo(lcCse()) << "Error creating cipher";
|
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialise the decryption operation. */
|
|
|
|
if(!EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL)) {
|
2017-11-28 14:36:35 +03:00
|
|
|
qCInfo(lcCse()) << "Error initializing context with aes_128";
|
2017-11-25 23:43:15 +03:00
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
// No padding
|
|
|
|
EVP_CIPHER_CTX_set_padding(ctx, 0);
|
|
|
|
|
|
|
|
/* Set IV length. */
|
|
|
|
if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), NULL)) {
|
|
|
|
qCInfo(lcCse()) << "Error setting iv length";
|
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialise key and IV */
|
|
|
|
if(!EVP_EncryptInit_ex(ctx, NULL, NULL, (unsigned char *)key.constData(), (unsigned char *)iv.constData())) {
|
|
|
|
qCInfo(lcCse()) << "Error initialising key and iv";
|
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
// We write the data base64 encoded
|
|
|
|
QByteArray dataB64 = data.toBase64();
|
|
|
|
|
|
|
|
// Make sure we have enough room in the cipher text
|
|
|
|
unsigned char *ctext = (unsigned char *)malloc(sizeof(unsigned char) * (dataB64.size() + 16));
|
|
|
|
|
|
|
|
// Do the actual encryption
|
|
|
|
int len = 0;
|
|
|
|
if(!EVP_EncryptUpdate(ctx, ctext, &len, (unsigned char *)dataB64.constData(), dataB64.size())) {
|
|
|
|
qCInfo(lcCse()) << "Error encrypting";
|
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
int clen = len;
|
|
|
|
|
|
|
|
/* Finalise the encryption. Normally ciphertext bytes may be written at
|
|
|
|
* this stage, but this does not occur in GCM mode
|
|
|
|
*/
|
|
|
|
if(1 != EVP_EncryptFinal_ex(ctx, ctext + len, &len)) {
|
|
|
|
qCInfo(lcCse()) << "Error finalizing encryption";
|
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
clen += len;
|
|
|
|
|
|
|
|
/* Get the tag */
|
|
|
|
unsigned char *tag = (unsigned char *)calloc(sizeof(unsigned char), 16);
|
|
|
|
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
|
|
|
|
qCInfo(lcCse()) << "Error getting the tag";
|
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray cipherTXT((char *)ctext, clen);
|
|
|
|
cipherTXT.append((char *)tag, 16);
|
|
|
|
|
|
|
|
QByteArray result = cipherTXT.toBase64();
|
|
|
|
result += "fA==";
|
|
|
|
result += iv.toBase64();
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-12-12 22:14:31 +03:00
|
|
|
QByteArray EncryptionHelper::encryptStringAsymmetric(EVP_PKEY *publicKey, const QByteArray& data) {
|
2017-11-25 23:43:15 +03:00
|
|
|
int err = -1;
|
|
|
|
|
2017-12-12 22:14:31 +03:00
|
|
|
auto ctx = EVP_PKEY_CTX_new(publicKey, ENGINE_get_default_RSA());
|
2017-11-25 23:43:15 +03:00
|
|
|
if (!ctx) {
|
|
|
|
qCInfo(lcCse()) << "Could not initialize the pkey context.";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (EVP_PKEY_encrypt_init(ctx) != 1) {
|
|
|
|
qCInfo(lcCse()) << "Error initilaizing the encryption.";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Error setting the encryption padding.";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, EVP_sha256()) <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Error setting OAEP SHA 256";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, EVP_sha256()) <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Error setting MGF1 padding";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t outLen = 0;
|
|
|
|
if (EVP_PKEY_encrypt(ctx, NULL, &outLen, (unsigned char *)data.constData(), data.size()) != 1) {
|
|
|
|
qCInfo(lcCse()) << "Error retrieving the size of the encrypted data";
|
|
|
|
exit(1);
|
|
|
|
} else {
|
|
|
|
qCInfo(lcCse()) << "Encrption Length:" << outLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char *out = (uchar*) OPENSSL_malloc(outLen);
|
|
|
|
if (!out) {
|
|
|
|
qCInfo(lcCse()) << "Error requesting memory for the encrypted contents";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (EVP_PKEY_encrypt(ctx, out, &outLen, (unsigned char *)data.constData(), data.size()) != 1) {
|
|
|
|
qCInfo(lcCse()) << "Could not encrypt key." << err;
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transform the encrypted data into base64.
|
|
|
|
QByteArray raw((const char*) out, outLen);
|
|
|
|
qCInfo(lcCse()) << raw.toBase64();
|
|
|
|
return raw.toBase64();
|
|
|
|
}
|
|
|
|
|
2017-11-27 18:11:21 +03:00
|
|
|
QByteArray EncryptionHelper::BIO2ByteArray(BIO *b) {
|
|
|
|
int pending = BIO_ctrl_pending(b);
|
|
|
|
char *tmp = (char *)calloc(pending+1, sizeof(char));
|
|
|
|
BIO_read(b, tmp, pending);
|
|
|
|
|
|
|
|
QByteArray res(tmp, pending);
|
|
|
|
free(tmp);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2017-09-12 12:21:53 +03:00
|
|
|
ClientSideEncryption::ClientSideEncryption()
|
2017-09-11 17:52:57 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-09-12 12:21:53 +03:00
|
|
|
void ClientSideEncryption::setAccount(AccountPtr account)
|
2017-09-11 17:52:57 +03:00
|
|
|
{
|
2017-09-12 12:21:53 +03:00
|
|
|
_account = account;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientSideEncryption::initialize()
|
|
|
|
{
|
|
|
|
qCInfo(lcCse()) << "Initializing";
|
2017-09-11 17:52:57 +03:00
|
|
|
if (!_account->capabilities().clientSideEncryptionAvaliable()) {
|
2017-09-12 12:21:53 +03:00
|
|
|
qCInfo(lcCse()) << "No Client side encryption avaliable on server.";
|
2017-09-11 17:52:57 +03:00
|
|
|
emit initializationFinished();
|
|
|
|
}
|
|
|
|
|
2017-11-27 18:11:21 +03:00
|
|
|
fetchFromKeyChain();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientSideEncryption::fetchFromKeyChain() {
|
|
|
|
const QString kck = AbstractCredentials::keychainKey(
|
|
|
|
_account->url().toString(),
|
2017-11-28 14:36:35 +03:00
|
|
|
_account->credentials()->user() + "_e2e-certificate",
|
2017-11-27 18:11:21 +03:00
|
|
|
_account->id()
|
|
|
|
);
|
|
|
|
|
|
|
|
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
|
|
|
job->setInsecureFallback(false);
|
|
|
|
job->setKey(kck);
|
|
|
|
connect(job, &ReadPasswordJob::finished, this, &ClientSideEncryption::publicKeyFetched);
|
|
|
|
job->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientSideEncryption::publicKeyFetched(Job *incoming) {
|
|
|
|
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incoming);
|
|
|
|
|
|
|
|
// Error or no valid public key error out
|
|
|
|
if (readJob->error() != NoError || readJob->binaryData().length() == 0) {
|
|
|
|
getPublicKeyFromServer();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_certificate = QSslCertificate(readJob->binaryData(), QSsl::Pem);
|
|
|
|
|
|
|
|
if (_certificate.isNull()) {
|
|
|
|
getPublicKeyFromServer();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-28 14:36:35 +03:00
|
|
|
_publicKey = _certificate.publicKey();
|
|
|
|
|
2017-11-27 18:11:21 +03:00
|
|
|
qCInfo(lcCse()) << "Public key fetched from keychain";
|
|
|
|
|
|
|
|
const QString kck = AbstractCredentials::keychainKey(
|
|
|
|
_account->url().toString(),
|
|
|
|
_account->credentials()->user() + "_e2e-private",
|
|
|
|
_account->id()
|
|
|
|
);
|
|
|
|
|
|
|
|
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
|
|
|
job->setInsecureFallback(false);
|
|
|
|
job->setKey(kck);
|
|
|
|
connect(job, &ReadPasswordJob::finished, this, &ClientSideEncryption::privateKeyFetched);
|
|
|
|
job->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientSideEncryption::privateKeyFetched(Job *incoming) {
|
|
|
|
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incoming);
|
|
|
|
|
|
|
|
// Error or no valid public key error out
|
|
|
|
if (readJob->error() != NoError || readJob->binaryData().length() == 0) {
|
|
|
|
_certificate = QSslCertificate();
|
2017-11-28 14:36:35 +03:00
|
|
|
_publicKey = QSslKey();
|
2017-11-27 18:11:21 +03:00
|
|
|
getPublicKeyFromServer();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_privateKey = QSslKey(readJob->binaryData(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
|
|
|
|
|
|
|
|
if (_privateKey.isNull()) {
|
|
|
|
getPrivateKeyFromServer();
|
|
|
|
return;
|
2017-09-12 12:21:53 +03:00
|
|
|
}
|
|
|
|
|
2017-11-27 18:11:21 +03:00
|
|
|
qCInfo(lcCse()) << "Private key fetched from keychain";
|
|
|
|
|
2017-11-28 14:36:35 +03:00
|
|
|
const QString kck = AbstractCredentials::keychainKey(
|
|
|
|
_account->url().toString(),
|
|
|
|
_account->credentials()->user() + "_e2e-mnemonic",
|
|
|
|
_account->id()
|
|
|
|
);
|
|
|
|
|
|
|
|
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
|
|
|
job->setInsecureFallback(false);
|
|
|
|
job->setKey(kck);
|
|
|
|
connect(job, &ReadPasswordJob::finished, this, &ClientSideEncryption::mnemonicKeyFetched);
|
|
|
|
job->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientSideEncryption::mnemonicKeyFetched(QKeychain::Job *incoming) {
|
|
|
|
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incoming);
|
|
|
|
|
|
|
|
// Error or no valid public key error out
|
|
|
|
if (readJob->error() != NoError || readJob->textData().length() == 0) {
|
|
|
|
_certificate = QSslCertificate();
|
|
|
|
_publicKey = QSslKey();
|
|
|
|
_privateKey = QSslKey();
|
|
|
|
getPublicKeyFromServer();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_mnemonic = readJob->textData();
|
|
|
|
|
|
|
|
qCInfo(lcCse()) << "Mnemonic key fetched from keychain";
|
|
|
|
|
2017-11-27 18:11:21 +03:00
|
|
|
emit initializationFinished();
|
2017-09-12 12:21:53 +03:00
|
|
|
}
|
|
|
|
|
2017-11-28 14:36:35 +03:00
|
|
|
void ClientSideEncryption::writePrivateKey() {
|
|
|
|
const QString kck = AbstractCredentials::keychainKey(
|
|
|
|
_account->url().toString(),
|
|
|
|
_account->credentials()->user() + "_e2e-private",
|
|
|
|
_account->id()
|
|
|
|
);
|
|
|
|
|
|
|
|
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
|
|
|
job->setInsecureFallback(false);
|
|
|
|
job->setKey(kck);
|
|
|
|
job->setBinaryData(_privateKey.toPem());
|
|
|
|
connect(job, &WritePasswordJob::finished, [this](Job *incoming) {
|
|
|
|
Q_UNUSED(incoming);
|
|
|
|
qCInfo(lcCse()) << "Private key stored in keychain";
|
|
|
|
});
|
|
|
|
job->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientSideEncryption::writeCertificate() {
|
|
|
|
const QString kck = AbstractCredentials::keychainKey(
|
|
|
|
_account->url().toString(),
|
|
|
|
_account->credentials()->user() + "_e2e-certificate",
|
|
|
|
_account->id()
|
|
|
|
);
|
|
|
|
|
|
|
|
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
|
|
|
job->setInsecureFallback(false);
|
|
|
|
job->setKey(kck);
|
|
|
|
job->setBinaryData(_certificate.toPem());
|
|
|
|
connect(job, &WritePasswordJob::finished, [this](Job *incoming) {
|
|
|
|
Q_UNUSED(incoming);
|
|
|
|
qCInfo(lcCse()) << "Certificate stored in keychain";
|
|
|
|
});
|
|
|
|
job->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientSideEncryption::writeMnemonic() {
|
|
|
|
const QString kck = AbstractCredentials::keychainKey(
|
|
|
|
_account->url().toString(),
|
|
|
|
_account->credentials()->user() + "_e2e-mnemonic",
|
|
|
|
_account->id()
|
|
|
|
);
|
|
|
|
|
|
|
|
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
|
|
|
job->setInsecureFallback(false);
|
|
|
|
job->setKey(kck);
|
|
|
|
job->setTextData(_mnemonic);
|
|
|
|
connect(job, &WritePasswordJob::finished, [this](Job *incoming) {
|
|
|
|
Q_UNUSED(incoming);
|
|
|
|
qCInfo(lcCse()) << "Mnemonic stored in keychain";
|
|
|
|
});
|
|
|
|
job->start();
|
|
|
|
}
|
|
|
|
|
2017-11-27 18:11:21 +03:00
|
|
|
|
2017-10-30 22:02:55 +03:00
|
|
|
QString publicKeyPath(AccountPtr account)
|
2017-09-12 12:21:53 +03:00
|
|
|
{
|
2017-10-30 22:02:55 +03:00
|
|
|
return baseDirectory() + account->displayName() + ".pub";
|
2017-09-11 17:52:57 +03:00
|
|
|
}
|
|
|
|
|
2017-10-30 22:02:55 +03:00
|
|
|
QString privateKeyPath(AccountPtr account)
|
2017-09-11 17:52:57 +03:00
|
|
|
{
|
2017-10-30 22:02:55 +03:00
|
|
|
return baseDirectory() + account->displayName() + ".rsa";
|
2017-09-12 12:21:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ClientSideEncryption::hasPrivateKey() const
|
|
|
|
{
|
2017-10-30 22:02:55 +03:00
|
|
|
return QFileInfo(privateKeyPath(_account)).exists();
|
2017-09-12 12:21:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ClientSideEncryption::hasPublicKey() const
|
|
|
|
{
|
2017-10-30 22:02:55 +03:00
|
|
|
return QFileInfo(publicKeyPath(_account)).exists();
|
2017-09-12 12:21:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void ClientSideEncryption::generateKeyPair()
|
|
|
|
{
|
2017-09-12 14:44:42 +03:00
|
|
|
// AES/GCM/NoPadding,
|
|
|
|
// metadataKeys with RSA/ECB/OAEPWithSHA-256AndMGF1Padding
|
|
|
|
qCInfo(lcCse()) << "No public key, generating a pair.";
|
|
|
|
const int rsaKeyLen = 2048;
|
|
|
|
|
|
|
|
EVP_PKEY *localKeyPair = nullptr;
|
|
|
|
|
|
|
|
// Init RSA
|
|
|
|
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
|
|
|
|
|
|
|
|
if(EVP_PKEY_keygen_init(ctx) <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Couldn't initialize the key generator";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, rsaKeyLen) <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Couldn't initialize the key generator bits";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(EVP_PKEY_keygen(ctx, &localKeyPair) <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Could not generate the key";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
EVP_PKEY_CTX_free(ctx);
|
|
|
|
qCInfo(lcCse()) << "Key correctly generated";
|
2017-09-12 15:35:05 +03:00
|
|
|
qCInfo(lcCse()) << "Storing keys locally";
|
|
|
|
|
2017-11-27 18:11:21 +03:00
|
|
|
BIO *privKey = BIO_new(BIO_s_mem());
|
|
|
|
if (PEM_write_bio_PrivateKey(privKey, localKeyPair, NULL, NULL, 0, NULL, NULL) <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Could not read private key from bio.";
|
2017-09-12 15:35:05 +03:00
|
|
|
return;
|
|
|
|
}
|
2017-11-27 18:11:21 +03:00
|
|
|
QByteArray key = EncryptionHelper::BIO2ByteArray(privKey);
|
|
|
|
_privateKey = QSslKey(key, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
|
2017-09-12 12:21:53 +03:00
|
|
|
|
2017-09-12 14:44:42 +03:00
|
|
|
qCInfo(lcCse()) << "Keys generated correctly, sending to server.";
|
2017-11-28 14:36:35 +03:00
|
|
|
generateCSR(localKeyPair);
|
2017-09-12 12:21:53 +03:00
|
|
|
}
|
|
|
|
|
2017-11-12 15:03:52 +03:00
|
|
|
void ClientSideEncryption::generateCSR(EVP_PKEY *keyPair)
|
2017-09-12 12:21:53 +03:00
|
|
|
{
|
2017-09-12 21:02:17 +03:00
|
|
|
// OpenSSL expects const char.
|
2017-11-06 22:57:50 +03:00
|
|
|
auto cnArray = _account->davUser().toLocal8Bit();
|
|
|
|
qCInfo(lcCse()) << "Getting the following array for the account Id" << cnArray;
|
|
|
|
|
2017-09-12 21:02:17 +03:00
|
|
|
auto certParams = std::map<const char *, const char*>{
|
|
|
|
{"C", "DE"},
|
|
|
|
{"ST", "Baden-Wuerttemberg"},
|
|
|
|
{"L", "Stuttgart"},
|
|
|
|
{"O","Nextcloud"},
|
2017-11-06 22:57:50 +03:00
|
|
|
{"CN", cnArray.constData()}
|
2017-09-12 21:02:17 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
int ret = 0;
|
|
|
|
int nVersion = 1;
|
|
|
|
|
2017-09-13 20:57:39 +03:00
|
|
|
X509_REQ *x509_req = nullptr;
|
|
|
|
SignPublicKeyApiJob *job = nullptr;
|
2017-09-12 21:02:17 +03:00
|
|
|
|
|
|
|
// 2. set version of x509 req
|
|
|
|
x509_req = X509_REQ_new();
|
|
|
|
ret = X509_REQ_set_version(x509_req, nVersion);
|
|
|
|
|
|
|
|
// 3. set subject of x509 req
|
|
|
|
auto x509_name = X509_REQ_get_subject_name(x509_req);
|
|
|
|
|
|
|
|
using ucharp = const unsigned char *;
|
|
|
|
for(const auto& v : certParams) {
|
|
|
|
ret = X509_NAME_add_entry_by_txt(x509_name, v.first, MBSTRING_ASC, (ucharp) v.second, -1, -1, 0);
|
|
|
|
if (ret != 1) {
|
|
|
|
qCInfo(lcCse()) << "Error Generating the Certificate while adding" << v.first << v.second;
|
2017-11-27 18:11:21 +03:00
|
|
|
X509_REQ_free(x509_req);
|
|
|
|
return;
|
2017-09-12 21:02:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = X509_REQ_set_pubkey(x509_req, keyPair);
|
|
|
|
if (ret != 1){
|
|
|
|
qCInfo(lcCse()) << "Error setting the public key on the csr";
|
2017-11-27 18:11:21 +03:00
|
|
|
X509_REQ_free(x509_req);
|
|
|
|
return;
|
2017-09-12 21:02:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = X509_REQ_sign(x509_req, keyPair, EVP_sha1()); // return x509_req->signature->length
|
|
|
|
if (ret <= 0){
|
|
|
|
qCInfo(lcCse()) << "Error setting the public key on the csr";
|
2017-11-27 18:11:21 +03:00
|
|
|
X509_REQ_free(x509_req);
|
|
|
|
return;
|
2017-09-12 21:02:17 +03:00
|
|
|
}
|
|
|
|
|
2017-11-27 18:11:21 +03:00
|
|
|
BIO *out = BIO_new(BIO_s_mem());
|
2017-09-12 21:02:17 +03:00
|
|
|
ret = PEM_write_bio_X509_REQ(out, x509_req);
|
2017-11-27 18:11:21 +03:00
|
|
|
QByteArray output = EncryptionHelper::BIO2ByteArray(out);
|
|
|
|
BIO_free(out);
|
|
|
|
EVP_PKEY_free(keyPair);
|
2017-09-12 21:02:17 +03:00
|
|
|
|
2017-09-12 22:52:10 +03:00
|
|
|
qCInfo(lcCse()) << "Returning the certificate";
|
|
|
|
qCInfo(lcCse()) << output;
|
|
|
|
|
2017-10-18 21:21:35 +03:00
|
|
|
job = new SignPublicKeyApiJob(_account, baseUrl() + "public-key", this);
|
2017-09-13 20:57:39 +03:00
|
|
|
job->setCsr(output);
|
2017-09-14 19:39:18 +03:00
|
|
|
|
2017-11-27 18:11:21 +03:00
|
|
|
connect(job, &SignPublicKeyApiJob::jsonReceived, [this](const QJsonDocument& json, int retCode) {
|
2017-09-14 16:57:41 +03:00
|
|
|
if (retCode == 200) {
|
2017-11-27 18:11:21 +03:00
|
|
|
QString cert = json.object().value("ocs").toObject().value("data").toObject().value("public-key").toString();
|
|
|
|
_certificate = QSslCertificate(cert.toLocal8Bit(), QSsl::Pem);
|
|
|
|
qCInfo(lcCse()) << "Certificate saved, Encrypting Private Key.";
|
|
|
|
encryptPrivateKey();
|
2017-09-14 16:57:41 +03:00
|
|
|
}
|
2017-09-13 20:57:39 +03:00
|
|
|
qCInfo(lcCse()) << retCode;
|
|
|
|
});
|
|
|
|
job->start();
|
2017-09-12 12:21:53 +03:00
|
|
|
}
|
|
|
|
|
2017-11-13 19:04:02 +03:00
|
|
|
void ClientSideEncryption::setTokenForFolder(const QByteArray& folderId, const QByteArray& token)
|
|
|
|
{
|
|
|
|
_folder2token[folderId] = token;
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray ClientSideEncryption::tokenForFolder(const QByteArray& folderId) const
|
|
|
|
{
|
|
|
|
Q_ASSERT(_folder2token.contains(folderId));
|
|
|
|
return _folder2token[folderId];
|
|
|
|
}
|
|
|
|
|
2017-11-27 18:11:21 +03:00
|
|
|
void ClientSideEncryption::encryptPrivateKey()
|
2017-09-14 19:39:18 +03:00
|
|
|
{
|
2017-11-27 18:11:21 +03:00
|
|
|
QStringList list = WordList::getRandomWords(12);
|
|
|
|
_mnemonic = list.join(' ');
|
2017-11-28 14:36:35 +03:00
|
|
|
qCInfo(lcCse()) << "mnemonic Generated:" << _mnemonic;
|
2017-09-14 19:39:18 +03:00
|
|
|
|
2017-11-27 18:11:21 +03:00
|
|
|
QString passPhrase = list.join(QString());
|
2017-11-24 22:58:20 +03:00
|
|
|
qCInfo(lcCse()) << "Passphrase Generated:" << passPhrase;
|
2017-09-14 19:39:18 +03:00
|
|
|
|
2017-11-27 18:11:21 +03:00
|
|
|
/*TODO: C++17: auto [secretKey, salt]. */
|
2017-11-27 17:21:29 +03:00
|
|
|
auto secretKey = EncryptionHelper::generatePassword(passPhrase);
|
2017-11-27 18:11:21 +03:00
|
|
|
auto cryptedText = EncryptionHelper::encryptPrivateKey(secretKey.first, _privateKey.toPem());
|
2017-10-24 16:53:17 +03:00
|
|
|
|
2017-11-25 00:10:28 +03:00
|
|
|
// Send private key to the server
|
2017-10-18 21:21:35 +03:00
|
|
|
auto job = new StorePrivateKeyApiJob(_account, baseUrl() + "private-key", this);
|
2017-11-27 18:11:21 +03:00
|
|
|
job->setPrivateKey(cryptedText);
|
2017-10-16 22:06:58 +03:00
|
|
|
connect(job, &StorePrivateKeyApiJob::jsonReceived, [this](const QJsonDocument& doc, int retCode) {
|
2017-11-12 14:55:12 +03:00
|
|
|
Q_UNUSED(doc);
|
2017-10-18 20:22:59 +03:00
|
|
|
switch(retCode) {
|
|
|
|
case 200:
|
2017-11-27 18:11:21 +03:00
|
|
|
qCInfo(lcCse()) << "Private key stored encrypted on server.";
|
2017-11-28 14:36:35 +03:00
|
|
|
writePrivateKey();
|
|
|
|
writeCertificate();
|
|
|
|
writeMnemonic();
|
2017-10-18 20:22:59 +03:00
|
|
|
emit initializationFinished();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
qCInfo(lcCse()) << "Store private key failed, return code:" << retCode;
|
|
|
|
}
|
2017-10-16 22:06:58 +03:00
|
|
|
});
|
|
|
|
job->start();
|
2017-09-14 19:39:18 +03:00
|
|
|
}
|
|
|
|
|
2017-11-28 14:36:35 +03:00
|
|
|
void ClientSideEncryption::decryptPrivateKey(const QByteArray &key) {
|
|
|
|
QString msg = tr("Please enter your end to end encryption passphrase:<br>"
|
|
|
|
"<br>"
|
|
|
|
"User: %2<br>"
|
|
|
|
"Account: %3<br>")
|
|
|
|
.arg(Utility::escape(_account->credentials()->user()),
|
|
|
|
Utility::escape(_account->displayName()));
|
|
|
|
|
|
|
|
QInputDialog dialog;
|
|
|
|
dialog.setWindowTitle(tr("Enter E2E passphrase"));
|
|
|
|
dialog.setLabelText(msg);
|
|
|
|
dialog.setTextEchoMode(QLineEdit::Normal);
|
|
|
|
|
|
|
|
QString prev;
|
|
|
|
|
|
|
|
while(true) {
|
|
|
|
if (!prev.isEmpty()) {
|
|
|
|
dialog.setTextValue(prev);
|
|
|
|
}
|
|
|
|
bool ok = dialog.exec();
|
|
|
|
if (ok) {
|
|
|
|
qCInfo(lcCse()) << "Got mnemonic:" << dialog.textValue();
|
|
|
|
prev = dialog.textValue();
|
|
|
|
|
|
|
|
_mnemonic = prev;
|
|
|
|
QString mnemonic = prev.split(" ").join(QString());
|
|
|
|
qCInfo(lcCse()) << "mnemonic:" << mnemonic;
|
|
|
|
auto pass = EncryptionHelper::generatePassword(mnemonic);
|
|
|
|
qCInfo(lcCse()) << "Generated key:" << pass.first;
|
|
|
|
|
|
|
|
QByteArray privateKey = EncryptionHelper::decryptPrivateKey(pass.first, key);
|
|
|
|
_privateKey = QSslKey(privateKey, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
|
|
|
|
|
|
|
|
qCInfo(lcCse()) << "Private key: " << _privateKey.toPem();
|
|
|
|
|
|
|
|
if (!_privateKey.isNull()) {
|
|
|
|
writePrivateKey();
|
|
|
|
writeCertificate();
|
|
|
|
writeMnemonic();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_mnemonic = QString();
|
|
|
|
_privateKey = QSslKey();
|
|
|
|
qCInfo(lcCse()) << "Cancelled";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
emit initializationFinished();
|
|
|
|
}
|
|
|
|
|
2017-09-12 12:21:53 +03:00
|
|
|
void ClientSideEncryption::getPrivateKeyFromServer()
|
|
|
|
{
|
2017-11-28 14:36:35 +03:00
|
|
|
qCInfo(lcCse()) << "Retrieving private key from server";
|
|
|
|
auto job = new JsonApiJob(_account, baseUrl() + "private-key", this);
|
|
|
|
connect(job, &JsonApiJob::jsonReceived, [this](const QJsonDocument& doc, int retCode) {
|
|
|
|
if (retCode == 200) {
|
|
|
|
QString key = doc.object()["ocs"].toObject()["data"].toObject()["private-key"].toString();
|
|
|
|
qCInfo(lcCse()) << key;
|
|
|
|
qCInfo(lcCse()) << "Found private key, lets decrypt it!";
|
|
|
|
decryptPrivateKey(key.toLocal8Bit());
|
|
|
|
} else if (retCode == 404) {
|
|
|
|
qCInfo(lcCse()) << "No private key on the server: setup is incomplete.";
|
|
|
|
} else {
|
|
|
|
qCInfo(lcCse()) << "Error while requesting public key: " << retCode;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
job->start();
|
2017-09-12 12:21:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void ClientSideEncryption::getPublicKeyFromServer()
|
|
|
|
{
|
|
|
|
qCInfo(lcCse()) << "Retrieving public key from server";
|
2017-10-18 21:21:35 +03:00
|
|
|
auto job = new JsonApiJob(_account, baseUrl() + "public-key", this);
|
2017-09-12 12:21:53 +03:00
|
|
|
connect(job, &JsonApiJob::jsonReceived, [this](const QJsonDocument& doc, int retCode) {
|
2017-11-28 14:36:35 +03:00
|
|
|
if (retCode == 200) {
|
|
|
|
QString publicKey = doc.object()["ocs"].toObject()["data"].toObject()["public-keys"].toObject()[_account->davUser()].toString();
|
|
|
|
_certificate = QSslCertificate(publicKey.toLocal8Bit(), QSsl::Pem);
|
|
|
|
_publicKey = _certificate.publicKey();
|
|
|
|
qCInfo(lcCse()) << publicKey;
|
|
|
|
qCInfo(lcCse()) << "Found Public key, requesting Private Key.";
|
|
|
|
getPrivateKeyFromServer();
|
|
|
|
} else if (retCode == 404) {
|
2017-09-12 14:44:42 +03:00
|
|
|
qCInfo(lcCse()) << "No public key on the server";
|
|
|
|
generateKeyPair();
|
2017-11-28 14:36:35 +03:00
|
|
|
} else {
|
|
|
|
qCInfo(lcCse()) << "Error while requesting public key: " << retCode;
|
|
|
|
}
|
2017-09-12 12:21:53 +03:00
|
|
|
});
|
|
|
|
job->start();
|
|
|
|
}
|
2017-10-23 19:15:39 +03:00
|
|
|
|
2017-11-20 23:38:17 +03:00
|
|
|
void ClientSideEncryption::fetchFolderEncryptedStatus() {
|
2017-11-23 18:55:12 +03:00
|
|
|
_refreshingEncryptionStatus = true;
|
2017-12-07 20:06:55 +03:00
|
|
|
auto getEncryptedStatus = new GetFolderEncryptStatusJob(_account, QString());
|
|
|
|
connect(getEncryptedStatus, &GetFolderEncryptStatusJob::encryptStatusReceived,
|
2017-11-23 18:55:12 +03:00
|
|
|
this, &ClientSideEncryption::folderEncryptedStatusFetched);
|
2017-12-07 20:06:55 +03:00
|
|
|
connect(getEncryptedStatus, &GetFolderEncryptStatusJob::encryptStatusError,
|
2017-11-23 18:55:12 +03:00
|
|
|
this, &ClientSideEncryption::folderEncryptedStatusError);
|
|
|
|
getEncryptedStatus->start();
|
2017-11-20 23:38:17 +03:00
|
|
|
}
|
|
|
|
|
2017-11-23 18:55:12 +03:00
|
|
|
void ClientSideEncryption::folderEncryptedStatusFetched(const QMap<QString, bool>& result)
|
2017-11-20 23:38:17 +03:00
|
|
|
{
|
2017-11-23 18:55:12 +03:00
|
|
|
_refreshingEncryptionStatus = false;
|
2017-11-27 23:06:38 +03:00
|
|
|
_folder2encryptedStatus = result;
|
2017-11-20 23:38:17 +03:00
|
|
|
qDebug() << "Retrieved correctly the encrypted status of the folders." << result;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:55:12 +03:00
|
|
|
void ClientSideEncryption::folderEncryptedStatusError(int error)
|
2017-11-20 23:38:17 +03:00
|
|
|
{
|
2017-11-23 18:55:12 +03:00
|
|
|
_refreshingEncryptionStatus = false;
|
|
|
|
qDebug() << "Failed to retrieve the status of the folders." << error;
|
2017-11-20 23:38:17 +03:00
|
|
|
}
|
2017-10-23 19:15:39 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
2017-10-30 17:39:40 +03:00
|
|
|
//TODO: Create an actuall encryption here.
|
|
|
|
auto metadataKeyEnc(const QByteArray& data) -> QByteArray
|
|
|
|
{
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto metadataKeyDec(const QByteArray& data) -> QByteArray
|
|
|
|
{
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2017-11-13 18:46:30 +03:00
|
|
|
FolderMetadata::FolderMetadata(AccountPtr account, const QByteArray& metadata) : _account(account), _metadata(metadata)
|
2017-10-23 20:27:34 +03:00
|
|
|
{
|
|
|
|
if (metadata.isEmpty()) {
|
2017-10-31 14:07:47 +03:00
|
|
|
qCInfo(lcCse()) << "Setupping Empty Metadata";
|
2017-10-30 17:39:40 +03:00
|
|
|
setupEmptyMetadata();
|
2017-10-31 14:07:47 +03:00
|
|
|
} else {
|
2017-12-12 17:35:53 +03:00
|
|
|
qCInfo(lcCse()) << "Setting up existing metadata";
|
|
|
|
setupExistingMetadata();
|
2017-10-23 20:27:34 +03:00
|
|
|
}
|
2017-10-30 17:39:40 +03:00
|
|
|
}
|
|
|
|
|
2017-12-12 17:35:53 +03:00
|
|
|
void FolderMetadata::setupExistingMetadata()
|
|
|
|
{
|
|
|
|
/* This is the json response from the server, it contains two extra objects that we are *not* interested.
|
|
|
|
* ocs and data.
|
|
|
|
*/
|
|
|
|
std::string byteArray(_metadata.constData(), _metadata.length());
|
|
|
|
nlohmann::json j = nlohmann::json::parse(byteArray);
|
|
|
|
|
|
|
|
// The metadata is being retrieved as a string stored in a json.
|
|
|
|
// This *seems* to be broken - the strung us nit base64 encoded,
|
|
|
|
// I'm currently unsure if this is error on my side or in the server implementation.
|
2017-12-12 18:09:31 +03:00
|
|
|
// And because inside of the meta-data there's an object called metadata, without '-'
|
|
|
|
// make it really different.
|
|
|
|
auto meta_Data = nlohmann::json::parse(j["ocs"]["data"]["meta-data"].get<std::string>());
|
2017-12-12 17:35:53 +03:00
|
|
|
|
|
|
|
qDebug() << "######################################333";
|
|
|
|
qDebug() << " EXisting Metadata";
|
|
|
|
qDebug() << _metadata;
|
2017-12-12 18:09:31 +03:00
|
|
|
qDebug() << meta_Data.dump(4);
|
|
|
|
for (nlohmann::json::iterator it = meta_Data.begin(); it != meta_Data.end(); ++it) {
|
2017-12-12 17:35:53 +03:00
|
|
|
std::cout << it.key() << " : " << it.value() << std::endl;
|
|
|
|
}
|
|
|
|
qDebug() << "##########################################";
|
|
|
|
|
2017-12-12 18:09:31 +03:00
|
|
|
// This is the encrypted metadata string.
|
|
|
|
std::string encrypted_metadata_keys = meta_Data["metadata"]["metadataKeys"];
|
|
|
|
std::string decrypted_metadata_keys = decryptMetadataKeys(encrypted_metadata_keys);
|
|
|
|
qDebug() << encrypted_metadata_keys;
|
|
|
|
qDebug() << decrypted_metadata_keys;
|
|
|
|
|
2017-12-12 17:35:53 +03:00
|
|
|
}
|
|
|
|
|
2017-10-30 17:39:40 +03:00
|
|
|
// RSA/ECB/OAEPWithSHA-256AndMGF1Padding using private / public key.
|
2017-11-25 23:43:15 +03:00
|
|
|
QByteArray FolderMetadata::encryptMetadataKeys(const nlohmann::json& metadataKeys) const {
|
2017-10-30 22:02:55 +03:00
|
|
|
|
2017-12-08 13:24:22 +03:00
|
|
|
BIO *publicKeyBio = BIO_new(BIO_s_mem());
|
|
|
|
QByteArray publicKeyPem = _account->e2e()->_publicKey.toPem();
|
|
|
|
BIO_write(publicKeyBio, publicKeyPem.constData(), publicKeyPem.size());
|
2017-10-30 22:02:55 +03:00
|
|
|
|
2017-12-12 22:14:31 +03:00
|
|
|
EVP_PKEY *publicKey = PEM_read_bio_PUBKEY(publicKeyBio, NULL, NULL, NULL);
|
2017-10-30 22:02:55 +03:00
|
|
|
|
2017-11-25 23:43:15 +03:00
|
|
|
auto data = QByteArray::fromStdString(metadataKeys.dump());
|
2017-12-12 22:14:31 +03:00
|
|
|
auto ret = EncryptionHelper::encryptStringAsymmetric(publicKey, data);
|
2017-10-30 21:05:55 +03:00
|
|
|
|
2017-12-12 22:14:31 +03:00
|
|
|
EVP_PKEY_free(publicKey);
|
2017-10-30 21:05:55 +03:00
|
|
|
|
2017-10-31 14:07:47 +03:00
|
|
|
return ret;
|
2017-10-30 17:39:40 +03:00
|
|
|
}
|
|
|
|
|
2017-10-31 15:06:20 +03:00
|
|
|
std::string FolderMetadata::decryptMetadataKeys(const std::string& encryptedMetadata) const
|
|
|
|
{
|
|
|
|
qCInfo(lcCse()) << "Starting to decrypt the metadata key";
|
2017-10-31 18:06:01 +03:00
|
|
|
unsigned char *out = nullptr;
|
|
|
|
size_t outlen = 0;
|
2017-10-31 15:06:20 +03:00
|
|
|
int err = -1;
|
|
|
|
|
2017-12-12 18:09:31 +03:00
|
|
|
BIO *privateKeyBio = BIO_new(BIO_s_mem());
|
2017-12-12 18:15:05 +03:00
|
|
|
QByteArray privateKeyPem = _account->e2e()->_privateKey.toPem();
|
|
|
|
BIO_write(privateKeyBio, privateKeyPem.constData(), privateKeyPem.size());
|
2017-12-12 18:09:31 +03:00
|
|
|
EVP_PKEY *key = PEM_read_bio_PrivateKey(privateKeyBio, NULL, NULL, NULL);
|
|
|
|
|
2017-10-31 18:06:01 +03:00
|
|
|
if (!key) {
|
|
|
|
qCInfo(lcCse()) << "Error reading private key";
|
|
|
|
}
|
2017-10-31 15:06:20 +03:00
|
|
|
|
2017-10-31 15:17:22 +03:00
|
|
|
// Data is base64 encoded.
|
2017-10-31 18:06:01 +03:00
|
|
|
qCInfo(lcCse()) << "encryptedMetadata" << encryptedMetadata;
|
2017-10-31 15:17:22 +03:00
|
|
|
auto raw = QByteArray(encryptedMetadata.c_str(), encryptedMetadata.length());
|
|
|
|
auto b64d = QByteArray::fromBase64(raw);
|
|
|
|
auto in = (unsigned char *) b64d.constData();
|
|
|
|
size_t inlen = b64d.length();
|
|
|
|
|
2017-10-31 18:06:01 +03:00
|
|
|
qCInfo(lcCse()) << "Encrypted metadata length: " << inlen;
|
|
|
|
|
2017-10-31 15:06:20 +03:00
|
|
|
/* NB: assumes key in, inlen are already set up
|
|
|
|
* and that key is an RSA private key
|
|
|
|
*/
|
|
|
|
auto ctx = EVP_PKEY_CTX_new(key, nullptr);
|
|
|
|
if (!ctx) {
|
|
|
|
qCInfo(lcCse()) << "Could not create the PKEY context.";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = EVP_PKEY_decrypt_init(ctx);
|
|
|
|
if (err <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Could not init the decryption of the metadata";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = EVP_PKEY_CTX_set_rsa_padding(ctx, 1); //TODO: Make this a class variable.
|
|
|
|
if (err <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Could not set the RSA padding";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = EVP_PKEY_decrypt(ctx, NULL, &outlen, in, inlen);
|
|
|
|
if (err <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Could not determine the buffer length";
|
|
|
|
exit(1);
|
2017-10-31 18:06:01 +03:00
|
|
|
} else {
|
|
|
|
qCInfo(lcCse()) << "Size of output is: " << outlen;
|
2017-10-31 15:06:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
out = (unsigned char *) OPENSSL_malloc(outlen);
|
|
|
|
if (!out) {
|
|
|
|
qCInfo(lcCse()) << "Could not alloc space for the decrypted metadata";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = EVP_PKEY_decrypt(ctx, out, &outlen, in, inlen);
|
|
|
|
if (err <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Could not decrypt the metadata";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
qCInfo(lcCse()) << "Metadata decrypted successfully";
|
2017-10-31 18:06:01 +03:00
|
|
|
const auto ret = std::string((char*) out, outlen);
|
|
|
|
|
|
|
|
return ret;
|
2017-10-31 15:06:20 +03:00
|
|
|
}
|
|
|
|
|
2017-10-30 17:39:40 +03:00
|
|
|
// AES/GCM/NoPadding (128 bit key size)
|
2017-11-25 23:43:15 +03:00
|
|
|
QByteArray FolderMetadata::encryptJsonObject(const nlohmann::json& obj, const QByteArray pass) const {
|
|
|
|
auto data = QByteArray::fromStdString(obj.dump());
|
|
|
|
return EncryptionHelper::encryptStringSymmetric(pass, data);
|
2017-11-01 16:42:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string FolderMetadata::decryptJsonObject(const std::string& encryptedMetadata, const std::string& pass) const
|
|
|
|
{
|
2017-11-01 17:24:34 +03:00
|
|
|
// Jesus, should I encrypt here in 128kb chunks?
|
|
|
|
// perhaps.
|
|
|
|
|
|
|
|
// Data is base64 encoded.
|
|
|
|
// TODO: Transform this bit into a function to remove duplicated code.
|
|
|
|
qCInfo(lcCse()) << "encryptedMetadata" << encryptedMetadata;
|
|
|
|
auto raw = QByteArray(encryptedMetadata.c_str(), encryptedMetadata.length());
|
|
|
|
auto b64d = QByteArray::fromBase64(raw);
|
|
|
|
auto in = (unsigned char *) b64d.constData();
|
2017-11-02 14:39:42 +03:00
|
|
|
// The tag is appended but it is not part of the cipher text
|
|
|
|
size_t inlen = b64d.length() - 16;
|
|
|
|
auto tag = in + inlen;
|
2017-11-01 17:24:34 +03:00
|
|
|
|
|
|
|
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
|
|
|
if (!ctx) {
|
|
|
|
qCInfo(lcCse()) << "Coult not create decryptioncontext, aborting.";
|
|
|
|
exit(1);
|
|
|
|
}
|
2017-11-03 19:58:27 +03:00
|
|
|
|
|
|
|
/* Initialise the decryption operation. */
|
|
|
|
if(!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL)) {
|
|
|
|
qCInfo(lcCse()) << "Error initialializing the decryption, aborting.";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2017-11-01 18:13:17 +03:00
|
|
|
EVP_CIPHER_CTX_set_padding(ctx, 0);
|
|
|
|
|
2017-11-01 17:24:34 +03:00
|
|
|
unsigned char *iv = (unsigned char *)"0123456789012345";
|
2017-11-03 19:58:27 +03:00
|
|
|
if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL)) {
|
|
|
|
qCInfo(lcCse()) << "Could not set IV length, aborting.";
|
2017-11-01 17:24:34 +03:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2017-11-03 19:58:27 +03:00
|
|
|
auto key = (const unsigned char*) pass.c_str();
|
|
|
|
int err = EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv);
|
2017-11-01 17:24:34 +03:00
|
|
|
if (err != 1) {
|
2017-11-03 19:58:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error setting the key and iv, aborting.";
|
2017-11-01 17:24:34 +03:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2017-11-03 19:58:27 +03:00
|
|
|
int outlen = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Max outlen is inlen + blocksize (16 bytes)
|
|
|
|
*/
|
|
|
|
auto out = (unsigned char *) OPENSSL_malloc(inlen + 16);
|
2017-11-01 17:24:34 +03:00
|
|
|
err = EVP_DecryptUpdate(ctx, out, &outlen, in, inlen);
|
|
|
|
if (err != 1) {
|
|
|
|
qCInfo(lcCse()) << "Error decrypting the json blob, aborting.";
|
|
|
|
exit(1);
|
|
|
|
}
|
2017-11-01 17:48:40 +03:00
|
|
|
qCInfo(lcCse()) << "currently decrypted" << std::string( (char*) out, outlen);
|
2017-11-01 18:13:17 +03:00
|
|
|
qCInfo(lcCse()) << "Current decrypt length" << outlen;
|
2017-11-01 17:24:34 +03:00
|
|
|
|
2017-11-02 14:39:42 +03:00
|
|
|
/* Set expected tag value. Works in OpenSSL 1.0.1d and later */
|
|
|
|
if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag)) {
|
2017-11-03 19:58:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error setting the tag, aborting.";
|
2017-11-02 14:39:42 +03:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2017-11-03 19:58:27 +03:00
|
|
|
qCInfo(lcCse()) << "Tag: " << tag;
|
|
|
|
|
|
|
|
int f_len = outlen;
|
|
|
|
err = EVP_DecryptFinal_ex(ctx, out + outlen, &f_len);
|
|
|
|
if (err != 1) {
|
|
|
|
qCInfo(lcCse()) << "Error finalyzing the decryption, aborting.";
|
|
|
|
exit(1);
|
|
|
|
}
|
2017-11-02 14:39:42 +03:00
|
|
|
|
2017-11-01 17:24:34 +03:00
|
|
|
qCInfo(lcCse()) << "Decryption finalized.";
|
2017-11-01 17:48:40 +03:00
|
|
|
const auto ret = std::string((char*) out, outlen);
|
|
|
|
return ret;
|
2017-10-30 17:39:40 +03:00
|
|
|
}
|
2017-10-23 20:27:34 +03:00
|
|
|
|
2017-10-30 17:39:40 +03:00
|
|
|
void FolderMetadata::setupEmptyMetadata() {
|
|
|
|
using namespace nlohmann;
|
2017-11-25 23:43:15 +03:00
|
|
|
QByteArray newMetadataPass = EncryptionHelper::generateRandom(16);
|
2017-10-31 18:06:01 +03:00
|
|
|
qCInfo(lcCse()) << "Key Generated for the Metadata" << newMetadataPass;
|
|
|
|
|
2017-11-25 23:43:15 +03:00
|
|
|
json metadataKeyObj = {"0", newMetadataPass.toStdString()};
|
2017-10-30 17:39:40 +03:00
|
|
|
json recepient = {"recipient", {}};
|
2017-10-31 18:06:01 +03:00
|
|
|
|
|
|
|
auto b64String = encryptMetadataKeys(metadataKeyObj);
|
2017-12-12 18:09:31 +03:00
|
|
|
qCInfo(lcCse()) << "ENCRYPTED METADATA KEY" << b64String;
|
|
|
|
auto db64String = decryptMetadataKeys(std::string(b64String.constData(), b64String.size()));
|
|
|
|
qCInfo(lcCse()) << "DECRYPTED METADATA KEY" << b64String;
|
2017-11-01 17:48:40 +03:00
|
|
|
auto sharingEncrypted = encryptJsonObject(recepient, newMetadataPass);
|
|
|
|
|
2017-10-30 17:39:40 +03:00
|
|
|
json m = {
|
|
|
|
{"metadata", {
|
2017-11-25 23:43:15 +03:00
|
|
|
{"metadataKeys", b64String.toStdString()},
|
|
|
|
{"sharing", sharingEncrypted.toStdString()},
|
2017-10-30 17:39:40 +03:00
|
|
|
{"version",1}
|
|
|
|
}},
|
2017-11-25 23:43:15 +03:00
|
|
|
{"files", {
|
2017-10-30 17:39:40 +03:00
|
|
|
}}
|
|
|
|
};
|
2017-10-30 21:05:55 +03:00
|
|
|
|
2017-11-25 23:43:15 +03:00
|
|
|
_metadata = QByteArray::fromStdString(m.dump());
|
2017-10-23 20:27:34 +03:00
|
|
|
}
|
|
|
|
|
2017-11-13 18:46:30 +03:00
|
|
|
QByteArray FolderMetadata::encryptedMetadata() {
|
|
|
|
return _metadata;
|
|
|
|
}
|
2017-11-01 19:36:54 +03:00
|
|
|
|
2017-11-01 19:54:17 +03:00
|
|
|
|
|
|
|
|
2017-11-20 23:38:17 +03:00
|
|
|
|
2017-11-27 23:19:54 +03:00
|
|
|
bool ClientSideEncryption::isFolderEncrypted(const QString& path) {
|
|
|
|
auto it = _folder2encryptedStatus.find(path);
|
|
|
|
if (it == _folder2encryptedStatus.end())
|
|
|
|
return false;
|
|
|
|
return (*it);
|
2017-11-27 23:06:38 +03:00
|
|
|
}
|
2017-11-27 23:19:54 +03:00
|
|
|
|
2017-09-11 16:00:01 +03:00
|
|
|
}
|