From e0e7e1b5cad29788372b779b6656d78dc93252fd Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Mon, 14 Jul 2025 00:33:40 +0900 Subject: [PATCH] ### Fixed - The encryption algorithm now uses HKDF with a master key. - `Fetch everything from the remote` now works correctly. - Extra log messages during QR code decoding have been removed. ### Changed - Some settings have been moved to the `Patches` pane: ### Behavioural and API Changes - `DirectFileManipulatorV2` now requires new settings (as you may already know, E2EEAlgorithm). - The database version has been increased to `12` from `10`. --- package-lock.json | 14 ++++---- package.json | 2 +- src/lib | 2 +- src/main.ts | 3 +- src/modules/core/ModuleRebuilder.ts | 6 ++-- src/modules/core/ModuleReplicator.ts | 13 +++++++- src/modules/coreFeatures/ModuleRedFlag.ts | 6 ++-- src/modules/essential/ModuleMigration.ts | 4 +-- .../essentialObsidian/ModuleObsidianAPI.ts | 23 +++++++------ src/modules/features/ModuleSetupObsidian.ts | 1 - .../ObsidianLiveSyncSettingTab.ts | 8 ++--- .../features/SettingDialogue/PanePatches.ts | 32 ++++++++++++++++++- .../SettingDialogue/PanePowerUsers.ts | 14 +------- .../SettingDialogue/PaneRemoteConfig.ts | 6 ---- 14 files changed, 80 insertions(+), 54 deletions(-) diff --git a/package-lock.json b/package-lock.json index a82360e..da14c85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "fflate": "^0.8.2", "idb": "^8.0.3", "minimatch": "^10.0.1", - "octagonal-wheels": "^0.1.31", + "octagonal-wheels": "^0.1.35", "qrcode-generator": "^1.4.4", "svelte-check": "^4.1.7", "trystero": "^0.21.5", @@ -8704,9 +8704,9 @@ } }, "node_modules/octagonal-wheels": { - "version": "0.1.31", - "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.31.tgz", - "integrity": "sha512-aR/QWyon6KUeW4UlJPRGKVqIiJLz4otm4F6PWOQp8aZAdqYIe/VYT5FBlaqQzCRNDrvkjVOvrHOlBGKIU++thw==", + "version": "0.1.35", + "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.35.tgz", + "integrity": "sha512-fjyvgg1+aG4SnpPjdZp6SPA/N6CseTgTLWnYWFN0mdH6qVAZzxNkDxKACtPxuxRQfgjj83yiPhQBMT3yA0XUnw==", "license": "MIT", "dependencies": { "idb": "^8.0.3" @@ -17515,9 +17515,9 @@ } }, "octagonal-wheels": { - "version": "0.1.31", - "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.31.tgz", - "integrity": "sha512-aR/QWyon6KUeW4UlJPRGKVqIiJLz4otm4F6PWOQp8aZAdqYIe/VYT5FBlaqQzCRNDrvkjVOvrHOlBGKIU++thw==", + "version": "0.1.35", + "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.35.tgz", + "integrity": "sha512-fjyvgg1+aG4SnpPjdZp6SPA/N6CseTgTLWnYWFN0mdH6qVAZzxNkDxKACtPxuxRQfgjj83yiPhQBMT3yA0XUnw==", "requires": { "idb": "^8.0.3" } diff --git a/package.json b/package.json index 9cfa9fb..bf8442f 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "fflate": "^0.8.2", "idb": "^8.0.3", "minimatch": "^10.0.1", - "octagonal-wheels": "^0.1.31", + "octagonal-wheels": "^0.1.35", "qrcode-generator": "^1.4.4", "svelte-check": "^4.1.7", "trystero": "^0.21.5", diff --git a/src/lib b/src/lib index dfbd635..ab02f72 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit dfbd6358b1fd31b827079293d957265beb74eebb +Subproject commit ab02f72aa515b0c1d9c1df8cc18bae68ec82f2ae diff --git a/src/main.ts b/src/main.ts index 7ceecd8..6a6cd22 100644 --- a/src/main.ts +++ b/src/main.ts @@ -292,7 +292,8 @@ export default class ObsidianLiveSyncPlugin skipInfo: boolean, compression: boolean, customHeaders: Record, - useRequestAPI: boolean + useRequestAPI: boolean, + getPBKDF2Salt: () => Promise ): Promise< | string | { diff --git a/src/modules/core/ModuleRebuilder.ts b/src/modules/core/ModuleRebuilder.ts index b382938..8ad38a9 100644 --- a/src/modules/core/ModuleRebuilder.ts +++ b/src/modules/core/ModuleRebuilder.ts @@ -11,7 +11,7 @@ import { AbstractModule } from "../AbstractModule.ts"; import type { Rebuilder } from "../interfaces/DatabaseRebuilder.ts"; import type { ICoreModule } from "../ModuleTypes.ts"; import type { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator.ts"; -import { fetchAllUsedChunks } from "../../lib/src/pouchdb/utils_couchdb.ts"; +import { fetchAllUsedChunks } from "@/lib/src/pouchdb/chunks.ts"; import { EVENT_DATABASE_REBUILT, eventHub } from "src/common/events.ts"; export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebuilder { @@ -90,8 +90,8 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu return this.rebuildEverything(); } - $fetchLocal(makeLocalChunkBeforeSync?: boolean): Promise { - return this.fetchLocal(makeLocalChunkBeforeSync); + $fetchLocal(makeLocalChunkBeforeSync?: boolean, preventMakeLocalFilesBeforeSync?: boolean): Promise { + return this.fetchLocal(makeLocalChunkBeforeSync, preventMakeLocalFilesBeforeSync); } async scheduleRebuild(): Promise { diff --git a/src/modules/core/ModuleReplicator.ts b/src/modules/core/ModuleReplicator.ts index 944e7a8..dac19d2 100644 --- a/src/modules/core/ModuleReplicator.ts +++ b/src/modules/core/ModuleReplicator.ts @@ -4,7 +4,8 @@ import { AbstractModule } from "../AbstractModule"; import type { ICoreModule } from "../ModuleTypes"; import { Logger, LOG_LEVEL_NOTICE, LOG_LEVEL_INFO, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; import { isLockAcquired, shareRunningResult, skipIfDuplicated } from "octagonal-wheels/concurrency/lock"; -import { purgeUnreferencedChunks, balanceChunkPurgedDBs } from "../../lib/src/pouchdb/utils_couchdb"; +import { balanceChunkPurgedDBs } from "@/lib/src/pouchdb/chunks"; +import { purgeUnreferencedChunks } from "@/lib/src/pouchdb/chunks"; import { LiveSyncCouchDBReplicator } from "../../lib/src/replication/couchdb/LiveSyncReplicator"; import { throttle } from "octagonal-wheels/function"; import { arrayToChunkedArray } from "octagonal-wheels/collection"; @@ -79,8 +80,18 @@ export class ModuleReplicator extends AbstractModule implements ICoreModule { $everyOnResetDatabase(db: LiveSyncLocalDB): Promise { return this.setReplicator(); } + async ensureReplicatorPBKDF2Salt(showMessage: boolean = false): Promise { + // Checking salt + const replicator = this.core.$$getReplicator(); + return await replicator.ensurePBKDF2Salt(this.settings, showMessage, true); + } async $everyBeforeReplicate(showMessage: boolean): Promise { + // Checking salt + if (!(await this.ensureReplicatorPBKDF2Salt(showMessage))) { + Logger("Failed to ensure PBKDF2 salt for replication.", LOG_LEVEL_NOTICE); + return false; + } await this.loadQueuedFiles(); return true; } diff --git a/src/modules/coreFeatures/ModuleRedFlag.ts b/src/modules/coreFeatures/ModuleRedFlag.ts index 565a655..7707135 100644 --- a/src/modules/coreFeatures/ModuleRedFlag.ts +++ b/src/modules/coreFeatures/ModuleRedFlag.ts @@ -121,9 +121,9 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule { } ); let makeLocalChunkBeforeSync = false; - let preventMakeLocalFilesBeforeSync = false; + let makeLocalFilesBeforeSync = false; if (chunkMode === method1) { - preventMakeLocalFilesBeforeSync = true; + makeLocalFilesBeforeSync = true; } else if (chunkMode === method2) { makeLocalChunkBeforeSync = true; } else if (chunkMode === method3) { @@ -133,7 +133,7 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule { return false; } - await this.core.rebuilder.$fetchLocal(makeLocalChunkBeforeSync, preventMakeLocalFilesBeforeSync); + await this.core.rebuilder.$fetchLocal(makeLocalChunkBeforeSync, !makeLocalFilesBeforeSync); await this.deleteRedFlag3(); if (this.settings.suspendFileWatching) { diff --git a/src/modules/essential/ModuleMigration.ts b/src/modules/essential/ModuleMigration.ts index 3ba0f6c..d50dd8b 100644 --- a/src/modules/essential/ModuleMigration.ts +++ b/src/modules/essential/ModuleMigration.ts @@ -84,8 +84,8 @@ export class ModuleMigration extends AbstractModule implements ICoreModule { $msg("Doctor.Dialogue.MainFix", { name: getConfName(key as AllSettingItemKey), current: `${this.settings[key]}`, - reason: value.reason ?? " N/A ", - ideal: `${value.valueDisplay ?? value.value}`, + reason: value.reasonFunc?.(this.settings) ?? value.reason ?? " N/A ", + ideal: `${value.valueDisplayFunc ? value.valueDisplayFunc(this.settings) : value.value}`, //@ts-ignore level: `${level}`, note: note, diff --git a/src/modules/essentialObsidian/ModuleObsidianAPI.ts b/src/modules/essentialObsidian/ModuleObsidianAPI.ts index afe0206..f436d34 100644 --- a/src/modules/essentialObsidian/ModuleObsidianAPI.ts +++ b/src/modules/essentialObsidian/ModuleObsidianAPI.ts @@ -3,13 +3,10 @@ import { LOG_LEVEL_DEBUG, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal- import { Notice, requestUrl, type RequestUrlParam, type RequestUrlResponse } from "../../deps.ts"; import { type CouchDBCredentials, type EntryDoc, type FilePathWithPrefix } from "../../lib/src/common/types.ts"; import { getPathFromTFile } from "../../common/utils.ts"; -import { - disableEncryption, - enableEncryption, - isCloudantURI, - isValidRemoteCouchDBURI, - replicationFilter, -} from "../../lib/src/pouchdb/utils_couchdb.ts"; +import { isCloudantURI, isValidRemoteCouchDBURI } from "../../lib/src/pouchdb/utils_couchdb.ts"; +import { replicationFilter } from "@/lib/src/pouchdb/compress.ts"; +import { disableEncryption } from "@/lib/src/pouchdb/encryption.ts"; +import { enableEncryption } from "@/lib/src/pouchdb/encryption.ts"; import { setNoticeClass } from "../../lib/src/mock_and_interop/wrapper.ts"; import { ObsHttpHandler } from "./APILib/ObsHttpHandler.ts"; import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser.ts"; @@ -103,7 +100,8 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi skipInfo: boolean, compression: boolean, customHeaders: Record, - useRequestAPI: boolean + useRequestAPI: boolean, + getPBKDF2Salt: () => Promise ): Promise; info: PouchDB.Core.DatabaseInfo }> { if (!isValidRemoteCouchDBURI(uri)) return "Remote URI is not valid"; if (uri.toLowerCase() != uri) return "Remote URI and database name could not contain capital letters."; @@ -229,7 +227,14 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi replicationFilter(db, compression); disableEncryption(); if (passphrase !== "false" && typeof passphrase === "string") { - enableEncryption(db, passphrase, useDynamicIterationCount, false); + enableEncryption( + db, + passphrase, + useDynamicIterationCount, + false, + getPBKDF2Salt, + this.settings.E2EEAlgorithm + ); } if (skipInfo) { return { db: db, info: { db_name: "", doc_count: 0, update_seq: "" } }; diff --git a/src/modules/features/ModuleSetupObsidian.ts b/src/modules/features/ModuleSetupObsidian.ts index a8cc514..cce6d15 100644 --- a/src/modules/features/ModuleSetupObsidian.ts +++ b/src/modules/features/ModuleSetupObsidian.ts @@ -101,7 +101,6 @@ export class ModuleSetupObsidian extends AbstractObsidianModule implements IObsi //@ts-ignore newSettings[settingKey] = settingValue; } - console.warn(newSettings); await this.applySettingWizard(this.settings, newSettings, "QR Code"); } async command_copySetupURI(stripExtra = true) { diff --git a/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts b/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts index 00f7ef4..d5c232e 100644 --- a/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts +++ b/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts @@ -16,11 +16,9 @@ import { import { delay, isObjectDifferent, sizeToHumanReadable } from "../../../lib/src/common/utils.ts"; import { versionNumberString2Number } from "../../../lib/src/string_and_binary/convert.ts"; import { Logger } from "../../../lib/src/common/logger.ts"; -import { - balanceChunkPurgedDBs, - checkSyncInfo, - purgeUnreferencedChunks, -} from "../../../lib/src/pouchdb/utils_couchdb.ts"; +import { checkSyncInfo } from "@/lib/src/pouchdb/negotiation.ts"; +import { balanceChunkPurgedDBs } from "@/lib/src/pouchdb/chunks.ts"; +import { purgeUnreferencedChunks } from "@/lib/src/pouchdb/chunks.ts"; import { testCrypt } from "../../../lib/src/encryption/e2ee_v2.ts"; import ObsidianLiveSyncPlugin from "../../../main.ts"; import { scheduleTask } from "../../../common/utils.ts"; diff --git a/src/modules/features/SettingDialogue/PanePatches.ts b/src/modules/features/SettingDialogue/PanePatches.ts index eed7c5c..c4234bd 100644 --- a/src/modules/features/SettingDialogue/PanePatches.ts +++ b/src/modules/features/SettingDialogue/PanePatches.ts @@ -1,4 +1,9 @@ -import { type HashAlgorithm, LOG_LEVEL_NOTICE } from "../../../lib/src/common/types.ts"; +import { + E2EEAlgorithmNames, + E2EEAlgorithms, + type HashAlgorithm, + LOG_LEVEL_NOTICE, +} from "../../../lib/src/common/types.ts"; import { Logger } from "../../../lib/src/common/logger.ts"; import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts"; import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; @@ -37,6 +42,19 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen void addPanel(paneEl, "Compatibility (Internal API Usage)").then((paneEl) => { new Setting(paneEl).autoWireToggle("watchInternalFileChanges", { invert: true }); }); + void addPanel(paneEl, "Compatibility (Remote Database)").then((paneEl) => { + new Setting(paneEl).autoWireDropDown("E2EEAlgorithm", { + options: E2EEAlgorithmNames, + }); + }); + new Setting(paneEl).autoWireToggle("useDynamicIterationCount", { + holdValue: true, + onUpdate: visibleOnly( + () => + this.isConfiguredAs("E2EEAlgorithm", E2EEAlgorithms.ForceV1) || + this.isConfiguredAs("E2EEAlgorithm", E2EEAlgorithms.V1) + ), + }); void addPanel(paneEl, "Edge case addressing (Database)").then((paneEl) => { new Setting(paneEl) @@ -79,4 +97,16 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen void addPanel(paneEl, "Compatibility (Trouble addressed)").then((paneEl) => { new Setting(paneEl).autoWireToggle("disableCheckingConfigMismatch"); }); + + void addPanel(paneEl, "Remote Database Tweak (In sunset)").then((paneEl) => { + new Setting(paneEl).autoWireToggle("useEden").setClass("wizardHidden"); + const onlyUsingEden = visibleOnly(() => this.isConfiguredAs("useEden", true)); + new Setting(paneEl).autoWireNumeric("maxChunksInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden"); + new Setting(paneEl) + .autoWireNumeric("maxTotalLengthInEden", { onUpdate: onlyUsingEden }) + .setClass("wizardHidden"); + new Setting(paneEl).autoWireNumeric("maxAgeInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden"); + + new Setting(paneEl).autoWireToggle("enableCompression").setClass("wizardHidden"); + }); } diff --git a/src/modules/features/SettingDialogue/PanePowerUsers.ts b/src/modules/features/SettingDialogue/PanePowerUsers.ts index 4d6edd5..e7f33e8 100644 --- a/src/modules/features/SettingDialogue/PanePowerUsers.ts +++ b/src/modules/features/SettingDialogue/PanePowerUsers.ts @@ -2,24 +2,12 @@ import { type ConfigPassphraseStore } from "../../../lib/src/common/types.ts"; import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts"; import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { PageFunctions } from "./SettingPane.ts"; -import { visibleOnly } from "./SettingPane.ts"; + export function panePowerUsers( this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions ): void { - void addPanel(paneEl, "Remote Database Tweak").then((paneEl) => { - new Setting(paneEl).autoWireToggle("useEden").setClass("wizardHidden"); - const onlyUsingEden = visibleOnly(() => this.isConfiguredAs("useEden", true)); - new Setting(paneEl).autoWireNumeric("maxChunksInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden"); - new Setting(paneEl) - .autoWireNumeric("maxTotalLengthInEden", { onUpdate: onlyUsingEden }) - .setClass("wizardHidden"); - new Setting(paneEl).autoWireNumeric("maxAgeInEden", { onUpdate: onlyUsingEden }).setClass("wizardHidden"); - - new Setting(paneEl).autoWireToggle("enableCompression").setClass("wizardHidden"); - }); - void addPanel(paneEl, "CouchDB Connection Tweak", undefined, this.onlyOnCouchDB).then((paneEl) => { paneEl.addClass("wizardHidden"); diff --git a/src/modules/features/SettingDialogue/PaneRemoteConfig.ts b/src/modules/features/SettingDialogue/PaneRemoteConfig.ts index 83adcfe..a19a21d 100644 --- a/src/modules/features/SettingDialogue/PaneRemoteConfig.ts +++ b/src/modules/features/SettingDialogue/PaneRemoteConfig.ts @@ -579,12 +579,6 @@ The pane also can be launched by \`P2P Replicator\` command from the Command Pal holdValue: true, onUpdate: isEncryptEnabled, }); - new Setting(paneEl) - .autoWireToggle("useDynamicIterationCount", { - holdValue: true, - onUpdate: isEncryptEnabled, - }) - .setClass("wizardHidden"); }); void addPanel(paneEl, $msg("obsidianLiveSyncSettingTab.titleFetchSettings")).then((paneEl) => {