From e187be5fa02f201dc956b6208a796ff50c5b3deb Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Thu, 30 Nov 2023 23:19:09 +0100 Subject: [PATCH] select the certificate to use by its sha256 fingerprint Signed-off-by: Matthieu Gallien --- src/gui/accountmanager.cpp | 9 ++--- src/libsync/account.cpp | 31 ++++----------- src/libsync/account.h | 18 +++------ src/libsync/clientsideencryption.cpp | 35 ++++++----------- src/libsync/clientsideencryption.h | 3 +- .../clientsideencryptiontokenselector.cpp | 38 ++++++------------- .../clientsideencryptiontokenselector.h | 20 +++------- 7 files changed, 44 insertions(+), 110 deletions(-) diff --git a/src/gui/accountmanager.cpp b/src/gui/accountmanager.cpp index 731bb164dc233..2c06cefc59851 100644 --- a/src/gui/accountmanager.cpp +++ b/src/gui/accountmanager.cpp @@ -46,8 +46,7 @@ constexpr auto serverVersionC = "serverVersion"; constexpr auto serverColorC = "serverColor"; constexpr auto serverTextColorC = "serverTextColor"; constexpr auto skipE2eeMetadataChecksumValidationC = "skipE2eeMetadataChecksumValidation"; -constexpr auto encryptionCertificateSerialNumberC = "encryptionCertificateSerialNumber"; -constexpr auto encryptionCertificateIssuerC = "encryptionCertificateIssuer"; +constexpr auto encryptionCertificateSha256FingerprintC = "encryptionCertificateSha256Fingerprint"; constexpr auto generalC = "General"; constexpr auto dummyAuthTypeC = "dummy"; @@ -318,8 +317,7 @@ void AccountManager::saveAccountHelper(Account *acc, QSettings &settings, bool s settings.setValue(QLatin1String(serverVersionC), acc->_serverVersion); settings.setValue(QLatin1String(serverColorC), acc->_serverColor); settings.setValue(QLatin1String(serverTextColorC), acc->_serverTextColor); - settings.setValue(QLatin1String(encryptionCertificateSerialNumberC), acc->encryptionCertificateSerialNumber()); - settings.setValue(QLatin1String(encryptionCertificateIssuerC), acc->encryptionCertificateIssuer()); + settings.setValue(QLatin1String(encryptionCertificateSha256FingerprintC), acc->encryptionCertificateFingerprint()); if (!acc->_skipE2eeMetadataChecksumValidation) { settings.remove(QLatin1String(skipE2eeMetadataChecksumValidationC)); } else { @@ -446,8 +444,7 @@ AccountPtr AccountManager::loadAccountHelper(QSettings &settings) acc->setCredentials(CredentialsFactory::create(authType)); - acc->setEncryptionCertificateSerialNumber(settings.value(QLatin1String(encryptionCertificateSerialNumberC)).toString()); - acc->setEncryptionCertificateIssuer(settings.value(QLatin1String(encryptionCertificateIssuerC)).toString()); + acc->setEncryptionCertificateFingerprint(settings.value(QLatin1String(encryptionCertificateSha256FingerprintC)).toByteArray()); // now the server cert, it is in the general group settings.beginGroup(QLatin1String(generalC)); diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index b2e84c124676f..2e26348b9a26a 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -1035,37 +1035,20 @@ QString Account::encryptionHardwareTokenDriverPath() const return {}; } -QString Account::encryptionCertificateSerialNumber() const +QByteArray Account::encryptionCertificateFingerprint() const { - return _encryptionCertificateSerialNumber; + return _encryptionCertificateFingerprint; } -void Account::setEncryptionCertificateSerialNumber(const QString &serialNumber) +void Account::setEncryptionCertificateFingerprint(const QByteArray &fingerprint) { - if (_encryptionCertificateSerialNumber == serialNumber) { + if (_encryptionCertificateFingerprint == fingerprint) { return; } - _encryptionCertificateSerialNumber = serialNumber; - _e2e.usbTokenInformation()->setSerialNumber(serialNumber); - Q_EMIT encryptionCertificateSerialNumberChanged(); - Q_EMIT wantsAccountSaved(this); -} - -QString Account::encryptionCertificateIssuer() const -{ - return _encryptionCertificateIssuer; -} - -void Account::setEncryptionCertificateIssuer(const QString &issuer) -{ - if (_encryptionCertificateIssuer == issuer) { - return; - } - - _encryptionCertificateIssuer = issuer; - _e2e.usbTokenInformation()->setIssuer(issuer); - Q_EMIT encryptionCertificateIssuerChanged(); + _encryptionCertificateFingerprint = fingerprint; + _e2e.usbTokenInformation()->setSha256Fingerprint(fingerprint); + Q_EMIT encryptionCertificateFingerprintChanged(); Q_EMIT wantsAccountSaved(this); } diff --git a/src/libsync/account.h b/src/libsync/account.h index 6cff8d79cda35..d80d2cde04eae 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -90,8 +90,7 @@ class OWNCLOUDSYNC_EXPORT Account : public QObject Q_PROPERTY(bool askUserForMnemonic READ askUserForMnemonic WRITE setAskUserForMnemonic NOTIFY askUserForMnemonicChanged) Q_PROPERTY(bool enforceUseHardwareTokenEncryption READ enforceUseHardwareTokenEncryption NOTIFY enforceUseHardwareTokenEncryptionChanged) Q_PROPERTY(QString encryptionHardwareTokenDriverPath READ encryptionHardwareTokenDriverPath NOTIFY encryptionHardwareTokenDriverPathChanged) - Q_PROPERTY(QString encryptionCertificateSerialNumber READ encryptionCertificateSerialNumber WRITE setEncryptionCertificateSerialNumber NOTIFY encryptionCertificateSerialNumberChanged) - Q_PROPERTY(QString encryptionCertificateIssuer READ encryptionCertificateIssuer WRITE setEncryptionCertificateIssuer NOTIFY encryptionCertificateIssuerChanged) + Q_PROPERTY(QByteArray encryptionCertificateFingerprint READ encryptionCertificateFingerprint WRITE setEncryptionCertificateFingerprint NOTIFY encryptionCertificateFingerprintChanged) public: static AccountPtr create(); @@ -334,13 +333,9 @@ class OWNCLOUDSYNC_EXPORT Account : public QObject [[nodiscard]] QString encryptionHardwareTokenDriverPath() const; - [[nodiscard]] QString encryptionCertificateSerialNumber() const; + [[nodiscard]] QByteArray encryptionCertificateFingerprint() const; - void setEncryptionCertificateSerialNumber(const QString &serialNumber); - - [[nodiscard]] QString encryptionCertificateIssuer() const; - - void setEncryptionCertificateIssuer(const QString &issuer); + void setEncryptionCertificateFingerprint(const QByteArray &fingerprint); public slots: /// Used when forgetting credentials @@ -388,8 +383,7 @@ public slots: void lockFileSuccess(); void lockFileError(const QString&); - void encryptionCertificateSerialNumberChanged(); - void encryptionCertificateIssuerChanged(); + void encryptionCertificateFingerprintChanged(); protected Q_SLOTS: void slotCredentialsFetched(); @@ -464,9 +458,7 @@ private slots: QHash> _lockStatusChangeInprogress; - QString _encryptionCertificateSerialNumber; - - QString _encryptionCertificateIssuer; + QByteArray _encryptionCertificateFingerprint; /* IMPORTANT - remove later - FIXME MS@2019-12-07 --> * TODO: For "Log out" & "Remove account": Remove client CA certs and KEY! diff --git a/src/libsync/clientsideencryption.cpp b/src/libsync/clientsideencryption.cpp index 5c2b55823ad59..632028d72ad13 100644 --- a/src/libsync/clientsideencryption.cpp +++ b/src/libsync/clientsideencryption.cpp @@ -90,8 +90,7 @@ constexpr char e2e_public[] = "_e2e-public"; constexpr char e2e_mnemonic[] = "_e2e-mnemonic"; constexpr auto metadataKeyJsonKey = "metadataKey"; -constexpr auto certificateSerialNumberKey = "certificateSerialNumber"; -constexpr auto certificateIssuerKey = "certificateIssuer"; +constexpr auto certificateSha256FingerprintKey = "certificateSha256Fingerprint"; constexpr qint64 blockSize = 1024; @@ -1206,11 +1205,6 @@ void ClientSideEncryption::initializeHardwareTokenEncryption(QWidget *settingsDi for (auto certificateIndex = 0u; certificateIndex < keysCount; ++certificateIndex) { const auto currentCertificate = &certificatesFromToken[certificateIndex]; - qCInfo(lcCse()) << "certificate metadata:" - << "label:" << currentCertificate->label; - - const auto certificateId = QByteArray{reinterpret_cast(currentCertificate->id), static_cast(currentCertificate->id_len)}; - qCInfo(lcCse()) << "new certificate ID:" << certificateId.toBase64(); Bio out; const auto ret = PEM_write_bio_X509(out, currentCertificate->x509); @@ -1223,9 +1217,8 @@ void ClientSideEncryption::initializeHardwareTokenEncryption(QWidget *settingsDi const auto result = BIO2ByteArray(out); const auto sslCertificate = QSslCertificate{result, QSsl::Pem}; - if (sslCertificate.issuerDisplayName() != _usbTokenInformation.issuer() || - sslCertificate.serialNumber() != _usbTokenInformation.serialNumber()) { - qCInfo(lcCse()) << "skipping certificate from" << sslCertificate.issuerDisplayName() << "with serial number" << sslCertificate.serialNumber(); + if (sslCertificate.digest(QCryptographicHash::Sha256).toBase64() != _usbTokenInformation.sha256Fingerprint()) { + qCInfo(lcCse()) << "skipping certificate from" << sslCertificate.subjectDisplayName() << "with fingerprint" << sslCertificate.digest(QCryptographicHash::Sha256).toBase64(); continue; } @@ -1615,10 +1608,8 @@ void ClientSideEncryption::forgetSensitiveData(const AccountPtr &account) deletePrivateKeyJob->start(); deleteCertJob->start(); deleteMnemonicJob->start(); - _usbTokenInformation.setSerialNumber({}); - _usbTokenInformation.setIssuer({}); - account->setEncryptionCertificateSerialNumber({}); - account->setEncryptionCertificateIssuer({}); + _usbTokenInformation.setSha256Fingerprint({}); + account->setEncryptionCertificateFingerprint({}); _tokenPublicKey = nullptr; _tokenPrivateKey = nullptr; } @@ -1680,7 +1671,7 @@ void ClientSideEncryption::handlePublicKeyDeleted(const QKeychain::Job * const i bool ClientSideEncryption::sensitiveDataRemaining() const { - return !_privateKey.isEmpty() || !_certificate.isNull() || !_mnemonic.isEmpty() || !_usbTokenInformation.serialNumber().isEmpty() || !_usbTokenInformation.issuer().isEmpty() || _tokenPublicKey || _tokenPrivateKey; + return !_privateKey.isEmpty() || !_certificate.isNull() || !_mnemonic.isEmpty() || !_usbTokenInformation.sha256Fingerprint().isEmpty() || _tokenPublicKey || _tokenPrivateKey; } void ClientSideEncryption::failedToInitialize(const AccountPtr &account) @@ -1691,8 +1682,7 @@ void ClientSideEncryption::failedToInitialize(const AccountPtr &account) void ClientSideEncryption::saveCertificateIdentification(const AccountPtr &account) const { - account->setEncryptionCertificateIssuer(_usbTokenInformation.issuer()); - account->setEncryptionCertificateSerialNumber(_usbTokenInformation.serialNumber()); + account->setEncryptionCertificateFingerprint(_usbTokenInformation.sha256Fingerprint()); } void ClientSideEncryption::cacheTokenPin(const QString pin) @@ -2235,17 +2225,15 @@ void FolderMetadata::setupExistingMetadata(const QByteArray& metadata) const auto files = metaDataDoc.object()["files"].toObject(); const auto metadataKey = metaDataDoc.object()["metadata"].toObject()["metadataKey"].toString().toUtf8(); const auto metadataKeyChecksum = metaDataDoc.object()["metadata"].toObject()["checksum"].toString().toUtf8(); - _metadataCertificateSerialNumber = metadataObj[certificateSerialNumberKey].toString(); - _metadataCertificateIssuer = metadataObj[certificateIssuerKey].toString(); + _metadataCertificateSha256Fingerprint = metadataObj[certificateSha256FingerprintKey].toString().toLatin1(); - if (!_metadataCertificateSerialNumber.isEmpty() && !_metadataCertificateIssuer.isEmpty()) { + if (!_metadataCertificateSha256Fingerprint.isEmpty()) { if (!_account->e2e()->useTokenBasedEncryption()) { qCWarning(lcCseMetadata()) << "e2ee metadata are missing proper information about the certificate used to encrypt them"; return; } - if (_metadataCertificateSerialNumber != _account->e2e()->usbTokenInformation()->serialNumber() || - _metadataCertificateIssuer != _account->e2e()->usbTokenInformation()->issuer()) { + if (_metadataCertificateSha256Fingerprint != _account->e2e()->usbTokenInformation()->sha256Fingerprint()) { qCInfo(lcCseMetadata()) << "migration of the certificate used to encrypt metadata is needed"; return; } @@ -2442,8 +2430,7 @@ QByteArray FolderMetadata::encryptedMetadata() const { QJsonObject metadata{ {"version", version}, {metadataKeyJsonKey, QJsonValue::fromVariant(*encryptedMetadataKey)}, - {certificateSerialNumberKey, _account->e2e()->usbTokenInformation()->serialNumber()}, - {certificateIssuerKey, _account->e2e()->usbTokenInformation()->issuer()}, + {certificateSha256FingerprintKey, QJsonValue::fromVariant(_account->e2e()->usbTokenInformation()->sha256Fingerprint())}, {"checksum", QJsonValue::fromVariant(computeMetadataKeyChecksum(*encryptedMetadataKey))}, }; diff --git a/src/libsync/clientsideencryption.h b/src/libsync/clientsideencryption.h index b89f4db28720d..c927ac30c3b2c 100644 --- a/src/libsync/clientsideencryption.h +++ b/src/libsync/clientsideencryption.h @@ -344,8 +344,7 @@ class OWNCLOUDSYNC_EXPORT FolderMetadata { QJsonObject _fileDrop; // used by unit tests, must get assigned simultaneously with _fileDrop and not erased QJsonObject _fileDropFromServer; - QString _metadataCertificateSerialNumber; - QString _metadataCertificateIssuer; + QByteArray _metadataCertificateSha256Fingerprint; bool _isMetadataSetup = false; bool _encryptedMetadataNeedUpdate = false; }; diff --git a/src/libsync/clientsideencryptiontokenselector.cpp b/src/libsync/clientsideencryptiontokenselector.cpp index d327393e42ed8..c7fd7ba8fceb3 100644 --- a/src/libsync/clientsideencryptiontokenselector.cpp +++ b/src/libsync/clientsideencryptiontokenselector.cpp @@ -109,7 +109,7 @@ ClientSideEncryptionTokenSelector::ClientSideEncryptionTokenSelector(QObject *pa bool ClientSideEncryptionTokenSelector::isSetup() const { - return !_issuer.isEmpty() && !_serialNumber.isEmpty(); + return !_sha256Fingerprint.isEmpty(); } QVariantList ClientSideEncryptionTokenSelector::discoveredCertificates() const @@ -117,14 +117,9 @@ QVariantList ClientSideEncryptionTokenSelector::discoveredCertificates() const return _discoveredCertificates; } -QString ClientSideEncryptionTokenSelector::serialNumber() const +QByteArray ClientSideEncryptionTokenSelector::sha256Fingerprint() const { - return _serialNumber; -} - -QString ClientSideEncryptionTokenSelector::issuer() const -{ - return _issuer; + return _sha256Fingerprint; } QFuture ClientSideEncryptionTokenSelector::searchForCertificates(const AccountPtr &account) @@ -134,24 +129,14 @@ QFuture ClientSideEncryptionTokenSelector::searchForCertificates(const Acc }); } -void ClientSideEncryptionTokenSelector::setSerialNumber(const QString &serialNumber) -{ - if (_serialNumber == serialNumber) { - return; - } - - _serialNumber = serialNumber; - Q_EMIT serialNumberChanged(); -} - -void ClientSideEncryptionTokenSelector::setIssuer(const QString &issuer) +void ClientSideEncryptionTokenSelector::setSha256Fingerprint(const QByteArray &sha256Fingerprint) { - if (_issuer == issuer) { + if (_sha256Fingerprint == sha256Fingerprint) { return; } - _issuer = issuer; - Q_EMIT issuerChanged(); + _sha256Fingerprint = sha256Fingerprint; + Q_EMIT sha256FingerprintChanged(); } void ClientSideEncryptionTokenSelector::discoverCertificates(const AccountPtr &account) @@ -233,7 +218,8 @@ void ClientSideEncryptionTokenSelector::discoverCertificates(const AccountPtr &a << "issuer:" << sslCertificate.issuerDisplayName() << "valid since:" << sslCertificate.effectiveDate() << "valid until:" << sslCertificate.expiryDate() - << "serial number:" << sslCertificate.serialNumber(); + << "serial number:" << sslCertificate.serialNumber() + << "SHA256 fingerprint:" << sslCertificate.digest(QCryptographicHash::Sha256).toBase64(); if (sslCertificate.isSelfSigned()) { qCDebug(lcCseSelector()) << "newly found certificate is self signed: goint to ignore it"; @@ -247,6 +233,7 @@ void ClientSideEncryptionTokenSelector::discoverCertificates(const AccountPtr &a {QStringLiteral("serialNumber"), sslCertificate.serialNumber()}, {QStringLiteral("validSince"), sslCertificate.effectiveDate()}, {QStringLiteral("validUntil"), sslCertificate.expiryDate()}, + {QStringLiteral("sha256Fingerprint"), sslCertificate.digest(QCryptographicHash::Sha256).toBase64()}, {QStringLiteral("certificate"), QVariant::fromValue(sslCertificate)}, }); } @@ -269,10 +256,9 @@ void ClientSideEncryptionTokenSelector::processDiscoveredCertificates() for (const auto &oneError : sslErrors) { qCInfo(lcCseSelector()) << oneError; } - qCInfo(lcCseSelector()) << "certificate is valid" << certificateData[QStringLiteral("serialNumber")] << certificateData[QStringLiteral("issuer")]; + qCInfo(lcCseSelector()) << "certificate is valid" << certificateData[QStringLiteral("subject")] << "from" << certificateData[QStringLiteral("issuer")] << "fingerprint" << sslCertificate.digest(QCryptographicHash::Sha256).toBase64(); - setIssuer(certificateData[QStringLiteral("issuer")].toString()); - setSerialNumber(certificateData[QStringLiteral("serialNumber")].toString()); + setSha256Fingerprint(sslCertificate.digest(QCryptographicHash::Sha256)); Q_EMIT isSetupChanged(); return; } diff --git a/src/libsync/clientsideencryptiontokenselector.h b/src/libsync/clientsideencryptiontokenselector.h index cc95cd94d7547..4b16fb819eed0 100644 --- a/src/libsync/clientsideencryptiontokenselector.h +++ b/src/libsync/clientsideencryptiontokenselector.h @@ -32,9 +32,7 @@ class OWNCLOUDSYNC_EXPORT ClientSideEncryptionTokenSelector : public QObject Q_PROPERTY(QVariantList discoveredCertificates READ discoveredCertificates NOTIFY discoveredCertificatesChanged) - Q_PROPERTY(QString serialNumber READ serialNumber WRITE setSerialNumber NOTIFY serialNumberChanged) - - Q_PROPERTY(QString issuer READ issuer WRITE setIssuer NOTIFY issuerChanged) + Q_PROPERTY(QByteArray sha256Fingerprint READ sha256Fingerprint WRITE setSha256Fingerprint NOTIFY sha256FingerprintChanged) public: explicit ClientSideEncryptionTokenSelector(QObject *parent = nullptr); @@ -43,16 +41,12 @@ class OWNCLOUDSYNC_EXPORT ClientSideEncryptionTokenSelector : public QObject [[nodiscard]] QVariantList discoveredCertificates() const; - [[nodiscard]] QString serialNumber() const; - - [[nodiscard]] QString issuer() const; + [[nodiscard]] QByteArray sha256Fingerprint() const; public slots: QFuture searchForCertificates(const OCC::AccountPtr &account); - void setSerialNumber(const QString &serialNumber); - - void setIssuer(const QString &issuer); + void setSha256Fingerprint(const QByteArray &sha256Fingerprint); signals: @@ -62,9 +56,7 @@ public slots: void certificateIndexChanged(); - void serialNumberChanged(); - - void issuerChanged(); + void sha256FingerprintChanged(); void failedToInitialize(const OCC::AccountPtr &account); @@ -75,9 +67,7 @@ public slots: QVariantList _discoveredCertificates; - QString _serialNumber; - - QString _issuer; + QByteArray _sha256Fingerprint; }; }