From fb7a5d15aa1409fe54ffde6561b0900f28669937 Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Wed, 29 Jan 2025 14:13:16 +0100 Subject: [PATCH] partial fix for broken automated tests will use different validation method for hardware stored certificates and pure software certificates emited by the nextcloud server Signed-off-by: Matthieu Gallien --- src/libsync/clientsideencryption.cpp | 44 +++++++++++++++---- src/libsync/clientsideencryption.h | 12 ++++- src/libsync/foldermetadata.cpp | 40 ++++++++++++++--- src/libsync/foldermetadata.h | 8 +++- .../updatee2eefolderusersmetadatajob.cpp | 5 ++- test/testclientsideencryptionv2.cpp | 4 +- 6 files changed, 94 insertions(+), 19 deletions(-) diff --git a/src/libsync/clientsideencryption.cpp b/src/libsync/clientsideencryption.cpp index a11789407f6a9..959a9c02471e5 100644 --- a/src/libsync/clientsideencryption.cpp +++ b/src/libsync/clientsideencryption.cpp @@ -940,7 +940,9 @@ const QString &ClientSideEncryption::getMnemonic() const void ClientSideEncryption::setCertificate(const QSslCertificate &certificate) { - _encryptionCertificate = CertificateInformation{_encryptionCertificate.getPrivateKeyData(), QSslCertificate{certificate}}; + _encryptionCertificate = CertificateInformation{useTokenBasedEncryption() ? CertificateInformation::CertificateType::HardwareCertificate : CertificateInformation::CertificateType::SoftwareNextcloudCertificate, + _encryptionCertificate.getPrivateKeyData(), + QSslCertificate{certificate}}; } const QSslCertificate& ClientSideEncryption::getCertificate() const @@ -1414,7 +1416,9 @@ void ClientSideEncryption::publicCertificateFetched(Job *incoming) return; } - _encryptionCertificate = CertificateInformation{_encryptionCertificate.getPrivateKeyData(), QSslCertificate{readJob->binaryData(), QSsl::Pem}}; + _encryptionCertificate = CertificateInformation{useTokenBasedEncryption() ? CertificateInformation::CertificateType::HardwareCertificate : CertificateInformation::CertificateType::SoftwareNextcloudCertificate, + _encryptionCertificate.getPrivateKeyData(), + QSslCertificate{readJob->binaryData(), QSsl::Pem}}; if (_encryptionCertificate.getCertificate().isNull()) { fetchPublicKeyFromKeyChain(account); @@ -2045,7 +2049,9 @@ void ClientSideEncryption::sendSignRequestCSR(const AccountPtr &account, connect(job, &SignPublicKeyApiJob::jsonReceived, job, [this, account, keyPair = std::move(keyPair)](const QJsonDocument& json, const int retCode) { if (retCode == 200) { const auto cert = json.object().value("ocs").toObject().value("data").toObject().value("public-key").toString(); - _encryptionCertificate = CertificateInformation{_encryptionCertificate.getPrivateKeyData(), QSslCertificate{cert.toLocal8Bit(), QSsl::Pem}}; + _encryptionCertificate = CertificateInformation{useTokenBasedEncryption() ? CertificateInformation::CertificateType::HardwareCertificate : CertificateInformation::CertificateType::SoftwareNextcloudCertificate, + _encryptionCertificate.getPrivateKeyData(), + QSslCertificate{cert.toLocal8Bit(), QSsl::Pem}}; Bio certificateBio; const auto certificatePem = _encryptionCertificate.getCertificate().toPem(); BIO_write(certificateBio, certificatePem.constData(), certificatePem.size()); @@ -2331,7 +2337,9 @@ void ClientSideEncryption::getPublicKeyFromServer(const AccountPtr &account) 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(); - _encryptionCertificate = CertificateInformation{_encryptionCertificate.getPrivateKeyData(), QSslCertificate{publicKey.toLocal8Bit(), QSsl::Pem}}; + _encryptionCertificate = CertificateInformation{useTokenBasedEncryption() ? CertificateInformation::CertificateType::HardwareCertificate : CertificateInformation::CertificateType::SoftwareNextcloudCertificate, + _encryptionCertificate.getPrivateKeyData(), + QSslCertificate{publicKey.toLocal8Bit(), QSsl::Pem}}; fetchAndValidatePublicKeyFromServer(account); } else if (retCode == 404) { qCDebug(lcCse()) << "No public key on the server"; @@ -3005,22 +3013,34 @@ CertificateInformation::CertificateInformation() CertificateInformation::CertificateInformation(PKCS11_KEY *hardwarePrivateKey, QSslCertificate &&certificate) - : _hardwarePrivateKey(hardwarePrivateKey) - , _certificate(std::move(certificate)) + : _hardwarePrivateKey{hardwarePrivateKey} + , _certificate{std::move(certificate)} + , _certificateType{CertificateType::HardwareCertificate} { checkEncryptionCertificate(); } -CertificateInformation::CertificateInformation(const QByteArray &privateKey, QSslCertificate &&certificate) +CertificateInformation::CertificateInformation(CertificateType certificateType, + const QByteArray &privateKey, + QSslCertificate &&certificate) : _hardwarePrivateKey() , _privateKeyData() , _certificate(std::move(certificate)) + , _certificateType{certificateType} { if (!privateKey.isEmpty()) { setPrivateKeyData(privateKey); } - checkEncryptionCertificate(); + switch (_certificateType) + { + case CertificateType::HardwareCertificate: + checkEncryptionCertificate(); + break; + case CertificateType::SoftwareNextcloudCertificate: + doNotCheckEncryptionCertificate(); + break; + } } bool CertificateInformation::operator==(const CertificateInformation &other) const @@ -3211,4 +3231,12 @@ void CertificateInformation::checkEncryptionCertificate() } } +void CertificateInformation::doNotCheckEncryptionCertificate() +{ + _certificateExpired = false; + _certificateNotYetValid = false; + _certificateRevoked = false; + _certificateInvalid = false; +} + } diff --git a/src/libsync/clientsideencryption.h b/src/libsync/clientsideencryption.h index acc6d95d16bf3..884ac8d4bb192 100644 --- a/src/libsync/clientsideencryption.h +++ b/src/libsync/clientsideencryption.h @@ -61,12 +61,18 @@ class ClientSideEncryption; class CertificateInformation { public: + enum class CertificateType { + SoftwareNextcloudCertificate, + HardwareCertificate, + }; + CertificateInformation(); explicit CertificateInformation(PKCS11_KEY *hardwarePrivateKey, QSslCertificate &&certificate); - explicit CertificateInformation(const QByteArray& privateKey, + explicit CertificateInformation(CertificateType certificateType, + const QByteArray& privateKey, QSslCertificate &&certificate); [[nodiscard]] bool operator==(const CertificateInformation &other) const; @@ -104,12 +110,16 @@ class CertificateInformation { private: void checkEncryptionCertificate(); + void doNotCheckEncryptionCertificate(); + PKCS11_KEY* _hardwarePrivateKey = nullptr; QByteArray _privateKeyData; QSslCertificate _certificate; + CertificateType _certificateType = CertificateType::SoftwareNextcloudCertificate; + bool _certificateExpired = true; bool _certificateNotYetValid = true; diff --git a/src/libsync/foldermetadata.cpp b/src/libsync/foldermetadata.cpp index c6d3a505bf2fa..71128382ce0a7 100644 --- a/src/libsync/foldermetadata.cpp +++ b/src/libsync/foldermetadata.cpp @@ -434,8 +434,16 @@ QByteArray FolderMetadata::encryptDataWithPublicKey(const QByteArray &binaryData const CertificateInformation &shareUserCertificate) const { const auto encryptBase64Result = EncryptionHelper::encryptStringAsymmetric(shareUserCertificate, _account->e2e()->paddingMode(), *_account->e2e(), binaryData); - qCDebug(lcCseMetadata()) << "encryptDataWithPublicKey" << binaryData.toBase64() << *encryptBase64Result; - return *encryptBase64Result; + + if (encryptBase64Result) { + qCDebug(lcCseMetadata()) << "encryptDataWithPublicKey" << binaryData.toBase64() << *encryptBase64Result; + return *encryptBase64Result; + } else { + qCWarning(lcCseMetadata()) << "fail to encryptDataWithPublicKey" << binaryData.toBase64(); + _account->reportClientStatus(OCC::ClientStatusReportingStatus::E2EeError_GeneralError); + return {}; + } + return {}; } QByteArray FolderMetadata::decryptDataWithPrivateKey(const QByteArray &base64Data, @@ -551,8 +559,12 @@ void FolderMetadata::initEmptyMetadata() return initEmptyMetadataLegacy(); } qCDebug(lcCseMetadata()) << "Setting up empty metadata v2"; + + const auto certificateType = _account->e2e()->useTokenBasedEncryption() ? + FolderMetadata::CertificateType::HardwareCertificate : FolderMetadata::CertificateType::SoftwareNextcloudCertificate; + if (_isRootEncryptedFolder) { - if (!addUser(_account->davUser(), _account->e2e()->getCertificate())) { + if (!addUser(_account->davUser(), _account->e2e()->getCertificate(), certificateType)) { qCDebug(lcCseMetadata) << "Empty metadata setup failed. Could not add first user."; _account->reportClientStatus(OCC::ClientStatusReportingStatus::E2EeError_GeneralError); return; @@ -1037,7 +1049,9 @@ void FolderMetadata::slotRootE2eeFolderMetadataReceived(int statusCode, const QS initMetadata(); } -bool FolderMetadata::addUser(const QString &userId, const QSslCertificate &certificate) +bool FolderMetadata::addUser(const QString &userId, + const QSslCertificate &certificate, + CertificateType certificateType) { Q_ASSERT(_isRootEncryptedFolder); Q_ASSERT(!certificate.isNull()); @@ -1046,9 +1060,23 @@ bool FolderMetadata::addUser(const QString &userId, const QSslCertificate &certi return false; } - const auto shareUserCertificate = CertificateInformation{{}, QSslCertificate{certificate}}; + auto convertedCertificateType = CertificateInformation::CertificateType::HardwareCertificate; + switch (certificateType) + { + case CertificateType::HardwareCertificate: + convertedCertificateType = CertificateInformation::CertificateType::HardwareCertificate; + break; + case CertificateType::SoftwareNextcloudCertificate: + convertedCertificateType = CertificateInformation::CertificateType::SoftwareNextcloudCertificate; + break; + } + + const auto shareUserCertificate = CertificateInformation{convertedCertificateType, {}, QSslCertificate{certificate}}; if (userId.isEmpty() || certificate.isNull() || !shareUserCertificate.canEncrypt()) { - qCWarning(lcCseMetadata()) << "Could not add a folder user. Invalid userId or certificate."; + qCWarning(lcCseMetadata()) << "Could not add a folder user. Invalid userId or certificate." + << userId + << (certificate.isNull() ? "user certificate is invalid" : "user certificate is valid") + << (shareUserCertificate.canEncrypt() ? "certificate of share receiver user can encrypt" : "certificate of share receiver user cannot encrypt"); return false; } diff --git a/src/libsync/foldermetadata.h b/src/libsync/foldermetadata.h index 34fda88e86e54..0fee917710e78 100644 --- a/src/libsync/foldermetadata.h +++ b/src/libsync/foldermetadata.h @@ -93,6 +93,12 @@ class OWNCLOUDSYNC_EXPORT FolderMetadata : public QObject }; Q_ENUM(FolderType) + enum class CertificateType { + SoftwareNextcloudCertificate, + HardwareCertificate, + }; + Q_ENUM(CertificateType) + FolderMetadata(AccountPtr account, const QString &remoteFolderRoot, FolderType folderType = FolderType::Nested); /* * construct metadata based on RootEncryptedFolderInfo @@ -121,7 +127,7 @@ class OWNCLOUDSYNC_EXPORT FolderMetadata : public QObject [[nodiscard]] bool moveFromFileDropToFiles(); // adds a user to have access to this folder (always generates new metadata key) - [[nodiscard]] bool addUser(const QString &userId, const QSslCertificate &certificate); + [[nodiscard]] bool addUser(const QString &userId, const QSslCertificate &certificate, CertificateType certificateType); // removes a user from this folder and removes and generates a new metadata key [[nodiscard]] bool removeUser(const QString &userId); diff --git a/src/libsync/updatee2eefolderusersmetadatajob.cpp b/src/libsync/updatee2eefolderusersmetadatajob.cpp index c6bfc07c049b9..065ace9bc28f6 100644 --- a/src/libsync/updatee2eefolderusersmetadatajob.cpp +++ b/src/libsync/updatee2eefolderusersmetadatajob.cpp @@ -136,8 +136,11 @@ void UpdateE2eeFolderUsersMetadataJob::startUpdate() return; } + const auto certificateType = _account->e2e()->useTokenBasedEncryption() ? + FolderMetadata::CertificateType::HardwareCertificate : FolderMetadata::CertificateType::SoftwareNextcloudCertificate; + const auto result = _operation == Operation::Add - ? _encryptedFolderMetadataHandler->folderMetadata()->addUser(_folderUserId, _folderUserCertificate) + ? _encryptedFolderMetadataHandler->folderMetadata()->addUser(_folderUserId, _folderUserCertificate, certificateType) : _encryptedFolderMetadataHandler->folderMetadata()->removeUser(_folderUserId); if (!result) { diff --git a/test/testclientsideencryptionv2.cpp b/test/testclientsideencryptionv2.cpp index 96ef34ddb3559..30b98e09fb01d 100644 --- a/test/testclientsideencryptionv2.cpp +++ b/test/testclientsideencryptionv2.cpp @@ -245,11 +245,11 @@ private slots: encryptedFile.initializationVector = EncryptionHelper::generateRandom(16); metadata->addEncryptedFile(encryptedFile); - QVERIFY(metadata->addUser(_secondAccount->davUser(), _secondAccount->e2e()->getCertificate())); + QVERIFY(metadata->addUser(_secondAccount->davUser(), _secondAccount->e2e()->getCertificate(), FolderMetadata::CertificateType::SoftwareNextcloudCertificate)); QVERIFY(metadata->removeUser(_secondAccount->davUser())); - QVERIFY(metadata->addUser(_secondAccount->davUser(), _secondAccount->e2e()->getCertificate())); + QVERIFY(metadata->addUser(_secondAccount->davUser(), _secondAccount->e2e()->getCertificate(), FolderMetadata::CertificateType::SoftwareNextcloudCertificate)); const auto encryptedMetadata = metadata->encryptedMetadata(); QVERIFY(!encryptedMetadata.isEmpty());