diff --git a/src/encryptUnified.ts b/src/encryptUnified.ts index 6a16354..8f93215 100644 --- a/src/encryptUnified.ts +++ b/src/encryptUnified.ts @@ -42,9 +42,18 @@ export class Cipher { return content; } if (this.method === "openssl-base64") { - return await openssl.encryptArrayBuffer(content, this.password); + const res = await openssl.encryptArrayBuffer(content, this.password); + if (res === undefined) { + throw Error(`cannot encrypt content`); + } + return res; } else if (this.method === "rclone-base64") { - return await this.cipherRClone!.encryptContentByCallingWorker(content); + const res = + await this.cipherRClone!.encryptContentByCallingWorker(content); + if (res === undefined) { + throw Error(`cannot encrypt content`); + } + return res; } else { throw Error(`not supported encrypt method=${this.method}`); } @@ -56,9 +65,18 @@ export class Cipher { return content; } if (this.method === "openssl-base64") { - return await openssl.decryptArrayBuffer(content, this.password); + const res = await openssl.decryptArrayBuffer(content, this.password); + if (res === undefined) { + throw Error(`cannot decrypt content`); + } + return res; } else if (this.method === "rclone-base64") { - return await this.cipherRClone!.decryptContentByCallingWorker(content); + const res = + await this.cipherRClone!.decryptContentByCallingWorker(content); + if (res === undefined) { + throw Error(`cannot decrypt content`); + } + return res; } else { throw Error(`not supported decrypt method=${this.method}`); } @@ -70,15 +88,23 @@ export class Cipher { return name; } if (this.method === "openssl-base64") { - return await openssl.encryptStringToBase64url(name, this.password); + const res = await openssl.encryptStringToBase64url(name, this.password); + if (res === undefined) { + throw Error(`cannot encrypt name=${name}`); + } + return res; } else if (this.method === "rclone-base64") { - return await this.cipherRClone!.encryptNameByCallingWorker(name); + const res = await this.cipherRClone!.encryptNameByCallingWorker(name); + if (res === undefined) { + throw Error(`cannot encrypt name=${name}`); + } + return res; } else { throw Error(`not supported encrypt method=${this.method}`); } } - async decryptName(name: string) { + async decryptName(name: string): Promise { // console.debug("start decryptName"); if (this.password === "") { return name; @@ -88,7 +114,7 @@ export class Cipher { // backward compitable with the openssl-base32 try { const res = await openssl.decryptBase32ToString(name, this.password); - if (isVaildText(res)) { + if (res !== undefined && isVaildText(res)) { return res; } else { throw Error(`cannot decrypt name=${name}`); @@ -102,7 +128,7 @@ export class Cipher { name, this.password ); - if (isVaildText(res)) { + if (res !== undefined && isVaildText(res)) { return res; } else { throw Error(`cannot decrypt name=${name}`); @@ -110,9 +136,17 @@ export class Cipher { } catch (error) { throw Error(`cannot decrypt name=${name}`); } + } else { + throw Error( + `method=${this.method} but the name=${name}, likely mismatch` + ); } } else if (this.method === "rclone-base64") { - return await this.cipherRClone!.decryptNameByCallingWorker(name); + const res = await this.cipherRClone!.decryptNameByCallingWorker(name); + if (res === undefined) { + throw Error(`cannot decrypt name=${name}`); + } + return res; } else { throw Error(`not supported decrypt method=${this.method}`); } @@ -136,7 +170,7 @@ export class Cipher { * @param name * @returns */ - static isLikelyEncryptedName(name: string): boolean { + static isLikelyOpenSSLEncryptedName(name: string): boolean { if ( name.startsWith(openssl.MAGIC_ENCRYPTED_PREFIX_BASE32) || name.startsWith(openssl.MAGIC_ENCRYPTED_PREFIX_BASE64URL) @@ -145,4 +179,37 @@ export class Cipher { } return false; } + + /** + * quick guess, no actual decryption here + * @param name + * @returns + */ + static isLikelyEncryptedName(name: string): boolean { + return Cipher.isLikelyOpenSSLEncryptedName(name); + } + + /** + * quick guess, no actual decryption here, only openssl can be guessed here + * @param name + * @returns + */ + static isLikelyEncryptedNameNotMatchMethod( + name: string, + method: CipherMethodType + ): boolean { + if ( + Cipher.isLikelyOpenSSLEncryptedName(name) && + method !== "openssl-base64" + ) { + return true; + } + if ( + !Cipher.isLikelyOpenSSLEncryptedName(name) && + method === "openssl-base64" + ) { + return true; + } + return false; + } } diff --git a/src/misc.ts b/src/misc.ts index 91208bb..7a169b4 100644 --- a/src/misc.ts +++ b/src/misc.ts @@ -165,6 +165,9 @@ export const base64ToBase64url = (a: string, pad: boolean = false) => { * @param a */ export const isVaildText = (a: string) => { + if (a === undefined) { + return false; + } // If the regex matches, the string is invalid. return !XRegExp("\\p{Cc}|\\p{Cf}|\\p{Co}|\\p{Cn}|\\p{Zl}|\\p{Zp}", "A").test( a diff --git a/src/settings.ts b/src/settings.ts index 6f9c658..48e4809 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -125,15 +125,9 @@ class PasswordModal extends Modal { class EncryptionMethodModal extends Modal { plugin: RemotelySavePlugin; - newEncryptionMethod: CipherMethodType; - constructor( - app: App, - plugin: RemotelySavePlugin, - newEncryptionMethod: CipherMethodType - ) { + constructor(app: App, plugin: RemotelySavePlugin) { super(app); this.plugin = plugin; - this.newEncryptionMethod = newEncryptionMethod; } onOpen() { @@ -153,22 +147,13 @@ class EncryptionMethodModal extends Modal { }); }); - new Setting(contentEl) - .addButton((button) => { - button.setButtonText(t("confirm")); - button.onClick(async () => { - this.plugin.settings.encryptionMethod = this.newEncryptionMethod; - await this.plugin.saveSettings(); - this.close(); - }); - button.setClass("encryptionmethod-second-confirm"); - }) - .addButton((button) => { - button.setButtonText(t("goback")); - button.onClick(() => { - this.close(); - }); + new Setting(contentEl).addButton((button) => { + button.setButtonText(t("confirm")); + button.onClick(async () => { + this.close(); }); + button.setClass("encryptionmethod-second-confirm"); + }); } onClose() { @@ -1693,26 +1678,17 @@ export class RemotelySaveSettingTab extends PluginSettingTab { .setName(t("settings_encryptionmethod")) .setDesc(stringToFragment(t("settings_encryptionmethod_desc"))) .addDropdown((dropdown) => { - dropdown.addOption( - "rclone-base64", - t("settings_encryptionmethod_rclone") - ); - dropdown.addOption( - "openssl-base64", - t("settings_encryptionmethod_openssl") - ); - dropdown.onChange(async (val: string) => { - if (this.plugin.settings.password === "") { + dropdown + .addOption("rclone-base64", t("settings_encryptionmethod_rclone")) + .addOption("openssl-base64", t("settings_encryptionmethod_openssl")) + .setValue(this.plugin.settings.encryptionMethod ?? "rclone-base64") + .onChange(async (val: string) => { this.plugin.settings.encryptionMethod = val as CipherMethodType; await this.plugin.saveSettings(); - } else { - new EncryptionMethodModal( - this.app, - this.plugin, - val as CipherMethodType - ).open(); - } - }); + if (this.plugin.settings.password !== "") { + new EncryptionMethodModal(this.app, this.plugin).open(); + } + }); }); new Setting(basicDiv) diff --git a/src/sync.ts b/src/sync.ts index 18e0af3..c3e4bea 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -53,8 +53,9 @@ export interface PasswordCheckType { | "unknown_encryption_method" | "remote_encrypted_local_no_password" | "password_matched" - | "password_not_matched_or_remote_not_encrypted" - | "likely_no_password_both_sides"; + | "password_or_method_not_matched_or_remote_not_encrypted" + | "likely_no_password_both_sides" + | "encryption_method_not_matched"; } export const isPasswordOk = async ( @@ -91,8 +92,19 @@ export const isPasswordOk = async ( reason: "unknown_encryption_method", }; } + if ( + Cipher.isLikelyEncryptedNameNotMatchMethod(santyCheckKey, cipher.method) + ) { + return { + ok: false, + reason: "encryption_method_not_matched", + }; + } try { - await cipher.decryptName(santyCheckKey); + const k = await cipher.decryptName(santyCheckKey); + if (k === undefined) { + throw Error(`decryption failed`); + } return { ok: true, reason: "password_matched", @@ -100,7 +112,7 @@ export const isPasswordOk = async ( } catch (error) { return { ok: false, - reason: "password_not_matched_or_remote_not_encrypted", + reason: "password_or_method_not_matched_or_remote_not_encrypted", }; } }