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-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-09-11 17:52:57 +03:00
|
|
|
|
2017-09-14 19:39:18 +03:00
|
|
|
#include "wordlist.h"
|
|
|
|
|
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-10-23 19:15:39 +03:00
|
|
|
Q_LOGGING_CATEGORY(lcSignPublicKeyApiJob, "sync.networkjob.sendcsr", QtInfoMsg);
|
|
|
|
Q_LOGGING_CATEGORY(lcStorePrivateKeyApiJob, "sync.networkjob.storeprivatekey", QtInfoMsg);
|
|
|
|
Q_LOGGING_CATEGORY(lcCseJob, "sync.networkjob.clientsideencrypt", 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-09-14 23:50:56 +03:00
|
|
|
int encrypt(unsigned char *plaintext,
|
|
|
|
int plaintext_len,
|
|
|
|
unsigned char *key,
|
|
|
|
unsigned char *iv,
|
2017-09-15 17:17:37 +03:00
|
|
|
unsigned char *ciphertext,
|
|
|
|
unsigned char *tag)
|
2017-09-14 23:45:12 +03:00
|
|
|
{
|
2017-09-15 17:17:37 +03:00
|
|
|
EVP_CIPHER_CTX *ctx;
|
|
|
|
int len;
|
|
|
|
int ciphertext_len;
|
2017-09-14 23:45:12 +03:00
|
|
|
|
|
|
|
/* Create and initialise the context */
|
|
|
|
if(!(ctx = EVP_CIPHER_CTX_new())) {
|
2017-10-16 21:24:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error creating the Cipher.";
|
2017-09-14 23:45:12 +03:00
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
2017-09-15 17:17:37 +03:00
|
|
|
/* Initialise the encryption operation. */
|
2017-10-24 16:49:45 +03:00
|
|
|
if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL)) {
|
2017-10-16 21:24:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error initializing the context with aes_256";
|
2017-09-14 23:45:12 +03:00
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
2017-10-24 16:44:34 +03:00
|
|
|
// We don't do padding
|
|
|
|
EVP_CIPHER_CTX_set_padding(ctx, 0);
|
|
|
|
|
|
|
|
/* Set IV length to 16 bytes */
|
2017-09-15 17:17:37 +03:00
|
|
|
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL)) {
|
2017-10-24 16:44:34 +03:00
|
|
|
qCInfo(lcCse()) << "Error setting the iv length to 16 bytes. ";
|
2017-09-15 17:17:37 +03:00
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialise key and IV */
|
|
|
|
if(1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) {
|
2017-10-16 21:24:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error initializing encryption";
|
2017-09-15 17:17:37 +03:00
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Provide the message to be encrypted, and obtain the encrypted output.
|
|
|
|
* EVP_EncryptUpdate can be called multiple times if necessary
|
|
|
|
*/
|
2017-09-14 23:45:12 +03:00
|
|
|
if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) {
|
2017-10-16 21:24:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error encrypting the cipher ext"; // Current error is here.
|
2017-09-14 23:45:12 +03:00
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
ciphertext_len = len;
|
|
|
|
|
2017-09-15 17:17:37 +03:00
|
|
|
/* Finalise the encryption. Normally ciphertext bytes may be written at
|
|
|
|
* this stage, but this does not occur in GCM mode
|
|
|
|
*/
|
2017-09-14 23:45:12 +03:00
|
|
|
if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
|
2017-10-16 21:24:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error finalizing the encryption";
|
2017-09-14 23:45:12 +03:00
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
ciphertext_len += len;
|
|
|
|
|
2017-09-15 17:17:37 +03:00
|
|
|
/* Get the tag */
|
|
|
|
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
|
2017-10-16 21:24:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error Retrieving the tag";
|
2017-09-15 17:17:37 +03:00
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
2017-10-24 16:44:34 +03:00
|
|
|
/* Add tag to cypher text to be compatible with the Android implementation */
|
|
|
|
memcpy(ciphertext + ciphertext_len, tag, 16);
|
|
|
|
ciphertext_len += 16;
|
|
|
|
|
2017-09-14 23:45:12 +03:00
|
|
|
/* Clean up */
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
|
|
|
|
return ciphertext_len;
|
|
|
|
}
|
2017-09-14 23:50:56 +03:00
|
|
|
|
|
|
|
int decrypt(unsigned char *ciphertext,
|
|
|
|
int ciphertext_len,
|
2017-09-15 17:17:37 +03:00
|
|
|
unsigned char *tag,
|
2017-09-14 23:50:56 +03:00
|
|
|
unsigned char *key,
|
|
|
|
unsigned char *iv,
|
|
|
|
unsigned char *plaintext)
|
|
|
|
{
|
2017-09-15 17:17:37 +03:00
|
|
|
EVP_CIPHER_CTX *ctx;
|
|
|
|
int len;
|
|
|
|
int plaintext_len;
|
|
|
|
int ret;
|
2017-09-14 23:50:56 +03:00
|
|
|
|
|
|
|
/* Create and initialise the context */
|
|
|
|
if(!(ctx = EVP_CIPHER_CTX_new())) {
|
2017-10-16 21:24:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error Initializing the decrypt context";
|
2017-09-14 23:50:56 +03:00
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
2017-09-15 17:17:37 +03:00
|
|
|
/* Initialise the decryption operation. */
|
2017-10-24 16:49:45 +03:00
|
|
|
if(!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL)) {
|
2017-10-16 21:24:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error initializing the decryption context";
|
2017-09-15 17:17:37 +03:00
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
2017-10-24 16:48:44 +03:00
|
|
|
/* Set IV length to 16 bytes */
|
2017-09-15 17:17:37 +03:00
|
|
|
if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL)) {
|
2017-10-16 21:24:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error seting th iv length for the decrypt context";
|
2017-09-15 17:17:37 +03:00
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialise key and IV */
|
|
|
|
if(!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) {
|
2017-10-16 21:24:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error setting the key and iv for decryption";
|
2017-09-14 23:50:56 +03:00
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
|
2017-10-24 16:48:44 +03:00
|
|
|
/* Provide the message to be decrypted, and obtain the plaintext output.
|
2017-09-15 17:17:37 +03:00
|
|
|
* EVP_DecryptUpdate can be called multiple times if necessary
|
2017-10-24 16:48:44 +03:00
|
|
|
*
|
|
|
|
* Do not try to decrypt the last 16 bytes. The tag is appended by Android.
|
|
|
|
* So we ignore the last 16 bytes.
|
2017-09-15 17:17:37 +03:00
|
|
|
*/
|
2017-10-24 16:48:44 +03:00
|
|
|
if(!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len - 16)) {
|
2017-10-16 21:24:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error decrypting the text";
|
2017-09-14 23:50:56 +03:00
|
|
|
handleErrors();
|
|
|
|
}
|
|
|
|
plaintext_len = len;
|
|
|
|
|
2017-09-15 17:17:37 +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-10-16 21:24:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error setting the tag on the decrupt context";
|
2017-09-14 23:50:56 +03:00
|
|
|
handleErrors();
|
|
|
|
}
|
2017-09-15 17:17:37 +03:00
|
|
|
|
|
|
|
/* Finalise the decryption. A positive return value indicates success,
|
|
|
|
* anything else is a failure - the plaintext is not trustworthy.
|
|
|
|
*/
|
|
|
|
ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
|
2017-09-14 23:50:56 +03:00
|
|
|
|
|
|
|
/* Clean up */
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
|
2017-09-15 17:17:37 +03:00
|
|
|
if(ret > 0)
|
|
|
|
{
|
|
|
|
/* Success */
|
|
|
|
plaintext_len += len;
|
|
|
|
return plaintext_len;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-10-16 21:24:27 +03:00
|
|
|
qCInfo(lcCse()) << "Error finalizing the decrypt";
|
2017-09-15 17:17:37 +03:00
|
|
|
/* Verify failed */
|
|
|
|
return -1;
|
|
|
|
}
|
2017-09-14 23:50:56 +03:00
|
|
|
}
|
2017-09-14 23:45:12 +03:00
|
|
|
}
|
|
|
|
|
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-09-12 12:21:53 +03:00
|
|
|
if (hasPrivateKey() && hasPublicKey()) {
|
|
|
|
qCInfo(lcCse()) << "Public and private keys already downloaded";
|
|
|
|
emit initializationFinished();
|
|
|
|
}
|
|
|
|
|
|
|
|
getPublicKeyFromServer();
|
|
|
|
}
|
|
|
|
|
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";
|
|
|
|
|
|
|
|
QDir dir;
|
2017-10-18 21:21:35 +03:00
|
|
|
if (!dir.mkpath(baseDirectory())) {
|
2017-09-12 15:35:05 +03:00
|
|
|
qCInfo(lcCse()) << "Could not create the folder for the keys.";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-10-30 22:02:55 +03:00
|
|
|
auto privKeyPath = privateKeyPath(_account).toLocal8Bit();
|
|
|
|
auto pubKeyPath = publicKeyPath(_account).toLocal8Bit();
|
2017-09-12 15:35:05 +03:00
|
|
|
FILE *privKeyFile = fopen(privKeyPath.constData(), "w");
|
|
|
|
FILE *pubKeyFile = fopen(pubKeyPath.constData(), "w");
|
|
|
|
|
|
|
|
qCInfo(lcCse()) << "Private key filename" << privKeyPath;
|
|
|
|
qCInfo(lcCse()) << "Public key filename" << pubKeyPath;
|
|
|
|
|
|
|
|
//TODO: Verify if the key needs to be stored with a Cipher and Pass.
|
|
|
|
if (!PEM_write_PrivateKey(privKeyFile, localKeyPair, NULL, NULL, 0, 0, NULL)) {
|
|
|
|
qCInfo(lcCse()) << "Could not write the private key to a file.";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!PEM_write_PUBKEY(pubKeyFile, localKeyPair)) {
|
|
|
|
qCInfo(lcCse()) << "Could not write the public key to a file.";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(privKeyFile);
|
|
|
|
fclose(pubKeyFile);
|
2017-09-12 12:21:53 +03:00
|
|
|
|
2017-09-12 21:02:17 +03:00
|
|
|
generateCSR(localKeyPair);
|
|
|
|
|
2017-09-12 15:35:05 +03:00
|
|
|
//TODO: Send to server.
|
2017-09-12 14:44:42 +03:00
|
|
|
qCInfo(lcCse()) << "Keys generated correctly, sending to server.";
|
2017-09-12 12:21:53 +03:00
|
|
|
}
|
|
|
|
|
2017-09-12 21:02:17 +03:00
|
|
|
QString 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.
|
|
|
|
auto certParams = std::map<const char *, const char*>{
|
|
|
|
{"C", "DE"},
|
|
|
|
{"ST", "Baden-Wuerttemberg"},
|
|
|
|
{"L", "Stuttgart"},
|
|
|
|
{"O","Nextcloud"},
|
|
|
|
{"CN", "www.nextcloud.com"}
|
|
|
|
};
|
|
|
|
|
|
|
|
int ret = 0;
|
|
|
|
int nVersion = 1;
|
|
|
|
|
2017-09-13 20:57:39 +03:00
|
|
|
X509_REQ *x509_req = nullptr;
|
2017-09-12 22:52:10 +03:00
|
|
|
auto out = BIO_new(BIO_s_mem());
|
2017-09-13 21:52:55 +03:00
|
|
|
QByteArray output;
|
2017-09-12 22:52:10 +03:00
|
|
|
char data[80];
|
2017-09-13 20:57:39 +03:00
|
|
|
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;
|
|
|
|
goto free_all;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = X509_REQ_set_pubkey(x509_req, keyPair);
|
|
|
|
if (ret != 1){
|
|
|
|
qCInfo(lcCse()) << "Error setting the public key on the csr";
|
|
|
|
goto free_all;
|
|
|
|
}
|
|
|
|
|
|
|
|
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";
|
|
|
|
goto free_all;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = PEM_write_bio_X509_REQ(out, x509_req);
|
2017-09-12 22:52:10 +03:00
|
|
|
do {
|
|
|
|
ret = BIO_gets(out, data, 80);
|
|
|
|
output += data;
|
2017-09-13 21:52:55 +03:00
|
|
|
if (output.endsWith("-----END CERTIFICATE REQUEST-----")) {
|
|
|
|
output = output.trimmed();
|
2017-09-12 22:52:10 +03:00
|
|
|
break;
|
2017-09-13 21:52:55 +03:00
|
|
|
}
|
2017-09-12 22:52:10 +03:00
|
|
|
} while (ret > 0 );
|
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
|
|
|
|
|
|
|
connect(job, &SignPublicKeyApiJob::jsonReceived, [this, keyPair](const QJsonDocument& json, int retCode) {
|
2017-09-14 16:57:41 +03:00
|
|
|
if (retCode == 200) {
|
|
|
|
auto caps = json.object().value("ocs").toObject().value("data").toObject().value("public-key").toString();
|
|
|
|
qCInfo(lcCse()) << "Public Key Returned" << caps;
|
2017-10-30 22:02:55 +03:00
|
|
|
QFile file(publicKeyPath(_account) + ".sign");
|
2017-09-14 16:57:41 +03:00
|
|
|
if (file.open(QIODevice::WriteOnly)) {
|
|
|
|
QTextStream s(&file);
|
|
|
|
s << caps;
|
|
|
|
}
|
|
|
|
file.close();
|
2017-09-14 19:39:18 +03:00
|
|
|
qCInfo(lcCse()) << "public key saved, Encrypting Private Key.";
|
|
|
|
encryptPrivateKey(keyPair);
|
2017-09-14 16:57:41 +03:00
|
|
|
}
|
2017-09-13 20:57:39 +03:00
|
|
|
qCInfo(lcCse()) << retCode;
|
|
|
|
});
|
|
|
|
job->start();
|
2017-09-12 22:52:10 +03:00
|
|
|
|
2017-09-12 21:02:17 +03:00
|
|
|
free_all:
|
|
|
|
X509_REQ_free(x509_req);
|
|
|
|
BIO_free_all(out);
|
|
|
|
return "";
|
2017-09-12 12:21:53 +03:00
|
|
|
}
|
|
|
|
|
2017-09-14 19:39:18 +03:00
|
|
|
void ClientSideEncryption::encryptPrivateKey(EVP_PKEY *keyPair)
|
|
|
|
{
|
|
|
|
// Write the Private File to a BIO
|
|
|
|
// Retrieve the BIO contents, and encrypt it.
|
|
|
|
// Send the encrypted key to the server.
|
|
|
|
// I have no idea what I'm doing.
|
|
|
|
|
2017-09-14 22:41:31 +03:00
|
|
|
using ucharp = unsigned char *;
|
|
|
|
const char *salt = "$4$YmBjm3hk$Qb74D5IUYwghUmzsMqeNFx5z0/8$";
|
|
|
|
const int saltLen = 40;
|
|
|
|
const int iterationCount = 1024;
|
|
|
|
const int keyStrength = 256;
|
2017-09-14 19:39:18 +03:00
|
|
|
BIO* bio = BIO_new(BIO_s_mem());
|
|
|
|
|
|
|
|
QString passPhrase = WordList::getUnifiedString(WordList::getRandomWords(12));
|
|
|
|
const char* passPhrasePtr = qPrintable(passPhrase);
|
|
|
|
qCInfo(lcCse()) << "Passphrase Generated:";
|
|
|
|
qCInfo(lcCse()) << passPhrase;
|
|
|
|
|
|
|
|
// Extract the Private key from the key pair.
|
|
|
|
PEM_write_bio_PrivateKey(bio, keyPair, NULL, NULL, 0, 0, NULL);
|
|
|
|
char data[80];
|
|
|
|
QString output;
|
|
|
|
int ret = 0;
|
|
|
|
do {
|
|
|
|
ret = BIO_gets(bio, data, 80);
|
|
|
|
output += data;
|
|
|
|
if (output.endsWith("-----END PRIVATE KEY-----")) {
|
|
|
|
output = output.trimmed();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (ret > 0 );
|
|
|
|
|
|
|
|
qCInfo(lcCse()) << "Private Key Extracted";
|
|
|
|
qCInfo(lcCse()) << output;
|
2017-09-14 22:41:31 +03:00
|
|
|
|
|
|
|
/* Jesus. the OpenSSL docs do not help at all.
|
|
|
|
* This PKCS5_PBKDF2_HMAC_SHA1 call will generate
|
|
|
|
* a new password from the password that was submited.
|
|
|
|
*/
|
|
|
|
unsigned char secretKey[keyStrength];
|
|
|
|
|
|
|
|
ret = PKCS5_PBKDF2_HMAC_SHA1(
|
|
|
|
passPhrasePtr, // const char *password,
|
|
|
|
passPhrase.size(), // int password length,
|
|
|
|
(ucharp) salt, // const unsigned char *salt,
|
|
|
|
saltLen, // int saltlen,
|
|
|
|
iterationCount, // int iterations,
|
|
|
|
keyStrength, // int keylen,
|
|
|
|
secretKey // unsigned char *out
|
|
|
|
);
|
|
|
|
qCInfo(lcCse()) << "Return of the PKCS5" << ret;
|
|
|
|
qCInfo(lcCse()) << "Result String" << secretKey;
|
|
|
|
|
2017-10-24 16:53:17 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* NOTES: the key + iv have to be base64 encoded in the metadata.
|
|
|
|
*/
|
|
|
|
|
2017-09-14 23:45:12 +03:00
|
|
|
//: FIRST TRY A SILLY PHRASE.
|
|
|
|
//: Hardcoed IV, really bad.
|
2017-09-15 18:59:14 +03:00
|
|
|
unsigned char *fakepass = (unsigned char*) "qwertyuiasdfghjkzxcvbnm,qwertyui";
|
2017-09-14 23:45:12 +03:00
|
|
|
unsigned char *iv = (unsigned char *)"0123456789012345";
|
2017-09-15 18:38:20 +03:00
|
|
|
unsigned char encryptTag[16];
|
|
|
|
|
|
|
|
|
|
|
|
const char *encryptTest = "a quick brown fox jumps over the lazy dog";
|
|
|
|
// TODO: Find a way to
|
|
|
|
unsigned char cryptedText[128];
|
|
|
|
unsigned char decryptedText[128];
|
|
|
|
unsigned char tag[16];
|
|
|
|
int cryptedText_len = encrypt(
|
|
|
|
(unsigned char*) encryptTest, //unsigned char *plaintext,
|
|
|
|
strlen(encryptTest), // int plaintext_len,
|
2017-09-15 18:59:14 +03:00
|
|
|
fakepass, // unsigned char *key,
|
2017-09-15 18:38:20 +03:00
|
|
|
iv, // unsigned char *iv,
|
|
|
|
cryptedText, // unsigned char *ciphertext,
|
|
|
|
tag // unsigned char *tag
|
|
|
|
);
|
2017-10-16 22:06:58 +03:00
|
|
|
/*
|
2017-09-15 18:38:20 +03:00
|
|
|
qCInfo(lcCse()) << "Encrypted Text" << QByteArray( (const char*) cryptedText, cryptedText_len);
|
|
|
|
int decryptedText_len = decrypt(
|
|
|
|
cryptedText, //unsigned char *ciphertext,
|
|
|
|
cryptedText_len, //int ciphertext_len,
|
|
|
|
NULL, //unsigned char *aad,
|
|
|
|
0, //int aad_len,
|
|
|
|
tag, //unsigned char *tag,
|
2017-09-15 18:59:14 +03:00
|
|
|
fakepass, //unsigned char *key,
|
2017-09-15 18:38:20 +03:00
|
|
|
iv, //unsigned char *iv,
|
|
|
|
decryptedText //unsigned char *plaintext
|
|
|
|
);
|
|
|
|
qCInfo(lcCse()) << "Decrypted Text" << QByteArray( (const char*) decryptedText, decryptedText_len);
|
2017-10-16 22:06:58 +03:00
|
|
|
*/
|
|
|
|
// Pretend that the private key is actually encrypted and send it to the server.
|
2017-10-18 21:21:35 +03:00
|
|
|
auto job = new StorePrivateKeyApiJob(_account, baseUrl() + "private-key", this);
|
2017-10-16 22:06:58 +03:00
|
|
|
job->setPrivateKey(QByteArray((const char*) cryptedText, 128));
|
|
|
|
connect(job, &StorePrivateKeyApiJob::jsonReceived, [this](const QJsonDocument& doc, int retCode) {
|
2017-10-18 20:22:59 +03:00
|
|
|
switch(retCode) {
|
|
|
|
case 200:
|
|
|
|
qCInfo(lcCse()) << "Store private key working as expected.";
|
|
|
|
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-09-12 12:21:53 +03:00
|
|
|
void ClientSideEncryption::getPrivateKeyFromServer()
|
|
|
|
{
|
2017-10-16 22:06:58 +03:00
|
|
|
qCInfo(lcCse()) << "Trying to store the private key on the server.";
|
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) {
|
|
|
|
switch(retCode) {
|
|
|
|
case 404: // no public key
|
2017-09-12 14:44:42 +03:00
|
|
|
qCInfo(lcCse()) << "No public key on the server";
|
|
|
|
generateKeyPair();
|
2017-09-12 12:21:53 +03:00
|
|
|
break;
|
|
|
|
case 400: // internal error
|
|
|
|
qCInfo(lcCse()) << "Internal server error while requesting the public key, encryption aborted.";
|
|
|
|
break;
|
|
|
|
case 200: // ok
|
|
|
|
qCInfo(lcCse()) << "Found Public key, requesting Private Key.";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
job->start();
|
|
|
|
}
|
2017-10-23 19:15:39 +03:00
|
|
|
|
|
|
|
|
|
|
|
SignPublicKeyApiJob::SignPublicKeyApiJob(const AccountPtr& account, const QString& path, QObject* parent)
|
|
|
|
: AbstractNetworkJob(account, path, parent)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void SignPublicKeyApiJob::setCsr(const QByteArray& csr)
|
|
|
|
{
|
|
|
|
QByteArray data = "csr=";
|
|
|
|
data += QUrl::toPercentEncoding(csr);
|
|
|
|
_csr.setData(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SignPublicKeyApiJob::start()
|
|
|
|
{
|
|
|
|
QNetworkRequest req;
|
|
|
|
req.setRawHeader("OCS-APIREQUEST", "true");
|
|
|
|
QUrl url = Utility::concatUrlPath(account()->url(), path());
|
|
|
|
QList<QPair<QString, QString>> params = {
|
|
|
|
qMakePair(QString::fromLatin1("format"), QString::fromLatin1("json"))
|
|
|
|
};
|
|
|
|
url.setQueryItems(params);
|
|
|
|
|
|
|
|
qCInfo(lcSignPublicKeyApiJob) << "Sending the CSR" << _csr.data();
|
|
|
|
sendRequest("POST", url, req, &_csr);
|
|
|
|
AbstractNetworkJob::start();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SignPublicKeyApiJob::finished()
|
|
|
|
{
|
|
|
|
qCInfo(lcStorePrivateKeyApiJob()) << "Sending CSR ended with" << path() << errorString() << reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
|
|
|
|
|
|
|
QJsonParseError error;
|
|
|
|
auto json = QJsonDocument::fromJson(reply()->readAll(), &error);
|
|
|
|
emit jsonReceived(json, reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
StorePrivateKeyApiJob::StorePrivateKeyApiJob(const AccountPtr& account, const QString& path, QObject* parent)
|
|
|
|
: AbstractNetworkJob(account, path, parent)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void StorePrivateKeyApiJob::setPrivateKey(const QByteArray& privKey)
|
|
|
|
{
|
|
|
|
QByteArray data = "privateKey=";
|
|
|
|
data += QUrl::toPercentEncoding(privKey);
|
|
|
|
_privKey.setData(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void StorePrivateKeyApiJob::start()
|
|
|
|
{
|
|
|
|
QNetworkRequest req;
|
|
|
|
req.setRawHeader("OCS-APIREQUEST", "true");
|
|
|
|
QUrl url = Utility::concatUrlPath(account()->url(), path());
|
|
|
|
QList<QPair<QString, QString>> params = {
|
|
|
|
qMakePair(QString::fromLatin1("format"), QString::fromLatin1("json"))
|
|
|
|
};
|
|
|
|
url.setQueryItems(params);
|
|
|
|
|
|
|
|
qCInfo(lcStorePrivateKeyApiJob) << "Sending the private key" << _privKey.data();
|
|
|
|
sendRequest("POST", url, req, &_privKey);
|
|
|
|
AbstractNetworkJob::start();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool StorePrivateKeyApiJob::finished()
|
|
|
|
{
|
|
|
|
int retCode = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
|
|
if (retCode != 200)
|
|
|
|
qCInfo(lcStorePrivateKeyApiJob()) << "Sending private key ended with" << path() << errorString() << retCode;
|
|
|
|
|
|
|
|
QJsonParseError error;
|
|
|
|
auto json = QJsonDocument::fromJson(reply()->readAll(), &error);
|
|
|
|
emit jsonReceived(json, reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
|
|
|
|
}
|
|
|
|
|
|
|
|
SetEncryptionFlagApiJob::SetEncryptionFlagApiJob(const AccountPtr& account, const QString& fileId, QObject* parent)
|
|
|
|
: AbstractNetworkJob(account, baseUrl() + QStringLiteral("encrypted/") + fileId, parent), _fileId(fileId)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetEncryptionFlagApiJob::start()
|
|
|
|
{
|
|
|
|
QNetworkRequest req;
|
|
|
|
req.setRawHeader("OCS-APIREQUEST", "true");
|
|
|
|
QUrl url = Utility::concatUrlPath(account()->url(), path());
|
|
|
|
QList<QPair<QString, QString>> params = {
|
|
|
|
qMakePair(QString::fromLatin1("format"), QString::fromLatin1("json"))
|
|
|
|
};
|
|
|
|
url.setQueryItems(params);
|
|
|
|
|
|
|
|
qCInfo(lcCseJob()) << "marking the file with id" << _fileId << "as encrypted";
|
|
|
|
sendRequest("PUT", url, req);
|
|
|
|
AbstractNetworkJob::start();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SetEncryptionFlagApiJob::finished()
|
|
|
|
{
|
|
|
|
int retCode = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
|
|
if (retCode != 200)
|
|
|
|
qCInfo(lcCseJob()) << "Setting the encrypted flag failed with" << path() << errorString() << retCode;
|
|
|
|
|
|
|
|
QJsonParseError error;
|
|
|
|
auto json = QJsonDocument::fromJson(reply()->readAll(), &error);
|
|
|
|
emit jsonReceived(json, reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
|
|
|
|
}
|
|
|
|
|
2017-10-23 20:27:34 +03:00
|
|
|
/* Test metdata:
|
|
|
|
{
|
|
|
|
// Metadata about the share
|
|
|
|
"metadata": {
|
|
|
|
// Encryption algorithm: RSA/ECB/OAEPWithSHA-256AndMGF1Padding, encrypted via private/public key (asymmetric)
|
|
|
|
"metadataKeys": {
|
|
|
|
"0": "OLDESTMETADATAKEY",
|
|
|
|
"2": "…",
|
|
|
|
"3": "NEWESTMETADATAKEY"
|
|
|
|
},
|
|
|
|
// Encryption algorithm: AES/GCM/NoPadding (128 bit key size) with metadata key from above (symmetric)
|
|
|
|
"sharing": {
|
|
|
|
// Name of recipients as well as public keys of the recipients
|
|
|
|
"recipient": {
|
|
|
|
"recipient1@example.com": "PUBLIC KEY",
|
|
|
|
"recipient2@example.com": "PUBLIC KEY"
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"version": 1
|
|
|
|
},
|
|
|
|
// A JSON blob referencing all files
|
|
|
|
"files": {
|
|
|
|
"ia7OEEEyXMoRa1QWQk8r": {
|
|
|
|
// Encryption algorithm: AES/GCM/NoPadding (128 bit key size) with metadata key from above (symmetric)
|
|
|
|
"encrypted": {
|
|
|
|
"key": "jtboLmgGR1OQf2uneqCVHpklQLlIwWL5TXAQ0keK",
|
|
|
|
"filename": "/foo/test.txt",
|
|
|
|
"mimetype": "plain/text",
|
|
|
|
"version": 1
|
|
|
|
},
|
|
|
|
"initializationVector": "+mHu52HyZq+pAAIN",
|
|
|
|
"authenticationTag": "GCM authentication tag",
|
|
|
|
"metadataKey": 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
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-10-30 22:02:55 +03:00
|
|
|
FolderMetadata::FolderMetadata(AccountPtr account, const QByteArray& metadata) : _account(account)
|
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 {
|
|
|
|
qCInfo(lcCse()) << "Metadata already exists, deal with it later.";
|
2017-10-23 20:27:34 +03:00
|
|
|
}
|
2017-10-30 17:39:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// RSA/ECB/OAEPWithSHA-256AndMGF1Padding using private / public key.
|
|
|
|
std::string FolderMetadata::encryptMetadataKeys(const nlohmann::json& metadataKeys) const {
|
2017-10-30 21:05:55 +03:00
|
|
|
std::string metadata = metadataKeys.dump();
|
|
|
|
const char *metadataPtr = metadata.c_str();
|
|
|
|
|
|
|
|
unsigned char *out;
|
|
|
|
unsigned char *in;
|
|
|
|
size_t outLen;
|
|
|
|
size_t inLen;
|
|
|
|
int err = -1;
|
|
|
|
const int rsaOeapPadding = 1;
|
|
|
|
EVP_PKEY *key = nullptr;
|
|
|
|
ENGINE *eng = nullptr;
|
|
|
|
|
2017-10-30 22:02:55 +03:00
|
|
|
auto path = publicKeyPath(_account);
|
|
|
|
const char *pathC = qPrintable(path);
|
|
|
|
|
|
|
|
FILE* pkeyFile = fopen(pathC, "r");
|
|
|
|
if (!pkeyFile) {
|
|
|
|
qCInfo(lcCse()) << "Could not open the public key";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
key = PEM_read_PUBKEY(pkeyFile, NULL, NULL, NULL);
|
|
|
|
|
2017-10-30 21:05:55 +03:00
|
|
|
auto ctx = EVP_PKEY_CTX_new(key, eng);
|
|
|
|
if (!ctx) {
|
|
|
|
qCInfo(lcCse()) << "Could not initialize the pkey context.";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = EVP_PKEY_encrypt_init(ctx);
|
|
|
|
if (err <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Error initilaizing the encryption.";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = EVP_PKEY_CTX_set_rsa_padding(ctx, rsaOeapPadding);
|
|
|
|
if (err <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Error setting the encryption padding.";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = EVP_PKEY_encrypt(ctx, NULL, &outLen, in, inLen);
|
|
|
|
if (err <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Error retrieving the size of the encrypted data";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
out = (uchar*) OPENSSL_malloc(outLen);
|
|
|
|
if (!out) {
|
|
|
|
qCInfo(lcCse()) << "Error requesting memory for the encrypted contents";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = EVP_PKEY_encrypt(ctx, out, &outLen, in, inLen);
|
|
|
|
if (err <= 0) {
|
|
|
|
qCInfo(lcCse()) << "Could not encrypt key.";
|
|
|
|
exit(1);
|
|
|
|
}
|
2017-10-31 14:07:47 +03:00
|
|
|
|
|
|
|
// Transform the encrypted data into base64.
|
|
|
|
const auto raw = QByteArray((const char*) out, outLen);
|
|
|
|
const auto b64 = raw.toBase64();
|
|
|
|
const auto ret = std::string(b64.constData(), b64.length());
|
|
|
|
|
|
|
|
qCInfo(lcCse()) << raw.toBase64();
|
|
|
|
return ret;
|
2017-10-30 17:39:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string FolderMetadata::genMetadataPass() const {
|
2017-10-30 21:08:03 +03:00
|
|
|
const char* charmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
const size_t charmapLength = strlen(charmap);
|
|
|
|
const int bytes = 16;
|
|
|
|
std::string result;
|
|
|
|
result.reserve(bytes);
|
|
|
|
generate_n(back_inserter(result), bytes, [&](){
|
|
|
|
return charmap[rand() % charmapLength];
|
|
|
|
});
|
|
|
|
return result;
|
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";
|
|
|
|
unsigned char *out;
|
|
|
|
size_t outlen;
|
|
|
|
int err = -1;
|
|
|
|
|
|
|
|
auto path = privateKeyPath(_account);
|
|
|
|
auto pathC = qPrintable(path);
|
|
|
|
auto pkeyFile = fopen(pathC, "r");
|
|
|
|
auto key = PEM_read_PrivateKey(pkeyFile, NULL, NULL, NULL);
|
|
|
|
|
2017-10-31 15:17:22 +03:00
|
|
|
// Data is base64 encoded.
|
|
|
|
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 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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";
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2017-10-30 17:39:40 +03:00
|
|
|
// AES/GCM/NoPadding (128 bit key size)
|
|
|
|
std::string FolderMetadata::encryptJsonObject(const nlohmann::json& obj,const std::string& pass) const {
|
|
|
|
return obj.dump();
|
|
|
|
}
|
2017-10-23 20:27:34 +03:00
|
|
|
|
2017-10-30 17:39:40 +03:00
|
|
|
void FolderMetadata::setupEmptyMetadata() {
|
|
|
|
using namespace nlohmann;
|
|
|
|
std::string newMetadataPass = genMetadataPass();
|
|
|
|
json metadataKeyObj = {"0", newMetadataPass};
|
|
|
|
json recepient = {"recipient", {}};
|
|
|
|
json m = {
|
|
|
|
{"metadata", {
|
|
|
|
{"metadataKeys", encryptMetadataKeys(metadataKeyObj)},
|
|
|
|
{"sharing", encryptJsonObject(recepient, newMetadataPass)},
|
|
|
|
{"version",1}
|
|
|
|
}},
|
|
|
|
{"files", {
|
|
|
|
}}
|
|
|
|
};
|
2017-10-30 21:05:55 +03:00
|
|
|
|
2017-10-31 14:07:47 +03:00
|
|
|
std::string result = m.dump();
|
|
|
|
QString output = QString::fromStdString(result);
|
|
|
|
|
|
|
|
qCInfo(lcCse()) << "Current Output" << output;
|
2017-10-23 20:27:34 +03:00
|
|
|
}
|
|
|
|
|
2017-09-11 16:00:01 +03:00
|
|
|
}
|