|
|
@@ -36,7 +36,8 @@
|
|
|
#include <QRandomGenerator>
|
|
|
|
|
|
#include <qt5keychain/keychain.h>
|
|
|
-#include "common/utility.h"
|
|
|
+#include <common/utility.h>
|
|
|
+#include <common/constants.h>
|
|
|
|
|
|
#include "wordlist.h"
|
|
|
|
|
|
@@ -66,6 +67,8 @@ namespace {
|
|
|
const char e2e_private[] = "_e2e-private";
|
|
|
const char e2e_mnemonic[] = "_e2e-mnemonic";
|
|
|
|
|
|
+ const int blockSize = 1024;
|
|
|
+
|
|
|
QList<QByteArray> oldCipherFormatSplit(const QByteArray &cipher)
|
|
|
{
|
|
|
const auto separator = QByteArrayLiteral("fA=="); // BASE64 encoded '|'
|
|
|
@@ -295,7 +298,7 @@ namespace {
|
|
|
};
|
|
|
|
|
|
QByteArray BIO2ByteArray(Bio &b) {
|
|
|
- int pending = BIO_ctrl_pending(b);
|
|
|
+ int pending = static_cast<int>(BIO_ctrl_pending(b));
|
|
|
QByteArray res(pending, '\0');
|
|
|
BIO_read(b, unsignedData(res), pending);
|
|
|
return res;
|
|
|
@@ -424,8 +427,8 @@ QByteArray encryptPrivateKey(
|
|
|
clen += len;
|
|
|
|
|
|
/* Get the tag */
|
|
|
- QByteArray tag(16, '\0');
|
|
|
- if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, unsignedData(tag))) {
|
|
|
+ QByteArray tag(OCC::CommonConstants::e2EeTagSize, '\0');
|
|
|
+ if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, OCC::CommonConstants::e2EeTagSize, unsignedData(tag))) {
|
|
|
qCInfo(lcCse()) << "Error getting the tag";
|
|
|
handleErrors();
|
|
|
}
|
|
|
@@ -463,8 +466,8 @@ QByteArray decryptPrivateKey(const QByteArray& key, const QByteArray& data) {
|
|
|
QByteArray cipherTXT = QByteArray::fromBase64(cipherTXT64);
|
|
|
QByteArray iv = QByteArray::fromBase64(ivB64);
|
|
|
|
|
|
- QByteArray tag = cipherTXT.right(16);
|
|
|
- cipherTXT.chop(16);
|
|
|
+ QByteArray tag = cipherTXT.right(OCC::CommonConstants::e2EeTagSize);
|
|
|
+ cipherTXT.chop(OCC::CommonConstants::e2EeTagSize);
|
|
|
|
|
|
// Init
|
|
|
CipherCtx ctx;
|
|
|
@@ -493,7 +496,7 @@ QByteArray decryptPrivateKey(const QByteArray& key, const QByteArray& data) {
|
|
|
return QByteArray();
|
|
|
}
|
|
|
|
|
|
- QByteArray ptext(cipherTXT.size() + 16, '\0');
|
|
|
+ QByteArray ptext(cipherTXT.size() + OCC::CommonConstants::e2EeTagSize, '\0');
|
|
|
int plen = 0;
|
|
|
|
|
|
/* Provide the message to be decrypted, and obtain the plaintext output.
|
|
|
@@ -553,8 +556,8 @@ QByteArray decryptStringSymmetric(const QByteArray& key, const QByteArray& data)
|
|
|
QByteArray cipherTXT = QByteArray::fromBase64(cipherTXT64);
|
|
|
QByteArray iv = QByteArray::fromBase64(ivB64);
|
|
|
|
|
|
- QByteArray tag = cipherTXT.right(16);
|
|
|
- cipherTXT.chop(16);
|
|
|
+ QByteArray tag = cipherTXT.right(OCC::CommonConstants::e2EeTagSize);
|
|
|
+ cipherTXT.chop(OCC::CommonConstants::e2EeTagSize);
|
|
|
|
|
|
// Init
|
|
|
CipherCtx ctx;
|
|
|
@@ -583,7 +586,7 @@ QByteArray decryptStringSymmetric(const QByteArray& key, const QByteArray& data)
|
|
|
return QByteArray();
|
|
|
}
|
|
|
|
|
|
- QByteArray ptext(cipherTXT.size() + 16, '\0');
|
|
|
+ QByteArray ptext(cipherTXT.size() + OCC::CommonConstants::e2EeTagSize, '\0');
|
|
|
int plen = 0;
|
|
|
|
|
|
/* Provide the message to be decrypted, and obtain the plaintext output.
|
|
|
@@ -687,8 +690,8 @@ QByteArray encryptStringSymmetric(const QByteArray& key, const QByteArray& data)
|
|
|
clen += len;
|
|
|
|
|
|
/* Get the tag */
|
|
|
- QByteArray tag(16, '\0');
|
|
|
- if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, unsignedData(tag))) {
|
|
|
+ QByteArray tag(OCC::CommonConstants::e2EeTagSize, '\0');
|
|
|
+ if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, OCC::CommonConstants::e2EeTagSize, unsignedData(tag))) {
|
|
|
qCInfo(lcCse()) << "Error getting the tag";
|
|
|
handleErrors();
|
|
|
return {};
|
|
|
@@ -753,7 +756,7 @@ QByteArray decryptStringAsymmetric(EVP_PKEY *privateKey, const QByteArray& data)
|
|
|
qCInfo(lcCseDecryption()) << "Size of data is: " << data.size();
|
|
|
}
|
|
|
|
|
|
- QByteArray out(outlen, '\0');
|
|
|
+ QByteArray out(static_cast<int>(outlen), '\0');
|
|
|
|
|
|
if (EVP_PKEY_decrypt(ctx, unsignedData(out), &outlen, (unsigned char *)data.constData(), data.size()) <= 0) {
|
|
|
const auto error = handleErrors();
|
|
|
@@ -804,7 +807,7 @@ QByteArray encryptStringAsymmetric(EVP_PKEY *publicKey, const QByteArray& data)
|
|
|
qCInfo(lcCse()) << "Encryption Length:" << outLen;
|
|
|
}
|
|
|
|
|
|
- QByteArray out(outLen, '\0');
|
|
|
+ QByteArray out(static_cast<int>(outLen), '\0');
|
|
|
if (EVP_PKEY_encrypt(ctx, unsignedData(out), &outLen, (unsigned char *)data.constData(), data.size()) != 1) {
|
|
|
qCInfo(lcCse()) << "Could not encrypt key." << err;
|
|
|
exit(1);
|
|
|
@@ -1650,13 +1653,13 @@ bool EncryptionHelper::fileEncryption(const QByteArray &key, const QByteArray &i
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- QByteArray out(1024 + 16 - 1, '\0');
|
|
|
+ QByteArray out(blockSize + OCC::CommonConstants::e2EeTagSize - 1, '\0');
|
|
|
int len = 0;
|
|
|
int total_len = 0;
|
|
|
|
|
|
qCDebug(lcCse) << "Starting to encrypt the file" << input->fileName() << input->atEnd();
|
|
|
while(!input->atEnd()) {
|
|
|
- QByteArray data = input->read(1024);
|
|
|
+ QByteArray data = input->read(blockSize);
|
|
|
|
|
|
if (data.size() == 0) {
|
|
|
qCInfo(lcCse()) << "Could not read data from file";
|
|
|
@@ -1681,14 +1684,14 @@ bool EncryptionHelper::fileEncryption(const QByteArray &key, const QByteArray &i
|
|
|
total_len += len;
|
|
|
|
|
|
/* Get the tag */
|
|
|
- QByteArray tag(16, '\0');
|
|
|
- if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, unsignedData(tag))) {
|
|
|
+ QByteArray tag(OCC::CommonConstants::e2EeTagSize, '\0');
|
|
|
+ if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, OCC::CommonConstants::e2EeTagSize, unsignedData(tag))) {
|
|
|
qCInfo(lcCse()) << "Could not get tag";
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
returnTag = tag;
|
|
|
- output->write(tag, 16);
|
|
|
+ output->write(tag, OCC::CommonConstants::e2EeTagSize);
|
|
|
|
|
|
input->close();
|
|
|
output->close();
|
|
|
@@ -1731,16 +1734,16 @@ bool EncryptionHelper::fileDecryption(const QByteArray &key, const QByteArray& i
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- qint64 size = input->size() - 16;
|
|
|
+ qint64 size = input->size() - OCC::CommonConstants::e2EeTagSize;
|
|
|
|
|
|
- QByteArray out(1024 + 16 - 1, '\0');
|
|
|
+ QByteArray out(blockSize + OCC::CommonConstants::e2EeTagSize - 1, '\0');
|
|
|
int len = 0;
|
|
|
|
|
|
while(input->pos() < size) {
|
|
|
|
|
|
auto toRead = size - input->pos();
|
|
|
- if (toRead > 1024) {
|
|
|
- toRead = 1024;
|
|
|
+ if (toRead > blockSize) {
|
|
|
+ toRead = blockSize;
|
|
|
}
|
|
|
|
|
|
QByteArray data = input->read(toRead);
|
|
|
@@ -1758,7 +1761,7 @@ bool EncryptionHelper::fileDecryption(const QByteArray &key, const QByteArray& i
|
|
|
output->write(out, len);
|
|
|
}
|
|
|
|
|
|
- QByteArray tag = input->read(16);
|
|
|
+ QByteArray tag = input->read(OCC::CommonConstants::e2EeTagSize);
|
|
|
|
|
|
/* 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())) {
|
|
|
@@ -1777,4 +1780,172 @@ bool EncryptionHelper::fileDecryption(const QByteArray &key, const QByteArray& i
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+EncryptionHelper::StreamingDecryptor::StreamingDecryptor(const QByteArray &key, const QByteArray &iv, quint64 totalSize) : _totalSize(totalSize)
|
|
|
+{
|
|
|
+ if (_ctx && !key.isEmpty() && !iv.isEmpty() && totalSize > 0) {
|
|
|
+ _isInitialized = true;
|
|
|
+
|
|
|
+ /* Initialize the decryption operation. */
|
|
|
+ if(!EVP_DecryptInit_ex(_ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
|
|
|
+ qCritical(lcCse()) << "Could not init cipher";
|
|
|
+ _isInitialized = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ EVP_CIPHER_CTX_set_padding(_ctx, 0);
|
|
|
+
|
|
|
+ /* Set IV length. */
|
|
|
+ if(!EVP_CIPHER_CTX_ctrl(_ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr)) {
|
|
|
+ qCritical(lcCse()) << "Could not set iv length";
|
|
|
+ _isInitialized = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Initialize key and IV */
|
|
|
+ if(!EVP_DecryptInit_ex(_ctx, nullptr, nullptr, reinterpret_cast<const unsigned char*>(key.constData()), reinterpret_cast<const unsigned char*>(iv.constData()))) {
|
|
|
+ qCritical(lcCse()) << "Could not set key and iv";
|
|
|
+ _isInitialized = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+qint32 EncryptionHelper::StreamingDecryptor::chunkDecryption(const char *input, QIODevice *output, quint32 chunkSize)
|
|
|
+{
|
|
|
+ Q_ASSERT(isInitialized());
|
|
|
+ if (!isInitialized()) {
|
|
|
+ qCritical(lcCse()) << "Decryption failed. Decryptor is not initialized!";
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ Q_ASSERT(output && output->isOpen() && output->isWritable());
|
|
|
+ if (!output || !output->isOpen() || !output->isWritable()) {
|
|
|
+ qCritical(lcCse()) << "Decryption failed. Incorrect output device!";
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ Q_ASSERT(input);
|
|
|
+ if (!input) {
|
|
|
+ qCritical(lcCse()) << "Decryption failed. Incorrect input!";
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ Q_ASSERT(chunkSize > 0);
|
|
|
+ if (chunkSize <= 0) {
|
|
|
+ qCritical(lcCse()) << "Decryption failed. Incorrect chunkSize!";
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_decryptedSoFar == 0) {
|
|
|
+ qCDebug(lcCse()) << "Decryption started";
|
|
|
+ }
|
|
|
+
|
|
|
+ Q_ASSERT(_decryptedSoFar + chunkSize <= _totalSize);
|
|
|
+ if (_decryptedSoFar + chunkSize > _totalSize) {
|
|
|
+ qCritical(lcCse()) << "Decryption failed. Chunk is out of range!";
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ const bool isLastChunk = _decryptedSoFar + chunkSize == _totalSize;
|
|
|
+
|
|
|
+ // last OCC::CommonConstants::e2EeTagSize bytes is ALWAYS a tag!!!
|
|
|
+ const qint32 size = isLastChunk ? static_cast<qint32>(chunkSize) - OCC::CommonConstants::e2EeTagSize : static_cast<qint32>(chunkSize);
|
|
|
+
|
|
|
+ Q_ASSERT(size > 0);
|
|
|
+ if (size <= 0) {
|
|
|
+ qCritical(lcCse()) << "Decryption failed. Invalid input size: " << size << " !";
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ int bytesWritten = 0;
|
|
|
+
|
|
|
+ QByteArray decryptedBlock(blockSize + OCC::CommonConstants::e2EeTagSize - 1, '\0');
|
|
|
+ int inputPos = 0;
|
|
|
+
|
|
|
+ while(inputPos < size) {
|
|
|
+ // read blockSize or less bytes
|
|
|
+ const QByteArray encryptedBlock = QByteArray(input + inputPos, qMin(size - inputPos, blockSize));
|
|
|
+
|
|
|
+ if (encryptedBlock.size() == 0) {
|
|
|
+ qCritical(lcCse()) << "Could not read data from the input buffer.";
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ int outLen = 0;
|
|
|
+
|
|
|
+ if(!EVP_DecryptUpdate(_ctx, unsignedData(decryptedBlock), &outLen, reinterpret_cast<const unsigned char*>(encryptedBlock.data()), encryptedBlock.size())) {
|
|
|
+ qCritical(lcCse()) << "Could not decrypt";
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ const auto writtenToOutput = output->write(decryptedBlock, outLen);
|
|
|
+
|
|
|
+ Q_ASSERT(writtenToOutput == outLen);
|
|
|
+ if (writtenToOutput != outLen) {
|
|
|
+ qCritical(lcCse()) << "Failed to write decrypted data to device.";
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ bytesWritten += writtenToOutput;
|
|
|
+
|
|
|
+ // advance input position for further read
|
|
|
+ inputPos += encryptedBlock.size();
|
|
|
+
|
|
|
+ _decryptedSoFar += encryptedBlock.size();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isLastChunk) {
|
|
|
+ // if it's a last chunk, we'd need to read a tag at the end and finalize the decryption
|
|
|
+
|
|
|
+ Q_ASSERT(chunkSize - inputPos == OCC::CommonConstants::e2EeTagSize);
|
|
|
+ if (chunkSize - inputPos != OCC::CommonConstants::e2EeTagSize) {
|
|
|
+ qCritical(lcCse()) << "Decryption failed. E2EE tag is missing!";
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ int outLen = 0;
|
|
|
+
|
|
|
+ QByteArray tag = QByteArray(input + inputPos, OCC::CommonConstants::e2EeTagSize);
|
|
|
+
|
|
|
+ /* 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(), reinterpret_cast<unsigned char*>(tag.data()))) {
|
|
|
+ qCritical(lcCse()) << "Could not set expected tag";
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(1 != EVP_DecryptFinal_ex(_ctx, unsignedData(decryptedBlock), &outLen)) {
|
|
|
+ qCritical(lcCse()) << "Could finalize decryption";
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ const auto writtenToOutput = output->write(decryptedBlock, outLen);
|
|
|
+
|
|
|
+ Q_ASSERT(writtenToOutput == outLen);
|
|
|
+ if (writtenToOutput != outLen) {
|
|
|
+ qCritical(lcCse()) << "Failed to write decrypted data to device.";
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ bytesWritten += writtenToOutput;
|
|
|
+
|
|
|
+ _decryptedSoFar += OCC::CommonConstants::e2EeTagSize;
|
|
|
+
|
|
|
+ _isFinished = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // qCDebug(lcCse()) <<"Decrypting:" << _decryptedSoFar << "/" << _totalSize;
|
|
|
+
|
|
|
+ if (isFinished()) {
|
|
|
+ qCDebug(lcCse()) << "Decryption complete";
|
|
|
+ }
|
|
|
+
|
|
|
+ return bytesWritten;
|
|
|
+}
|
|
|
+
|
|
|
+bool EncryptionHelper::StreamingDecryptor::isInitialized() const
|
|
|
+{
|
|
|
+ return _isInitialized;
|
|
|
+}
|
|
|
+
|
|
|
+bool EncryptionHelper::StreamingDecryptor::isFinished() const
|
|
|
+{
|
|
|
+ return _isFinished;
|
|
|
}
|
|
|
+};
|