Skip to content

Commit

Permalink
store all certificates and use the correct one by its sha256 digest
Browse files Browse the repository at this point in the history
Signed-off-by: Matthieu Gallien <[email protected]>
  • Loading branch information
mgallien committed Dec 8, 2023
1 parent 73d9dc0 commit 61de2d6
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 29 deletions.
2 changes: 1 addition & 1 deletion src/libsync/abstractpropagateremotedeleteencrypted.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ void AbstractPropagateRemoteDeleteEncrypted::slotFolderEncryptedIdReceived(const

void AbstractPropagateRemoteDeleteEncrypted::slotTryLock(const QByteArray &folderId)
{
auto lockJob = new LockEncryptFolderApiJob(_propagator->account(), folderId, _propagator->_journal, this);
auto lockJob = new LockEncryptFolderApiJob(_propagator->account(), folderId, _propagator->account()->e2e()->certificateSha256Fingerprint(), _propagator->_journal, this);
connect(lockJob, &LockEncryptFolderApiJob::success, this, &AbstractPropagateRemoteDeleteEncrypted::slotFolderLockedSuccessfully);
connect(lockJob, &LockEncryptFolderApiJob::error, this, &AbstractPropagateRemoteDeleteEncrypted::taskFailed);
lockJob->start();
Expand Down
74 changes: 54 additions & 20 deletions src/libsync/clientsideencryption.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,8 @@ std::optional<QByteArray> encryptStringAsymmetric(const ClientSideEncryption &en
}
}

std::optional<QByteArray> decryptStringAsymmetric(const ClientSideEncryption &encryptionEngine,
std::optional<QByteArray> decryptStringAsymmetric(const QByteArray &certificateSha256Fingerprint,

Check warning on line 727 in src/libsync/clientsideencryption.cpp

View workflow job for this annotation

GitHub Actions / build

/src/libsync/clientsideencryption.cpp:727:27 [modernize-use-trailing-return-type]

use a trailing return type for this function
const ClientSideEncryption &encryptionEngine,
const QByteArray &base64Data)
{
if (!encryptionEngine.isInitialized()) {
Expand All @@ -733,6 +734,11 @@ std::optional<QByteArray> decryptStringAsymmetric(const ClientSideEncryption &en
}

if (encryptionEngine.useTokenBasedEncryption()) {
if (encryptionEngine.getTokenCertificate().sha256Fingerprint() != certificateSha256Fingerprint) {
qCWarning(lcCse()) << "wrong certificate: cannot decrypt what has been encrypted with another certificate:" << certificateSha256Fingerprint << "current certificate" << encryptionEngine.getTokenCertificate().sha256Fingerprint();
return {};
}

const auto decryptBase64Result = internals::decryptStringAsymmetricWithToken(encryptionEngine.sslEngine(),
encryptionEngine.getTokenCertificate().getPrivateKey(),
QByteArray::fromBase64(base64Data));
Expand Down Expand Up @@ -1243,19 +1249,13 @@ void ClientSideEncryption::initializeHardwareTokenEncryption(QWidget *settingsDi
}

const auto result = BIO2ByteArray(out);
const auto sslCertificate = QSslCertificate{result, QSsl::Pem};
auto sslCertificate = QSslCertificate{result, QSsl::Pem};

if (sslCertificate.isSelfSigned()) {
qCDebug(lcCse()) << "newly found certificate is self signed: goint to ignore it";
continue;
}

const auto &sha256Fingerprint = sslCertificate.digest(QCryptographicHash::Sha256).toBase64();
if (sha256Fingerprint != _usbTokenInformation.sha256Fingerprint()) {
qCInfo(lcCse()) << "skipping certificate from" << sslCertificate.subjectDisplayName() << "with fingerprint" << sha256Fingerprint << "different from" << _usbTokenInformation.sha256Fingerprint();
continue;
}

const auto certificateKey = PKCS11_find_key(currentCertificate);
if (!certificateKey) {
qCWarning(lcCse()) << "PKCS11_find_key failed" << ERR_reason_error_string(ERR_get_error());
Expand All @@ -1264,20 +1264,39 @@ void ClientSideEncryption::initializeHardwareTokenEncryption(QWidget *settingsDi
return;
}

setEncryptionCertificate({certificateKey, certificateKey, sslCertificate});
_otherCertificates.emplace_back(certificateKey, certificateKey, std::move(sslCertificate));
}
}

if (canEncrypt() && !checkEncryptionIsWorking()) {
qCWarning(lcCse()) << "encryption is not properly setup";
for (const auto &oneCertificateInformation : _otherCertificates) {
if (oneCertificateInformation.isSelfSigned()) {
qCDebug(lcCse()) << "newly found certificate is self signed: goint to ignore it";
continue;
}

failedToInitialize(account);
return;
}
if (oneCertificateInformation.sha256Fingerprint() != _usbTokenInformation.sha256Fingerprint()) {
qCInfo(lcCse()) << "skipping certificate from" << "with fingerprint" << oneCertificateInformation.sha256Fingerprint() << "different from" << _usbTokenInformation.sha256Fingerprint();
continue;
}

saveCertificateIdentification(account);
const auto &sslErrors = oneCertificateInformation.verify();
for (const auto &sslError : sslErrors) {
qCInfo(lcCse()) << "certificate validation error" << sslError;
}

emit initializationFinished();
setEncryptionCertificate(oneCertificateInformation);

if (canEncrypt() && !checkEncryptionIsWorking()) {
qCWarning(lcCse()) << "encryption is not properly setup";

failedToInitialize(account);
return;
}

saveCertificateIdentification(account);

emit initializationFinished();
return;
}

failedToInitialize(account);
Expand Down Expand Up @@ -1335,7 +1354,7 @@ bool ClientSideEncryption::checkPublicKeyValidity(const AccountPtr &account) con
BIO_write(privateKeyBio, privateKeyPem.constData(), privateKeyPem.size());
auto key = PKey::readPrivateKey(privateKeyBio);

const auto decryptionResult = EncryptionHelper::decryptStringAsymmetric(*account->e2e(), *encryptedData);
const auto decryptionResult = EncryptionHelper::decryptStringAsymmetric(account->e2e()->certificateSha256Fingerprint(), *account->e2e(), *encryptedData);
if (!decryptionResult) {
qCWarning(lcCse()) << "encryption error";
return false;
Expand All @@ -1360,7 +1379,7 @@ bool ClientSideEncryption::checkEncryptionIsWorking() const
return false;
}

const auto decryptionResult = EncryptionHelper::decryptStringAsymmetric(*this, *encryptedData);
const auto decryptionResult = EncryptionHelper::decryptStringAsymmetric(certificateSha256Fingerprint(), *this, *encryptedData);
if (!decryptionResult) {
qCWarning(lcCse()) << "encryption error";
return false;
Expand Down Expand Up @@ -2385,7 +2404,7 @@ std::optional<QByteArray> FolderMetadata::encryptData(const QByteArray& binaryDa

std::optional<QByteArray> FolderMetadata::decryptData(const QByteArray &base64Data) const
{
const auto decryptBase64Result = EncryptionHelper::decryptStringAsymmetric(*_account->e2e(), base64Data);
const auto decryptBase64Result = EncryptionHelper::decryptStringAsymmetric(certificateSha256Fingerprint(), *_account->e2e(), base64Data);

if (!decryptBase64Result || decryptBase64Result->isEmpty())
{
Expand Down Expand Up @@ -2619,6 +2638,11 @@ QJsonObject FolderMetadata::fileDrop() const
return _fileDropFromServer;
}

QByteArray FolderMetadata::certificateSha256Fingerprint() const
{
return _metadataCertificateSha256Fingerprint;
}

bool EncryptionHelper::fileEncryption(const QByteArray &key, const QByteArray &iv, QFile *input, QFile *output, QByteArray& returnTag)
{
if (!input->open(QIODevice::ReadOnly)) {
Expand Down Expand Up @@ -2960,7 +2984,7 @@ CertificateInformation::CertificateInformation()

CertificateInformation::CertificateInformation(PKCS11_KEY *publicKey,
PKCS11_KEY *privateKey,
QSslCertificate certificate)
QSslCertificate &&certificate)
: _publicKey(publicKey)
, _privateKey(privateKey)
, _certificate(std::move(certificate))
Expand Down Expand Up @@ -2989,6 +3013,16 @@ void CertificateInformation::clear()
_certificateInvalid = true;
}

QList<QSslError> CertificateInformation::verify() const
{
return QSslCertificate::verify({_certificate});
}

bool CertificateInformation::isSelfSigned() const
{
return _certificate.isSelfSigned();
}

PKCS11_KEY *CertificateInformation::getPublicKey() const
{
return _publicKey;
Expand Down
12 changes: 10 additions & 2 deletions src/libsync/clientsideencryption.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,16 @@ class CertificateInformation {

CertificateInformation(PKCS11_KEY *publicKey,
PKCS11_KEY *privateKey,
QSslCertificate certificate);
QSslCertificate &&certificate);

[[nodiscard]] bool operator==(const CertificateInformation &other) const;

void clear();

[[nodiscard]] QList<QSslError> verify() const;

[[nodiscard]] bool isSelfSigned() const;

[[nodiscard]] PKCS11_KEY* getPublicKey() const;

[[nodiscard]] PKCS11_KEY* getPrivateKey() const;
Expand Down Expand Up @@ -120,7 +124,8 @@ OWNCLOUDSYNC_EXPORT QByteArray decryptStringSymmetric(
[[nodiscard]] OWNCLOUDSYNC_EXPORT std::optional<QByteArray> encryptStringAsymmetric(const ClientSideEncryption &encryptionEngine,
const QByteArray &binaryData);

[[nodiscard]] OWNCLOUDSYNC_EXPORT std::optional<QByteArray> decryptStringAsymmetric(const ClientSideEncryption &encryptionEngine,
[[nodiscard]] OWNCLOUDSYNC_EXPORT std::optional<QByteArray> decryptStringAsymmetric(const QByteArray &certificateSha256Fingerprint,
const ClientSideEncryption &encryptionEngine,
const QByteArray &base64Data);

QByteArray privateKeyToPem(const QByteArray key);
Expand Down Expand Up @@ -334,6 +339,7 @@ private slots:
ClientSideEncryptionTokenSelector _usbTokenInformation;

CertificateInformation _encryptionCertificate;
std::vector<CertificateInformation> _otherCertificates;
};

/* Generates the Metadata for the folder */
Expand Down Expand Up @@ -375,6 +381,8 @@ class OWNCLOUDSYNC_EXPORT FolderMetadata {

[[nodiscard]] QJsonObject fileDrop() const;

[[nodiscard]] QByteArray certificateSha256Fingerprint() const;

private:
/* Use std::string and std::vector internally on this class
* to ease the port to Nlohmann Json API
Expand Down
4 changes: 3 additions & 1 deletion src/libsync/clientsideencryptionjobs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,12 @@ bool DeleteMetadataApiJob::finished()

LockEncryptFolderApiJob::LockEncryptFolderApiJob(const AccountPtr &account,
const QByteArray &fileId,
const QByteArray &certificateSha256Fingerprint,
SyncJournalDb *journalDb,
QObject *parent)
: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("lock/") + fileId, parent)
, _fileId(fileId)
, _certificateSha256Fingerprint(certificateSha256Fingerprint)
, _journalDb(journalDb)
{
}
Expand All @@ -251,7 +253,7 @@ void LockEncryptFolderApiJob::start()

if (!folderTokenEncrypted.isEmpty()) {
qCInfo(lcCseJob()) << "lock folder started for:" << path() << " for fileId: " << _fileId << " but we need to first lift the previous lock";
const auto folderToken = EncryptionHelper::decryptStringAsymmetric(*_account->e2e(), folderTokenEncrypted);
const auto folderToken = EncryptionHelper::decryptStringAsymmetric(_certificateSha256Fingerprint, *_account->e2e(), folderTokenEncrypted);
if (!folderToken) {
qCWarning(lcCseJob()) << "decrypt failed";
return;
Expand Down
7 changes: 6 additions & 1 deletion src/libsync/clientsideencryptionjobs.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,11 @@ class OWNCLOUDSYNC_EXPORT LockEncryptFolderApiJob : public AbstractNetworkJob
{
Q_OBJECT
public:
explicit LockEncryptFolderApiJob(const AccountPtr &account, const QByteArray &fileId, SyncJournalDb *journalDb, QObject *parent = nullptr);
explicit LockEncryptFolderApiJob(const AccountPtr &account,
const QByteArray &fileId,
const QByteArray &certificateSha256Fingerprint,
SyncJournalDb *journalDb,
QObject *parent = nullptr);

public slots:
void start() override;
Expand All @@ -160,6 +164,7 @@ public slots:

private:
QByteArray _fileId;
QByteArray _certificateSha256Fingerprint;
QPointer<SyncJournalDb> _journalDb;
};

Expand Down
2 changes: 1 addition & 1 deletion src/libsync/encryptfolderjob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ void EncryptFolderJob::slotEncryptionFlagSuccess(const QByteArray &fileId)
qCWarning(lcEncryptFolderJob) << "Error when setting the file record to the database" << rec._path << result.error();
}

const auto lockJob = new LockEncryptFolderApiJob(_account, fileId, _journal, this);
const auto lockJob = new LockEncryptFolderApiJob(_account, fileId, _account->e2e()->certificateSha256Fingerprint(), _journal, this);
connect(lockJob, &LockEncryptFolderApiJob::success,
this, &EncryptFolderJob::slotLockForEncryptionSuccess);
connect(lockJob, &LockEncryptFolderApiJob::error,
Expand Down
2 changes: 1 addition & 1 deletion src/libsync/propagateuploadencrypted.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ void PropagateUploadEncrypted::slotFolderEncryptedIdReceived(const QStringList &

void PropagateUploadEncrypted::slotTryLock(const QByteArray& fileId)
{
const auto lockJob = new LockEncryptFolderApiJob(_propagator->account(), fileId, _propagator->_journal, this);
const auto lockJob = new LockEncryptFolderApiJob(_propagator->account(), fileId, _propagator->account()->e2e()->certificateSha256Fingerprint(), _propagator->_journal, this);
connect(lockJob, &LockEncryptFolderApiJob::success, this, &PropagateUploadEncrypted::slotFolderLockedSuccessfully);
connect(lockJob, &LockEncryptFolderApiJob::error, this, &PropagateUploadEncrypted::slotFolderLockedError);
lockJob->start();
Expand Down
2 changes: 1 addition & 1 deletion src/libsync/syncengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@ void SyncEngine::startSync()
for (const auto &e2EeLockedFolder : e2EeLockedFolders) {
const auto folderId = e2EeLockedFolder.first;
qCInfo(lcEngine()) << "start unlock job for folderId:" << folderId;
const auto folderToken = EncryptionHelper::decryptStringAsymmetric(*_account->e2e(), e2EeLockedFolder.second);
const auto folderToken = EncryptionHelper::decryptStringAsymmetric(_account->e2e()->certificateSha256Fingerprint(), *_account->e2e(), e2EeLockedFolder.second);
if (!folderToken) {
qCWarning(lcEngine()) << "decrypt failed";
return;
Expand Down
2 changes: 1 addition & 1 deletion src/libsync/updatefiledropmetadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ void UpdateFileDropMetadataJob::slotFolderEncryptedIdReceived(const QStringList

void UpdateFileDropMetadataJob::slotTryLock(const QByteArray &fileId)
{
const auto lockJob = new LockEncryptFolderApiJob(propagator()->account(), fileId, propagator()->_journal, this);
const auto lockJob = new LockEncryptFolderApiJob(propagator()->account(), fileId, propagator()->account()->e2e()->certificateSha256Fingerprint(), propagator()->_journal, this);
connect(lockJob, &LockEncryptFolderApiJob::success, this, &UpdateFileDropMetadataJob::slotFolderLockedSuccessfully);
connect(lockJob, &LockEncryptFolderApiJob::error, this, &UpdateFileDropMetadataJob::slotFolderLockedError);
lockJob->start();
Expand Down

0 comments on commit 61de2d6

Please sign in to comment.