Merge pull request #5586 from nextcloud/bugfix/e2eeBetterInit

Bugfix/e2ee better init
This commit is contained in:
Matthieu Gallien 2023-06-30 16:17:26 +02:00 committed by GitHub
commit bea7bd1a4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 359 additions and 104 deletions

View File

@ -1561,13 +1561,7 @@ void AccountSettings::initializeE2eEncryption()
if (!_accountState->account()->e2e()->_mnemonic.isEmpty()) {
slotE2eEncryptionMnemonicReady();
} else {
_ui->encryptionMessage->setMessageType(KMessageWidget::Information);
_ui->encryptionMessage->setText(tr("This account supports end-to-end encryption"));
_ui->encryptionMessage->setIcon(Theme::createColorAwareIcon(QStringLiteral(":/client/theme/black/state-info.svg")));
_ui->encryptionMessage->hide();
auto *const actionEnableE2e = addActionToEncryptionMessage(tr("Set up encryption"), e2EeUiActionEnableEncryptionId);
connect(actionEnableE2e, &QAction::triggered, this, &AccountSettings::slotE2eEncryptionGenerateKeys);
initializeE2eEncryptionSettingsMessage();
connect(_accountState->account()->e2e(), &ClientSideEncryption::initializationFinished, this, [this] {
if (!_accountState->account()->e2e()->_publicKey.isNull()) {
@ -1590,7 +1584,7 @@ void AccountSettings::resetE2eEncryption()
}
_ui->encryptionMessage->setText({});
_ui->encryptionMessage->setIcon({});
initializeE2eEncryption();
initializeE2eEncryptionSettingsMessage();
checkClientSideEncryptionState();
const auto account = _accountState->account();
@ -1627,6 +1621,17 @@ QAction *AccountSettings::addActionToEncryptionMessage(const QString &actionTitl
return action;
}
void AccountSettings::initializeE2eEncryptionSettingsMessage()
{
_ui->encryptionMessage->setMessageType(KMessageWidget::Information);
_ui->encryptionMessage->setText(tr("This account supports end-to-end encryption"));
_ui->encryptionMessage->setIcon(Theme::createColorAwareIcon(QStringLiteral(":/client/theme/black/state-info.svg")));
_ui->encryptionMessage->hide();
auto *const actionEnableE2e = addActionToEncryptionMessage(tr("Set up encryption"), e2EeUiActionEnableEncryptionId);
connect(actionEnableE2e, &QAction::triggered, this, &AccountSettings::slotE2eEncryptionGenerateKeys);
}
} // namespace OCC
#include "accountsettings.moc"

View File

@ -132,6 +132,8 @@ private:
bool event(QEvent *) override;
QAction *addActionToEncryptionMessage(const QString &actionTitle, const QString &actionId);
void initializeE2eEncryptionSettingsMessage();
/// Returns the alias of the selected folder, empty string if none
[[nodiscard]] QString selectedFolderAlias() const;

View File

@ -63,9 +63,10 @@ QString e2eeBaseUrl()
namespace {
constexpr char accountProperty[] = "account";
const char e2e_cert[] = "_e2e-certificate";
const char e2e_private[] = "_e2e-private";
const char e2e_mnemonic[] = "_e2e-mnemonic";
constexpr char e2e_cert[] = "_e2e-certificate";
constexpr char e2e_private[] = "_e2e-private";
constexpr char e2e_public[] = "_e2e-public";
constexpr char e2e_mnemonic[] = "_e2e-mnemonic";
constexpr auto metadataKeyJsonKey = "metadataKey";
@ -145,6 +146,11 @@ public:
BIO_free_all(_bio);
}
operator const BIO*() const
{
return _bio;
}
operator BIO*()
{
return _bio;
@ -881,10 +887,10 @@ void ClientSideEncryption::initialize(const AccountPtr &account)
return;
}
fetchFromKeyChain(account);
fetchCertificateFromKeyChain(account);
}
void ClientSideEncryption::fetchFromKeyChain(const AccountPtr &account)
void ClientSideEncryption::fetchCertificateFromKeyChain(const AccountPtr &account)
{
const QString kck = AbstractCredentials::keychainKey(
account->url().toString(),
@ -892,6 +898,22 @@ void ClientSideEncryption::fetchFromKeyChain(const AccountPtr &account)
account->id()
);
const auto job = new ReadPasswordJob(Theme::instance()->appName());
job->setProperty(accountProperty, QVariant::fromValue(account));
job->setInsecureFallback(false);
job->setKey(kck);
connect(job, &ReadPasswordJob::finished, this, &ClientSideEncryption::publicCertificateFetched);
job->start();
}
void ClientSideEncryption::fetchPublicKeyFromKeyChain(const AccountPtr &account)
{
const QString kck = AbstractCredentials::keychainKey(
account->url().toString(),
account->credentials()->user() + e2e_public,
account->id()
);
auto *job = new ReadPasswordJob(Theme::instance()->appName());
job->setProperty(accountProperty, QVariant::fromValue(account));
job->setInsecureFallback(false);
@ -950,7 +972,7 @@ bool ClientSideEncryption::checkServerPublicKeyValidity(const QByteArray &server
return true;
}
void ClientSideEncryption::publicKeyFetched(Job *incoming)
void ClientSideEncryption::publicCertificateFetched(Job *incoming)
{
auto *readJob = dynamic_cast<ReadPasswordJob *>(incoming);
auto account = readJob->property(accountProperty).value<AccountPtr>();
@ -958,14 +980,14 @@ void ClientSideEncryption::publicKeyFetched(Job *incoming)
// Error or no valid public key error out
if (readJob->error() != NoError || readJob->binaryData().length() == 0) {
getPublicKeyFromServer(account);
fetchPublicKeyFromKeyChain(account);
return;
}
_certificate = QSslCertificate(readJob->binaryData(), QSsl::Pem);
if (_certificate.isNull()) {
getPublicKeyFromServer(account);
fetchPublicKeyFromKeyChain(account);
return;
}
@ -987,6 +1009,41 @@ void ClientSideEncryption::publicKeyFetched(Job *incoming)
job->start();
}
void ClientSideEncryption::publicKeyFetched(QKeychain::Job *incoming)
{
const auto readJob = dynamic_cast<ReadPasswordJob *>(incoming);
const auto account = readJob->property(accountProperty).value<AccountPtr>();
Q_ASSERT(account);
// Error or no valid public key error out
if (readJob->error() != NoError || readJob->binaryData().length() == 0) {
getPublicKeyFromServer(account);
return;
}
const auto publicKey = QSslKey(readJob->binaryData(), QSsl::Rsa, QSsl::Pem, QSsl::PublicKey);
if (publicKey.isNull()) {
getPublicKeyFromServer(account);
return;
}
_publicKey = publicKey;
const QString kck = AbstractCredentials::keychainKey(
account->url().toString(),
account->credentials()->user() + e2e_private,
account->id()
);
const auto job = new ReadPasswordJob(Theme::instance()->appName());
job->setProperty(accountProperty, QVariant::fromValue(account));
job->setInsecureFallback(false);
job->setKey(kck);
connect(job, &ReadPasswordJob::finished, this, &ClientSideEncryption::privateKeyFetched);
job->start();
}
void ClientSideEncryption::privateKeyFetched(Job *incoming)
{
auto *readJob = dynamic_cast<ReadPasswordJob *>(incoming);
@ -995,8 +1052,7 @@ void ClientSideEncryption::privateKeyFetched(Job *incoming)
// Error or no valid public key error out
if (readJob->error() != NoError || readJob->binaryData().length() == 0) {
_certificate = QSslCertificate();
_publicKey = QSslKey();
forgetSensitiveData(account);
getPublicKeyFromServer(account);
return;
}
@ -1033,9 +1089,7 @@ void ClientSideEncryption::mnemonicKeyFetched(QKeychain::Job *incoming)
// Error or no valid public key error out
if (readJob->error() != NoError || readJob->textData().length() == 0) {
_certificate = QSslCertificate();
_publicKey = QSslKey();
_privateKey = QByteArray();
forgetSensitiveData(account);
getPublicKeyFromServer(account);
return;
}
@ -1044,7 +1098,7 @@ void ClientSideEncryption::mnemonicKeyFetched(QKeychain::Job *incoming)
qCInfo(lcCse()) << "Mnemonic key fetched from keychain: " << _mnemonic;
emit initializationFinished();
checkServerHasSavedKeys(account);
}
void ClientSideEncryption::writePrivateKey(const AccountPtr &account)
@ -1085,7 +1139,15 @@ void ClientSideEncryption::writeCertificate(const AccountPtr &account)
job->start();
}
void ClientSideEncryption::writeMnemonic(const AccountPtr &account)
void ClientSideEncryption::generateMnemonic()
{
const auto list = WordList::getRandomWords(12);
_mnemonic = list.join(' ');
}
template <typename L>
void ClientSideEncryption::writeMnemonic(OCC::AccountPtr account,
L nextCall)
{
const QString kck = AbstractCredentials::keychainKey(
account->url().toString(),
@ -1097,17 +1159,19 @@ void ClientSideEncryption::writeMnemonic(const AccountPtr &account)
job->setInsecureFallback(false);
job->setKey(kck);
job->setTextData(_mnemonic);
connect(job, &WritePasswordJob::finished, [](Job *incoming) {
Q_UNUSED(incoming);
qCInfo(lcCse()) << "Mnemonic stored in keychain";
connect(job, &WritePasswordJob::finished, [this, account, nextCall = std::move(nextCall)](Job *incoming) mutable {
if (incoming->error() != Error::NoError) {
failedToInitialize(account);
return;
}
nextCall();
});
job->start();
}
void ClientSideEncryption::forgetSensitiveData(const AccountPtr &account)
{
_publicKey = QSslKey();
if (!sensitiveDataRemaining()) {
checkAllSensitiveDataDeleted();
return;
@ -1175,22 +1239,40 @@ void ClientSideEncryption::handleMnemonicDeleted(const QKeychain::Job* const inc
checkAllSensitiveDataDeleted();
}
void ClientSideEncryption::handlePublicKeyDeleted(const QKeychain::Job * const incoming)
{
const auto error = incoming->error();
if (error != QKeychain::NoError && error != QKeychain::EntryNotFound) {
qCWarning(lcCse) << "Public key could not be deleted:" << incoming->errorString();
return;
}
_publicKey.clear();
Q_EMIT publicKeyDeleted();
checkAllSensitiveDataDeleted();
}
bool ClientSideEncryption::sensitiveDataRemaining() const
{
return !_privateKey.isEmpty() || !_certificate.isNull() || !_mnemonic.isEmpty();
}
void ClientSideEncryption::failedToInitialize(const AccountPtr &account)
{
forgetSensitiveData(account);
Q_EMIT initializationFinished();
}
void ClientSideEncryption::checkAllSensitiveDataDeleted()
{
if (sensitiveDataRemaining()) {
qCDebug(lcCse) << "Some sensitive data emaining:"
<< "Private key:" << _privateKey
<< "Certificate is null:" << _certificate.isNull()
<< "Mnemonic:" << _mnemonic;
qCWarning(lcCse) << "Some sensitive data emaining:"
<< "Private key:" << (_privateKey.isEmpty() ? "is empty" : "is not empty")
<< "Certificate is null:" << (_certificate.isNull() ? "true" : "false")
<< "Mnemonic:" << (_mnemonic.isEmpty() ? "is empty" : "is not empty");
return;
}
qCDebug(lcCse) << "All sensitive encryption data has been deleted.";
Q_EMIT sensitiveDataForgotten();
}
@ -1207,41 +1289,57 @@ void ClientSideEncryption::generateKeyPair(const AccountPtr &account)
if(EVP_PKEY_keygen_init(ctx) <= 0) {
qCInfo(lcCse()) << "Couldn't initialize the key generator";
failedToInitialize(account);
return;
}
if(EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, rsaKeyLen) <= 0) {
qCInfo(lcCse()) << "Couldn't initialize the key generator bits";
failedToInitialize(account);
return;
}
auto localKeyPair = PKey::generate(ctx);
if(!localKeyPair) {
qCInfo(lcCse()) << "Could not generate the key";
failedToInitialize(account);
return;
}
qCInfo(lcCse()) << "Key correctly generated";
qCInfo(lcCse()) << "Storing keys locally";
{
Bio privKey;
if (PEM_write_bio_PrivateKey(privKey, localKeyPair, nullptr, nullptr, 0, nullptr, nullptr) <= 0) {
qCWarning(lcCse()) << "Could not read private key from bio.";
failedToInitialize(account);
return;
}
_privateKey = BIO2ByteArray(privKey);
}
Bio privKey;
if (PEM_write_bio_PrivateKey(privKey, localKeyPair, nullptr, nullptr, 0, nullptr, nullptr) <= 0) {
qCInfo(lcCse()) << "Could not read private key from bio.";
failedToInitialize(account);
return;
}
QByteArray key = BIO2ByteArray(privKey);
//_privateKey = QSslKey(key, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
_privateKey = key;
qCInfo(lcCse()) << "Keys generated correctly, sending to server.";
generateCSR(account, std::move(localKeyPair));
qCDebug(lcCse()) << "Key correctly generated";
auto csrContent = generateCSR(account, std::move(localKeyPair), PKey::readPrivateKey(privKey));
writeMnemonic(account, [account, keyPair = std::move(csrContent.second), csrContent = std::move(csrContent.first), this]() mutable -> void {
writeKeyPair(account, std::move(keyPair), csrContent);
});
}
void ClientSideEncryption::generateCSR(const AccountPtr &account, PKey keyPair)
std::pair<QByteArray, ClientSideEncryption::PKey> ClientSideEncryption::generateCSR(const AccountPtr &account,
PKey keyPair,
PKey privateKey)
{
auto result = QByteArray{};
// OpenSSL expects const char.
auto cnArray = account->davUser().toLocal8Bit();
qCInfo(lcCse()) << "Getting the following array for the account Id" << cnArray;
auto certParams = std::map<const char *, const char*>{
{"C", "DE"},
@ -1255,7 +1353,7 @@ void ClientSideEncryption::generateCSR(const AccountPtr &account, PKey keyPair)
int nVersion = 1;
// 2. set version of x509 req
X509_REQ *x509_req = X509_REQ_new();
auto x509_req = X509_REQ_new();
auto release_on_exit_x509_req = qScopeGuard([&] {
X509_REQ_free(x509_req);
});
@ -1268,34 +1366,44 @@ void ClientSideEncryption::generateCSR(const AccountPtr &account, PKey keyPair)
for(const auto& v : certParams) {
ret = X509_NAME_add_entry_by_txt(x509_name, v.first, MBSTRING_ASC, (const unsigned char*) v.second, -1, -1, 0);
if (ret != 1) {
qCInfo(lcCse()) << "Error Generating the Certificate while adding" << v.first << v.second;
return;
qCWarning(lcCse()) << "Error Generating the Certificate while adding" << v.first << v.second;
return {result, std::move(keyPair)};
}
}
ret = X509_REQ_set_pubkey(x509_req, keyPair);
if (ret != 1){
qCInfo(lcCse()) << "Error setting the public key on the csr";
return;
qCWarning(lcCse()) << "Error setting the public key on the csr";
return {result, std::move(keyPair)};
}
ret = X509_REQ_sign(x509_req, keyPair, EVP_sha1()); // return x509_req->signature->length
ret = X509_REQ_sign(x509_req, privateKey, EVP_sha1()); // return x509_req->signature->length
if (ret <= 0){
qCInfo(lcCse()) << "Error setting the public key on the csr";
return;
qCWarning(lcCse()) << "Error signing the csr with the private key";
return {result, std::move(keyPair)};
}
Bio out;
ret = PEM_write_bio_X509_REQ(out, x509_req);
QByteArray output = BIO2ByteArray(out);
if (ret <= 0){
qCWarning(lcCse()) << "Error exporting the csr to the BIO";
return {result, std::move(keyPair)};
}
qCInfo(lcCse()) << "Returning the certificate";
qCInfo(lcCse()) << output;
result = BIO2ByteArray(out);
sendSignRequestCSR(account, std::move(keyPair), output);
qCDebug(lcCse()) << "CSR generated";
if (_mnemonic.isEmpty()) {
generateMnemonic();
}
return {result, std::move(keyPair)};
}
void ClientSideEncryption::sendSignRequestCSR(const AccountPtr &account, PKey keyPair, const QByteArray &csrContent)
void ClientSideEncryption::sendSignRequestCSR(const AccountPtr &account,
PKey keyPair,
const QByteArray &csrContent)
{
auto job = new SignPublicKeyApiJob(account, e2eeBaseUrl() + "public-key", this);
job->setCsr(csrContent);
@ -1312,28 +1420,143 @@ void ClientSideEncryption::sendSignRequestCSR(const AccountPtr &account, PKey ke
if (!X509_check_private_key(x509Certificate, keyPair)) {
auto lastError = ERR_get_error();
while (lastError) {
qCInfo(lcCse()) << ERR_lib_error_string(lastError);
qCWarning(lcCse()) << ERR_lib_error_string(lastError);
lastError = ERR_get_error();
}
forgetSensitiveData(account);
failedToInitialize(account);
return;
}
qCInfo(lcCse()) << "received a valid certificate";
fetchAndValidatePublicKeyFromServer(account);
} else {
qCWarning(lcCse()) << retCode;
failedToInitialize(account);
return;
}
qCInfo(lcCse()) << retCode;
});
job->start();
}
void ClientSideEncryption::writeKeyPair(const AccountPtr &account,
PKey keyPair,
const QByteArray &csrContent)
{
const auto privateKeyKeychainId = AbstractCredentials::keychainKey(
account->url().toString(),
account->credentials()->user() + e2e_private,
account->id()
);
const auto publicKeyKeychainId = AbstractCredentials::keychainKey(
account->url().toString(),
account->credentials()->user() + e2e_public,
account->id()
);
Bio privateKey;
if (PEM_write_bio_PrivateKey(privateKey, keyPair, nullptr, nullptr, 0, nullptr, nullptr) <= 0) {
qCWarning(lcCse()) << "Could not read private key from bio.";
failedToInitialize(account);
return;
}
const auto bytearrayPrivateKey = BIO2ByteArray(privateKey);
const auto privateKeyJob = new WritePasswordJob(Theme::instance()->appName());
privateKeyJob->setInsecureFallback(false);
privateKeyJob->setKey(privateKeyKeychainId);
privateKeyJob->setBinaryData(bytearrayPrivateKey);
connect(privateKeyJob, &WritePasswordJob::finished, [keyPair = std::move(keyPair), publicKeyKeychainId, account, csrContent, this] (Job *incoming) mutable {
if (incoming->error() != Error::NoError) {
failedToInitialize(account);
return;
}
Bio publicKey;
if (PEM_write_bio_PUBKEY(publicKey, keyPair) <= 0) {
qCWarning(lcCse()) << "Could not read public key from bio.";
failedToInitialize(account);
return;
}
const auto bytearrayPublicKey = BIO2ByteArray(publicKey);
const auto publicKeyJob = new WritePasswordJob(Theme::instance()->appName());
publicKeyJob->setInsecureFallback(false);
publicKeyJob->setKey(publicKeyKeychainId);
publicKeyJob->setBinaryData(bytearrayPublicKey);
connect(publicKeyJob, &WritePasswordJob::finished, [account, keyPair = std::move(keyPair), csrContent, this](Job *incoming) mutable {
if (incoming->error() != Error::NoError) {
failedToInitialize(account);
return;
}
sendSignRequestCSR(account, std::move(keyPair), csrContent);
});
publicKeyJob->start();
});
privateKeyJob->start();
}
void ClientSideEncryption::checkServerHasSavedKeys(const AccountPtr &account)
{
const auto keyIsNotOnServer = [account, this] () {
qCInfo(lcCse) << "server is missing keys. deleting local keys";
failedToInitialize(account);
};
const auto privateKeyOnServerIsValid = [this] () {
Q_EMIT initializationFinished();
};
const auto publicKeyOnServerIsValid = [this, account, privateKeyOnServerIsValid, keyIsNotOnServer] () {
checkUserPrivateKeyOnServer(account, privateKeyOnServerIsValid, keyIsNotOnServer);
};
checkUserPublicKeyOnServer(account, publicKeyOnServerIsValid, keyIsNotOnServer);
}
template <typename SUCCESS_CALLBACK, typename ERROR_CALLBACK>
void ClientSideEncryption::checkUserKeyOnServer(const QString &keyType,
const AccountPtr &account,
SUCCESS_CALLBACK nextCheck,
ERROR_CALLBACK onError)
{
auto job = new JsonApiJob(account, e2eeBaseUrl() + keyType, this);
connect(job, &JsonApiJob::jsonReceived, [nextCheck, onError](const QJsonDocument& doc, int retCode) {
Q_UNUSED(doc)
if (retCode == 200) {
nextCheck();
} else {
onError();
}
});
job->start();
}
template <typename SUCCESS_CALLBACK, typename ERROR_CALLBACK>
void ClientSideEncryption::checkUserPublicKeyOnServer(const AccountPtr &account,
SUCCESS_CALLBACK nextCheck,
ERROR_CALLBACK onError)
{
checkUserKeyOnServer("public-key", account, nextCheck, onError);
}
template <typename SUCCESS_CALLBACK, typename ERROR_CALLBACK>
void ClientSideEncryption::checkUserPrivateKeyOnServer(const AccountPtr &account, SUCCESS_CALLBACK nextCheck, ERROR_CALLBACK onError)
{
checkUserKeyOnServer("private-key", account, nextCheck, onError);
}
void ClientSideEncryption::encryptPrivateKey(const AccountPtr &account)
{
QStringList list = WordList::getRandomWords(12);
_mnemonic = list.join(' ');
qCInfo(lcCse()) << "mnemonic Generated:" << _mnemonic;
if (_mnemonic.isEmpty()) {
generateMnemonic();
}
QString passPhrase = list.join(QString()).toLower();
qCInfo(lcCse()) << "Passphrase Generated:" << passPhrase;
auto passPhrase = _mnemonic;
passPhrase = passPhrase.remove(' ').toLower();
qCDebug(lcCse) << "Passphrase Generated";
auto salt = EncryptionHelper::generateRandom(40);
auto secretKey = EncryptionHelper::generatePassword(passPhrase, salt);
@ -1346,14 +1569,15 @@ void ClientSideEncryption::encryptPrivateKey(const AccountPtr &account)
Q_UNUSED(doc);
switch(retCode) {
case 200:
qCInfo(lcCse()) << "Private key stored encrypted on server.";
writePrivateKey(account);
writeCertificate(account);
writeMnemonic(account);
emit initializationFinished(true);
writeMnemonic(account, [this] () {
emit initializationFinished(true);
});
break;
default:
qCInfo(lcCse()) << "Store private key failed, return code:" << retCode;
qCWarning(lcCse) << "Store private key failed, return code:" << retCode;
failedToInitialize(account);
}
});
job->start();
@ -1362,7 +1586,7 @@ void ClientSideEncryption::encryptPrivateKey(const AccountPtr &account)
void ClientSideEncryption::decryptPrivateKey(const AccountPtr &account, const QByteArray &key) {
if (!account->askUserForMnemonic()) {
qCDebug(lcCse) << "Not allowed to ask user for mnemonic";
emit initializationFinished();
failedToInitialize(account);
return;
}
@ -1386,36 +1610,30 @@ void ClientSideEncryption::decryptPrivateKey(const AccountPtr &account, const QB
}
bool ok = dialog.exec();
if (ok) {
qCInfo(lcCse()) << "Got mnemonic:" << dialog.textValue();
prev = dialog.textValue();
_mnemonic = prev;
QString mnemonic = prev.split(" ").join(QString()).toLower();
qCInfo(lcCse()) << "mnemonic:" << mnemonic;
// split off salt
const auto salt = EncryptionHelper::extractPrivateKeySalt(key);
auto pass = EncryptionHelper::generatePassword(mnemonic, salt);
qCInfo(lcCse()) << "Generated key:" << pass;
QByteArray privateKey = EncryptionHelper::decryptPrivateKey(pass, key);
//_privateKey = QSslKey(privateKey, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
_privateKey = privateKey;
qCInfo(lcCse()) << "Private key: " << _privateKey;
if (!_privateKey.isNull() && checkPublicKeyValidity(account)) {
writePrivateKey(account);
writeCertificate(account);
writeMnemonic(account);
writeMnemonic(account, [] () {});
break;
}
} else {
_mnemonic = QString();
_privateKey = QByteArray();
qCInfo(lcCse()) << "Cancelled";
break;
qCDebug(lcCse()) << "Cancelled";
failedToInitialize(account);
return;
}
}
@ -1424,20 +1642,17 @@ void ClientSideEncryption::decryptPrivateKey(const AccountPtr &account, const QB
void ClientSideEncryption::getPrivateKeyFromServer(const AccountPtr &account)
{
qCInfo(lcCse()) << "Retrieving private key from server";
auto job = new JsonApiJob(account, e2eeBaseUrl() + "private-key", this);
connect(job, &JsonApiJob::jsonReceived, [this, account](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(account, key.toLocal8Bit());
} else if (retCode == 404) {
qCInfo(lcCse()) << "No private key on the server: setup is incomplete.";
qCWarning(lcCse) << "No private key on the server: setup is incomplete.";
emit initializationFinished();
return;
} else {
qCInfo(lcCse()) << "Error while requesting public key: " << retCode;
qCWarning(lcCse) << "Error while requesting public key: " << retCode;
emit initializationFinished();
return;
}
@ -1447,25 +1662,24 @@ void ClientSideEncryption::getPrivateKeyFromServer(const AccountPtr &account)
void ClientSideEncryption::getPublicKeyFromServer(const AccountPtr &account)
{
qCInfo(lcCse()) << "Retrieving public key from server";
auto job = new JsonApiJob(account, e2eeBaseUrl() + "public-key", this);
connect(job, &JsonApiJob::jsonReceived, [this, account](const QJsonDocument& doc, int retCode) {
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()) << "Found Public key, requesting Server Public Key. Public key:" << publicKey;
fetchAndValidatePublicKeyFromServer(account);
} else if (retCode == 404) {
qCInfo(lcCse()) << "No public key on the server";
qCDebug(lcCse()) << "No public key on the server";
if (!account->e2eEncryptionKeysGenerationAllowed()) {
qCInfo(lcCse()) << "User did not allow E2E keys generation.";
emit initializationFinished();
qCDebug(lcCse()) << "User did not allow E2E keys generation.";
failedToInitialize(account);
return;
}
generateKeyPair(account);
} else {
qCInfo(lcCse()) << "Error while requesting public key: " << retCode;
qCWarning(lcCse) << "Error while requesting public key: " << retCode;
failedToInitialize(account);
}
});
job->start();
@ -1473,30 +1687,26 @@ void ClientSideEncryption::getPublicKeyFromServer(const AccountPtr &account)
void ClientSideEncryption::fetchAndValidatePublicKeyFromServer(const AccountPtr &account)
{
qCInfo(lcCse()) << "Retrieving public key from server";
auto job = new JsonApiJob(account, e2eeBaseUrl() + "server-key", this);
connect(job, &JsonApiJob::jsonReceived, [this, account](const QJsonDocument& doc, int retCode) {
if (retCode == 200) {
const auto serverPublicKey = doc.object()["ocs"].toObject()["data"].toObject()["public-key"].toString().toLatin1();
qCInfo(lcCse()) << "Found Server Public key, checking it. Server public key:" << serverPublicKey;
if (checkServerPublicKeyValidity(serverPublicKey)) {
if (_privateKey.isEmpty()) {
qCInfo(lcCse()) << "Valid Server Public key, requesting Private Key.";
getPrivateKeyFromServer(account);
} else {
qCInfo(lcCse()) << "Certificate saved, Encrypting Private Key.";
encryptPrivateKey(account);
}
} else {
qCInfo(lcCse()) << "Error invalid server public key";
_certificate = QSslCertificate();
_publicKey = QSslKey();
_privateKey = QByteArray();
qCWarning(lcCse) << "Error invalid server public key";
forgetSensitiveData(account);
getPublicKeyFromServer(account);
return;
}
} else {
qCInfo(lcCse()) << "Error while requesting server public key: " << retCode;
qCWarning(lcCse) << "Error while requesting server public key: " << retCode;
failedToInitialize(account);
return;
}
});
job->start();
@ -1513,7 +1723,7 @@ FolderMetadata::FolderMetadata(AccountPtr account,
RequiredMetadataVersion requiredMetadataVersion,
const QByteArray& metadata,
int statusCode)
: _account(account)
: _account(std::move(account))
, _requiredMetadataVersion(requiredMetadataVersion)
{
if (metadata.isEmpty() || statusCode == 404) {

View File

@ -135,6 +135,7 @@ signals:
void privateKeyDeleted();
void certificateDeleted();
void mnemonicDeleted();
void publicKeyDeleted();
public slots:
void initialize(const OCC::AccountPtr &account);
@ -144,6 +145,7 @@ private slots:
void generateKeyPair(const OCC::AccountPtr &account);
void encryptPrivateKey(const OCC::AccountPtr &account);
void publicCertificateFetched(QKeychain::Job *incoming);
void publicKeyFetched(QKeychain::Job *incoming);
void privateKeyFetched(QKeychain::Job *incoming);
void mnemonicKeyFetched(QKeychain::Job *incoming);
@ -151,6 +153,7 @@ private slots:
void handlePrivateKeyDeleted(const QKeychain::Job* const incoming);
void handleCertificateDeleted(const QKeychain::Job* const incoming);
void handleMnemonicDeleted(const QKeychain::Job* const incoming);
void handlePublicKeyDeleted(const QKeychain::Job* const incoming);
void checkAllSensitiveDataDeleted();
void getPrivateKeyFromServer(const OCC::AccountPtr &account);
@ -158,19 +161,54 @@ private slots:
void fetchAndValidatePublicKeyFromServer(const OCC::AccountPtr &account);
void decryptPrivateKey(const OCC::AccountPtr &account, const QByteArray &key);
void fetchFromKeyChain(const OCC::AccountPtr &account);
void fetchCertificateFromKeyChain(const OCC::AccountPtr &account);
void fetchPublicKeyFromKeyChain(const OCC::AccountPtr &account);
void writePrivateKey(const OCC::AccountPtr &account);
void writeCertificate(const OCC::AccountPtr &account);
void writeMnemonic(const OCC::AccountPtr &account);
private:
void generateCSR(const AccountPtr &account, PKey keyPair);
void sendSignRequestCSR(const AccountPtr &account, PKey keyPair, const QByteArray &csrContent);
void generateMnemonic();
[[nodiscard]] std::pair<QByteArray, PKey> generateCSR(const AccountPtr &account,
PKey keyPair,
PKey privateKey);
void sendSignRequestCSR(const AccountPtr &account,
PKey keyPair,
const QByteArray &csrContent);
void writeKeyPair(const AccountPtr &account,
PKey keyPair,
const QByteArray &csrContent);
template <typename L>
void writeMnemonic(OCC::AccountPtr account,
L nextCall);
void checkServerHasSavedKeys(const AccountPtr &account);
template <typename SUCCESS_CALLBACK, typename ERROR_CALLBACK>
void checkUserPublicKeyOnServer(const OCC::AccountPtr &account,
SUCCESS_CALLBACK nextCheck,
ERROR_CALLBACK onError);
template <typename SUCCESS_CALLBACK, typename ERROR_CALLBACK>
void checkUserPrivateKeyOnServer(const OCC::AccountPtr &account,
SUCCESS_CALLBACK nextCheck,
ERROR_CALLBACK onError);
template <typename SUCCESS_CALLBACK, typename ERROR_CALLBACK>
void checkUserKeyOnServer(const QString &keyType,
const OCC::AccountPtr &account,
SUCCESS_CALLBACK nextCheck,
ERROR_CALLBACK onError);
[[nodiscard]] bool checkPublicKeyValidity(const AccountPtr &account) const;
[[nodiscard]] bool checkServerPublicKeyValidity(const QByteArray &serverPublicKeyString) const;
[[nodiscard]] bool sensitiveDataRemaining() const;
void failedToInitialize(const AccountPtr &account);
bool isInitialized = false;
};