mirror of
https://github.com/nextcloud/desktop.git
synced 2025-10-26 11:17:43 +00:00
246 lines
8.6 KiB
C++
246 lines
8.6 KiB
C++
#pragma once
|
|
/*
|
|
* Copyright (C) 2024 by Oleksandr Zolotov <alex@nextcloud.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
#include "accountfwd.h"
|
|
#include "encryptedfoldermetadatahandler.h"
|
|
#include "csync.h"
|
|
#include "rootencryptedfolderinfo.h"
|
|
#include <QByteArray>
|
|
#include <QHash>
|
|
#include <QJsonObject>
|
|
#include <QObject>
|
|
#include <QSet>
|
|
#include <QSslKey>
|
|
#include <QString>
|
|
#include <QVector>
|
|
|
|
class QSslCertificate;
|
|
class QJsonDocument;
|
|
class TestClientSideEncryptionV2;
|
|
class TestSecureFileDrop;
|
|
namespace OCC
|
|
{
|
|
// Handles parsing and altering the metadata, encryption and decryption. Setup of the instance is always asynchronouse and emits void setupComplete()
|
|
class OWNCLOUDSYNC_EXPORT FolderMetadata : public QObject
|
|
{
|
|
friend class ::TestClientSideEncryptionV2;
|
|
friend class ::TestSecureFileDrop;
|
|
Q_OBJECT
|
|
|
|
struct UserWithFolderAccess {
|
|
QString userId;
|
|
QByteArray certificatePem;
|
|
QByteArray encryptedMetadataKey;
|
|
};
|
|
|
|
// based on api-version and "version" key in metadata JSON
|
|
enum MetadataVersion {
|
|
VersionUndefined = -1,
|
|
Version1,
|
|
Version1_2,
|
|
Version2_0,
|
|
};
|
|
|
|
struct UserWithFileDropEntryAccess {
|
|
QString userId;
|
|
QByteArray decryptedFiledropKey;
|
|
|
|
inline bool isValid() const
|
|
{
|
|
return !userId.isEmpty() && !decryptedFiledropKey.isEmpty();
|
|
}
|
|
};
|
|
|
|
struct FileDropEntry {
|
|
QString encryptedFilename;
|
|
QByteArray cipherText;
|
|
QByteArray nonce;
|
|
QByteArray authenticationTag;
|
|
UserWithFileDropEntryAccess currentUser;
|
|
|
|
inline bool isValid() const
|
|
{
|
|
return !cipherText.isEmpty() && !nonce.isEmpty() && !authenticationTag.isEmpty();
|
|
}
|
|
};
|
|
|
|
public:
|
|
struct EncryptedFile {
|
|
QByteArray encryptionKey;
|
|
QByteArray mimetype;
|
|
QByteArray initializationVector;
|
|
QByteArray authenticationTag;
|
|
QString encryptedFilename;
|
|
QString originalFilename;
|
|
bool isDirectory() const;
|
|
};
|
|
|
|
enum class FolderType {
|
|
Nested = 0,
|
|
Root = 1,
|
|
};
|
|
Q_ENUM(FolderType)
|
|
|
|
FolderMetadata(AccountPtr account, FolderType folderType = FolderType::Nested);
|
|
/*
|
|
* construct metadata based on RootEncryptedFolderInfo
|
|
* as per E2EE V2, the encryption key and users that have access are only stored in root(top-level) encrypted folder's metadata
|
|
* see: https://github.com/nextcloud/end_to_end_encryption_rfc/blob/v2.1/RFC.md
|
|
*/
|
|
FolderMetadata(AccountPtr account,
|
|
const QByteArray &metadata,
|
|
const RootEncryptedFolderInfo &rootEncryptedFolderInfo,
|
|
const QByteArray &signature,
|
|
QObject *parent = nullptr);
|
|
|
|
[[nodiscard]] QVector<EncryptedFile> files() const;
|
|
|
|
[[nodiscard]] bool isValid() const;
|
|
|
|
[[nodiscard]] bool isFileDropPresent() const;
|
|
|
|
[[nodiscard]] bool isRootEncryptedFolder() const;
|
|
|
|
[[nodiscard]] bool encryptedMetadataNeedUpdate() const;
|
|
|
|
[[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);
|
|
// removes a user from this folder and removes and generates a new metadata key
|
|
[[nodiscard]] bool removeUser(const QString &userId);
|
|
|
|
[[nodiscard]] const QByteArray metadataKeyForEncryption() const;
|
|
[[nodiscard]] const QByteArray metadataKeyForDecryption() const;
|
|
[[nodiscard]] const QSet<QByteArray> &keyChecksums() const;
|
|
|
|
[[nodiscard]] QByteArray encryptedMetadata();
|
|
|
|
[[nodiscard]] EncryptionStatusEnums::ItemEncryptionStatus existingMetadataEncryptionStatus() const;
|
|
[[nodiscard]] EncryptionStatusEnums::ItemEncryptionStatus encryptedMetadataEncryptionStatus() const;
|
|
|
|
[[nodiscard]] bool isVersion2AndUp() const;
|
|
|
|
[[nodiscard]] quint64 newCounter() const;
|
|
|
|
[[nodiscard]] QByteArray metadataSignature() const;
|
|
|
|
[[nodiscard]] QByteArray initialMetadata() const;
|
|
|
|
public slots:
|
|
void addEncryptedFile(const EncryptedFile &f);
|
|
void removeEncryptedFile(const EncryptedFile &f);
|
|
void removeAllEncryptedFiles();
|
|
|
|
private:
|
|
[[nodiscard]] QByteArray encryptedMetadataLegacy();
|
|
|
|
[[nodiscard]] bool verifyMetadataKey(const QByteArray &metadataKey) const;
|
|
|
|
[[nodiscard]] QByteArray encryptDataWithPublicKey(const QByteArray &data, const QSslKey &key) const;
|
|
[[nodiscard]] QByteArray decryptDataWithPrivateKey(const QByteArray &data) const;
|
|
|
|
[[nodiscard]] QByteArray encryptJsonObject(const QByteArray& obj, const QByteArray pass) const;
|
|
[[nodiscard]] QByteArray decryptJsonObject(const QByteArray& encryptedJsonBlob, const QByteArray& pass) const;
|
|
|
|
[[nodiscard]] bool checkMetadataKeyChecksum(const QByteArray &metadataKey, const QByteArray &metadataKeyChecksum) const;
|
|
|
|
[[nodiscard]] QByteArray computeMetadataKeyChecksum(const QByteArray &metadataKey) const;
|
|
|
|
[[nodiscard]] EncryptedFile parseEncryptedFileFromJson(const QString &encryptedFilename, const QJsonValue &fileJSON) const;
|
|
|
|
[[nodiscard]] QJsonObject convertFileToJsonObject(const EncryptedFile *encryptedFile) const;
|
|
|
|
[[nodiscard]] MetadataVersion latestSupportedMetadataVersion() const;
|
|
|
|
[[nodiscard]] bool parseFileDropPart(const QJsonDocument &doc);
|
|
|
|
void setFileDrop(const QJsonObject &fileDrop);
|
|
|
|
static EncryptionStatusEnums::ItemEncryptionStatus fromMedataVersionToItemEncryptionStatus(const MetadataVersion metadataVersion);
|
|
static MetadataVersion fromItemEncryptionStatusToMedataVersion(const EncryptionStatusEnums::ItemEncryptionStatus encryptionStatus);
|
|
|
|
static QByteArray prepareMetadataForSignature(const QJsonDocument &fullMetadata);
|
|
|
|
private slots:
|
|
void initMetadata();
|
|
void initEmptyMetadata();
|
|
void initEmptyMetadataLegacy();
|
|
|
|
void setupExistingMetadata(const QByteArray &metadata);
|
|
void setupExistingMetadataLegacy(const QByteArray &metadata);
|
|
|
|
void setupVersionFromExistingMetadata(const QByteArray &metadata);
|
|
|
|
void startFetchRootE2eeFolderMetadata(const QString &path);
|
|
void slotRootE2eeFolderMetadataReceived(int statusCode, const QString &message);
|
|
|
|
void updateUsersEncryptedMetadataKey();
|
|
void createNewMetadataKeyForEncryption();
|
|
|
|
void emitSetupComplete();
|
|
|
|
signals:
|
|
void setupComplete();
|
|
|
|
private:
|
|
AccountPtr _account;
|
|
QByteArray _initialMetadata;
|
|
|
|
bool _isRootEncryptedFolder = false;
|
|
// always contains the last generated metadata key (non-encrypted and non-base64)
|
|
QByteArray _metadataKeyForEncryption;
|
|
// used for storing initial metadataKey to use for decryption, especially in nested folders when changing the metadataKey and re-encrypting nested dirs
|
|
QByteArray _metadataKeyForDecryption;
|
|
QByteArray _metadataNonce;
|
|
// metadatakey checksums for validation during setting up from existing metadata
|
|
QSet<QByteArray> _keyChecksums;
|
|
|
|
// filedrop part non-parsed, for upload in case parsing can not be done (due to not having access for the current user, etc.)
|
|
QJsonObject _fileDrop;
|
|
// used by unit tests, must get assigned simultaneously with _fileDrop and never erased
|
|
QJsonObject _fileDropFromServer;
|
|
|
|
// legacy, remove after migration is done
|
|
QMap<int, QByteArray> _metadataKeys;
|
|
|
|
// users that have access to current folder's "ciphertext", except "filedrop" part
|
|
QHash<QString, UserWithFolderAccess> _folderUsers;
|
|
|
|
// must increment on each metadata upload
|
|
quint64 _counter = 0;
|
|
|
|
MetadataVersion _existingMetadataVersion = MetadataVersion::VersionUndefined;
|
|
MetadataVersion _encryptedMetadataVersion = MetadataVersion::VersionUndefined;
|
|
|
|
// generated each time QByteArray encryptedMetadata() is called, and will later be used for validation if uploaded
|
|
QByteArray _metadataSignature;
|
|
// signature from server-side metadata
|
|
QByteArray _initialSignature;
|
|
|
|
// both files and folders info
|
|
QVector<EncryptedFile> _files;
|
|
|
|
// parsed filedrop entries ready for move
|
|
QVector<FileDropEntry> _fileDropEntries;
|
|
|
|
// sets to "true" on successful parse
|
|
bool _isMetadataValid = false;
|
|
|
|
QScopedPointer<EncryptedFolderMetadataHandler> _encryptedFolderMetadataHandler;
|
|
};
|
|
|
|
} // namespace OCC
|