Skip to content

Commit

Permalink
Prepare code for signature verification.
Browse files Browse the repository at this point in the history
Signed-off-by: alex-z <blackslayer4@gmail.com>
  • Loading branch information
allexzander committed Jul 11, 2023
1 parent eb02efe commit 2f44ef8
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 19 deletions.
5 changes: 5 additions & 0 deletions src/libsync/clientsideencryptionjobs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ GetMetadataApiJob::GetMetadataApiJob(const AccountPtr& account,
{
}

const QByteArray &GetMetadataApiJob::signature() const
{
return _signature;
}

void GetMetadataApiJob::start()
{
QNetworkRequest req;
Expand Down
2 changes: 2 additions & 0 deletions src/libsync/clientsideencryptionjobs.h
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,8 @@ class OWNCLOUDSYNC_EXPORT GetMetadataApiJob : public AbstractNetworkJob
const QByteArray& fileId,
QObject *parent = nullptr);

[[nodiscard]] const QByteArray &signature() const;

public slots:
void start() override;

Expand Down
14 changes: 13 additions & 1 deletion src/libsync/discoveryphase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,15 @@ void DiscoverySingleDirectoryJob::metadataReceived(const QJsonDocument &json, in
qCDebug(lcDiscovery) << "Metadata received, applying it to the result list";
Q_ASSERT(_subPath.startsWith('/'));

const auto job = qobject_cast<GetMetadataApiJob *>(sender());
Q_ASSERT(job);
if (!job) {
qCDebug(lcDiscovery) << "metadataReceived must be called from GetMetadataApiJob's signal";
emit finished(HttpError{0, tr("Encrypted metadata setup error!")});
deleteLater();
return;
}

QString topLevelFolderPath;

for (const QString &topLevelPath : _listRootE2eeFolders) {
Expand All @@ -671,7 +680,10 @@ void DiscoverySingleDirectoryJob::metadataReceived(const QJsonDocument &json, in
topLevelFolderPath = QStringLiteral("/");
}

_e2EeFolderMetadata.reset(new FolderMetadata(_account, statusCode == 404 ? QByteArray{} : json.toJson(QJsonDocument::Compact), FolderMetadata::RootEncryptedFolderInfo(topLevelFolderPath)));
_e2EeFolderMetadata.reset(new FolderMetadata(_account,
statusCode == 404 ? QByteArray{} : json.toJson(QJsonDocument::Compact),
FolderMetadata::RootEncryptedFolderInfo(topLevelFolderPath),
job->signature()));
connect(_e2EeFolderMetadata.data(), &FolderMetadata::setupComplete, this, [this] {
if (!_e2EeFolderMetadata->isValid()) {
emit finished(HttpError{0, tr("Encrypted metadata setup error!")});
Expand Down
11 changes: 7 additions & 4 deletions src/libsync/encryptfolderjob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,13 @@ void EncryptFolderJob::uploadMetadata()
emit finished(Error, EncryptionStatusEnums::ItemEncryptionStatus::NotEncrypted);
return;
}
QSharedPointer<FolderMetadata> emptyMetadata(
new FolderMetadata(_account,
{},
FolderMetadata::RootEncryptedFolderInfo(FolderMetadata::RootEncryptedFolderInfo::createRootPath(currentPath, rec.path()))));

auto emptyMetadata(QSharedPointer<FolderMetadata>::create(
_account,
QByteArray{},
FolderMetadata::RootEncryptedFolderInfo(FolderMetadata::RootEncryptedFolderInfo::createRootPath(currentPath, rec.path())),
QByteArray{}));

connect(emptyMetadata.data(), &FolderMetadata::setupComplete, this, [this, emptyMetadata] {
const auto encryptedMetadata = !emptyMetadata->isValid() ? QByteArray{} : emptyMetadata->encryptedMetadata();
if (encryptedMetadata.isEmpty()) {
Expand Down
15 changes: 12 additions & 3 deletions src/libsync/fetchanduploade2eefoldermetadatajob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,12 @@ bool FetchAndUploadE2eeFolderMetadataJob::validateBeforeLock()
return false;
}

if (!folderMetadata()->isRootEncryptedFolder()) {
// normally, we should allow locking any nested folder to update its metadata, yet, with the new V2 architecture, this is something we might want to disallow
/*if (!folderMetadata()->isRootEncryptedFolder()) {
qCDebug(lcFetchAndUploadE2eeFolderMetadataJob) << "Error locking folder" << _folderId << "as it is not a top level folder";
emit uploadFinished(-1, tr("Error locking folder."));
return false;
}
}*/
return true;
}

Expand Down Expand Up @@ -153,6 +154,14 @@ void FetchAndUploadE2eeFolderMetadataJob::slotMetadataReceived(const QJsonDocume
{
qCDebug(lcFetchAndUploadE2eeFolderMetadataJob) << "Metadata Received, parsing it and decrypting" << json.toVariant();

const auto job = qobject_cast<GetMetadataApiJob *>(sender());
Q_ASSERT(job);
if (!job) {
qCDebug(lcFetchAndUploadE2eeFolderMetadataJob) << "slotMetadataReceived must be called from GetMetadataApiJob's signal";
emit fetchFinished(statusCode, tr("Error fetching metadata."));
return;
}

_allowEmptyMetadata = false;

if (statusCode != 200 && statusCode != 404) {
Expand All @@ -164,7 +173,7 @@ void FetchAndUploadE2eeFolderMetadataJob::slotMetadataReceived(const QJsonDocume

const auto rawMetadata = statusCode == 404
? QByteArray{} : json.toJson(QJsonDocument::Compact);
const auto metadata(QSharedPointer<FolderMetadata>::create(_account, rawMetadata, _rootEncryptedFolderInfo));
const auto metadata(QSharedPointer<FolderMetadata>::create(_account, rawMetadata, _rootEncryptedFolderInfo, job->signature()));
connect(metadata.data(), &FolderMetadata::setupComplete, this, [this, statusCode, metadata] {
if (!metadata->isValid()) {
qCDebug(lcFetchAndUploadE2eeFolderMetadataJob) << "Error parsing or decrypting metadata for folder" << _folderPath;
Expand Down
33 changes: 26 additions & 7 deletions src/libsync/foldermetadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ FolderMetadata::FolderMetadata(AccountPtr account)
FolderMetadata::FolderMetadata(AccountPtr account,
const QByteArray &metadata,
const RootEncryptedFolderInfo &rootEncryptedFolderInfo,
const QByteArray &signature,
QObject *parent)
: QObject(parent)
, _account(account)
Expand All @@ -114,6 +115,8 @@ FolderMetadata::FolderMetadata(AccountPtr account,
, _metadataKeyForEncryption(rootEncryptedFolderInfo.keyForEncryption)
, _metadataKeyForDecryption(rootEncryptedFolderInfo.keyForDecryption)
, _keyChecksums(rootEncryptedFolderInfo.keyChecksums)
, _counterForTopLevelMetadata(rootEncryptedFolderInfo.counter)
, _initialSignature(signature)
{
setupVersionFromExistingMetadata(metadata);

Expand Down Expand Up @@ -150,6 +153,13 @@ void FolderMetadata::setupExistingMetadata(const QByteArray &metadata)
const auto doc = QJsonDocument::fromJson(metadata);
qCDebug(lcCseMetadata()) << "Got existing metadata:" << doc.toJson(QJsonDocument::Compact);

if (_isRootEncryptedFolder && !_initialSignature.isEmpty()) {
const auto metadataForSignature = prepareMetadataForSignature(doc);

auto verifyResult = _account->e2e()->verifySignatureCMS(QByteArray::fromBase64(_initialSignature), metadataForSignature);
verifyResult = false;
}

if (_existingMetadataVersion < MetadataVersion::Version1) {
qCDebug(lcCseMetadata()) << "Could not setup metadata. Incorrect version" << _existingMetadataVersion;
return;
Expand Down Expand Up @@ -668,7 +678,7 @@ QByteArray FolderMetadata::encryptedMetadata()
auto jsonString = internalMetadata.toJson();

const auto metadataForSignature = prepareMetadataForSignature(internalMetadata);
_metadataSignature = _account->e2e()->generateSignatureCMS(metadataForSignature.toBase64()).toBase64();
_metadataSignature = _account->e2e()->generateSignatureCMS(metadataForSignature).toBase64();

_encryptedMetadataVersion = latestSupportedMetadataVersion();

Expand Down Expand Up @@ -770,19 +780,19 @@ QByteArray FolderMetadata::initialMetadata() const

quint64 FolderMetadata::counterForTopLevelMetadata() const
{
Q_ASSERT(_isRootEncryptedFolder);
/*Q_ASSERT(_isRootEncryptedFolder);
if (!_isRootEncryptedFolder) {
qCDebug(lcCseMetadata()) << "Counter is only applicable for top level metadata.";
}
}*/
return _counterForTopLevelMetadata;
}

quint64 FolderMetadata::newCounterForTopLevelMetadata() const
{
Q_ASSERT(_isRootEncryptedFolder);
/*Q_ASSERT(_isRootEncryptedFolder);
if (!_isRootEncryptedFolder) {
qCDebug(lcCseMetadata()) << "Counter is only applicable for top level metadata.";
}
}*/
return _counterForTopLevelMetadata + 1;
}

Expand Down Expand Up @@ -836,7 +846,7 @@ QByteArray FolderMetadata::prepareMetadataForSignature(const QJsonDocument &full

metdataModified.setObject(modifiedObject);
auto jsonString = metdataModified.toJson();
return metdataModified.toJson();
return metdataModified.toBinaryData();
}

void FolderMetadata::addEncryptedFile(const EncryptedFile &f) {
Expand Down Expand Up @@ -971,12 +981,20 @@ void FolderMetadata::rotE2eeFolderEncryptedMetadataReceivedError(const QByteArra

void FolderMetadata::rootE2eeFolderMetadataReceived(const QJsonDocument &json, int statusCode)
{
const auto job = qobject_cast<GetMetadataApiJob *>(sender());
Q_ASSERT(job);
if (!job) {
qCDebug(lcCseMetadata()) << "rootE2eeFolderMetadataReceived must be called from GetMetadataApiJob's signal";
initMetadata();
return;
}

if (json.isEmpty()) {
initMetadata();
return;
}

QSharedPointer<FolderMetadata> rootE2eeFolderMetadata(new FolderMetadata(_account, json.toJson(QJsonDocument::Compact), RootEncryptedFolderInfo::makeDefault()));
const auto rootE2eeFolderMetadata(QSharedPointer<FolderMetadata>::create(_account, json.toJson(QJsonDocument::Compact), RootEncryptedFolderInfo::makeDefault(), job->signature()));
connect(rootE2eeFolderMetadata.data(), &FolderMetadata::setupComplete, this, [this, rootE2eeFolderMetadata]() {
if (!rootE2eeFolderMetadata->isValid() || !rootE2eeFolderMetadata->isVersion2AndUp()) {
initMetadata();
Expand All @@ -992,6 +1010,7 @@ void FolderMetadata::rootE2eeFolderMetadataReceived(const QJsonDocument &json, i
_metadataKeyForDecryption = rootE2eeFolderMetadata->metadataKeyForDecryption();
_metadataKeyForEncryption = rootE2eeFolderMetadata->metadataKeyForEncryption();
_keyChecksums = rootE2eeFolderMetadata->keyChecksums();
_counterForTopLevelMetadata = rootE2eeFolderMetadata->counterForTopLevelMetadata();
initMetadata();
});
}
Expand Down
3 changes: 3 additions & 0 deletions src/libsync/foldermetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class OWNCLOUDSYNC_EXPORT FolderMetadata : public QObject
FolderMetadata(AccountPtr account,
const QByteArray &metadata,
const RootEncryptedFolderInfo &rootEncryptedFolderInfo,
const QByteArray &signature,
QObject *parent = nullptr);

[[nodiscard]] QVector<EncryptedFile> files() const;
Expand Down Expand Up @@ -208,6 +209,8 @@ private slots:

QByteArray _metadataSignature;

QByteArray _initialSignature;

QVector<EncryptedFile> _files;

bool _isMetadataValid = false;
Expand Down
14 changes: 12 additions & 2 deletions src/libsync/propagatedownloadencrypted.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,26 @@ void PropagateDownloadEncrypted::checkFolderEncryptedMetadata(const QJsonDocumen
{
qCDebug(lcPropagateDownloadEncrypted) << "Metadata Received reading"
<< _item->_instruction << _item->_file << _item->_encryptedFileName;

const auto job = qobject_cast<GetMetadataApiJob *>(sender());
Q_ASSERT(job);
if (!job) {
qCDebug(lcPropagateDownloadEncrypted) << "checkFolderEncryptedMetadata must be called from GetMetadataApiJob's signal";
emit failed();
return;
}

SyncJournalFileRecord rec;
if (!_propagator->_journal->getRootE2eFolderRecord(_parentPathInDb, &rec) || !rec.isValid()) {
emit failed();
return;
}

const QSharedPointer<FolderMetadata> metadata(new FolderMetadata(
const auto metadata(QSharedPointer<FolderMetadata>::create(
_propagator->account(),
json.toJson(QJsonDocument::Compact),
FolderMetadata::RootEncryptedFolderInfo(FolderMetadata::RootEncryptedFolderInfo::createRootPath(_item->_file, rec.path())))
FolderMetadata::RootEncryptedFolderInfo(FolderMetadata::RootEncryptedFolderInfo::createRootPath(_item->_file, rec.path())),
job->signature())
);

const auto filename = _info.fileName();
Expand Down
14 changes: 12 additions & 2 deletions src/libsync/vfs/cfapi/hydrationjob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,16 +199,26 @@ void OCC::HydrationJob::slotCheckFolderEncryptedMetadata(const QJsonDocument &js
{
// TODO: the following code is borrowed from PropagateDownloadEncrypted (see HydrationJob::onNewConnection() for explanation of next steps)
qCDebug(lcHydration) << "Metadata Received reading" << e2eMangledName();

const auto job = qobject_cast<GetMetadataApiJob *>(sender());
Q_ASSERT(job);
if (!job) {
qCDebug(lcHydration) << "slotCheckFolderEncryptedMetadata must be called from GetMetadataApiJob's signal";
emitFinished(Error);
return;
}

const auto filename = e2eMangledName();
SyncJournalFileRecord rec;
if (!_journal->getRootE2eFolderRecord(_remoteParentPath, &rec) || !rec.isValid()) {
emitFinished(Error);
return;
}
const QSharedPointer<FolderMetadata> metadata(new FolderMetadata(
const auto metadata(QSharedPointer<FolderMetadata>::create(
_account,
json.toJson(QJsonDocument::Compact),
FolderMetadata::RootEncryptedFolderInfo(FolderMetadata::RootEncryptedFolderInfo::createRootPath(rec.path(), _remoteParentPath)))
FolderMetadata::RootEncryptedFolderInfo(FolderMetadata::RootEncryptedFolderInfo::createRootPath(rec.path(), _remoteParentPath)),
job->signature())
);
connect(metadata.data(), &FolderMetadata::setupComplete, this, [this, metadata, filename] {
if (metadata->isValid()) {
Expand Down

0 comments on commit 2f44ef8

Please sign in to comment.