From da921ab4d80a836e3550ce4f88ee87828788f334 Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Sat, 24 Oct 2020 11:43:46 +0300 Subject: [PATCH 01/19] Dummy commit. --- extension/js/common/core/crypto/key.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/extension/js/common/core/crypto/key.ts b/extension/js/common/core/crypto/key.ts index 14e6a1fa28a..f8c87b7c15b 100644 --- a/extension/js/common/core/crypto/key.ts +++ b/extension/js/common/core/crypto/key.ts @@ -79,6 +79,7 @@ export interface KeyInfo extends PrvKeyInfo { primary: boolean; } + export type PrvPacket = (OpenPGP.packet.SecretKey | OpenPGP.packet.SecretSubkey); export class UnexpectedKeyTypeError extends Error { } From 92562877868c8c32e2ea8c72c27c3490c6e9efcb Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Sat, 24 Oct 2020 12:25:50 +0300 Subject: [PATCH 02/19] Remove unused types. --- extension/js/common/core/crypto/key.ts | 1 - test/source/platform/store/key-store.ts | 15 --------------- 2 files changed, 16 deletions(-) delete mode 100644 test/source/platform/store/key-store.ts diff --git a/extension/js/common/core/crypto/key.ts b/extension/js/common/core/crypto/key.ts index f8c87b7c15b..14e6a1fa28a 100644 --- a/extension/js/common/core/crypto/key.ts +++ b/extension/js/common/core/crypto/key.ts @@ -79,7 +79,6 @@ export interface KeyInfo extends PrvKeyInfo { primary: boolean; } - export type PrvPacket = (OpenPGP.packet.SecretKey | OpenPGP.packet.SecretSubkey); export class UnexpectedKeyTypeError extends Error { } diff --git a/test/source/platform/store/key-store.ts b/test/source/platform/store/key-store.ts deleted file mode 100644 index 0af1dc5fce4..00000000000 --- a/test/source/platform/store/key-store.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com */ - -export interface PrvKeyInfo { - private: string; - longid: string; - decrypted?: OpenPGP.key.Key; -} - -export interface KeyInfo extends PrvKeyInfo { - public: string; - fingerprint: string; - primary: boolean; -} - -export type KeyInfosWithPassphrases = { keys: PrvKeyInfo[]; passphrases: string[]; }; From 263cbc59b500e09a3caa3afc5d2e88b8a844b775 Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Sat, 24 Oct 2020 13:12:29 +0300 Subject: [PATCH 03/19] Remove primary key. --- .../compose-modules/compose-storage-module.ts | 2 +- extension/chrome/elements/passphrase.ts | 2 +- extension/chrome/settings/index.ts | 10 ++++------ extension/chrome/settings/modules/experimental.ts | 1 - extension/js/common/core/crypto/key.ts | 1 - extension/js/common/platform/store/key-store.ts | 11 ++++------- 6 files changed, 10 insertions(+), 17 deletions(-) diff --git a/extension/chrome/elements/compose-modules/compose-storage-module.ts b/extension/chrome/elements/compose-modules/compose-storage-module.ts index 1b712f78710..424a4e069cf 100644 --- a/extension/chrome/elements/compose-modules/compose-storage-module.ts +++ b/extension/chrome/elements/compose-modules/compose-storage-module.ts @@ -39,7 +39,7 @@ export class ComposeStorageModule extends ViewModule { let result = await this.view.myPubkeyModule.chooseMyPublicKeyBySenderEmail(keys, senderEmail); if (!result) { this.view.errModule.debug(`ComposerStorage.getKey: could not find key based on senderEmail: ${senderEmail}, using primary instead`); - result = keys.find(ki => ki.primary); + result = keys[0]; Assert.abortAndRenderErrorIfKeyinfoEmpty(result); } else { this.view.errModule.debug(`ComposerStorage.getKey: found key based on senderEmail: ${senderEmail}`); diff --git a/extension/chrome/elements/passphrase.ts b/extension/chrome/elements/passphrase.ts index 09c4a0c428f..5d83babd825 100644 --- a/extension/chrome/elements/passphrase.ts +++ b/extension/chrome/elements/passphrase.ts @@ -35,7 +35,7 @@ View.run(class PassphraseView extends View { Ui.event.protect(); await initPassphraseToggle(['passphrase']); const allPrivateKeys = await KeyStore.get(this.acctEmail); - this.myPrivateKeys = allPrivateKeys.filter(ki => this.longids.includes(ki.longid) || (ki.primary && this.longids.includes('primary'))); + this.myPrivateKeys = allPrivateKeys.filter(ki => this.longids.includes(ki.longid)); if (this.type === 'embedded') { $('h1').parent().css('display', 'none'); $('div.separator').css('display', 'none'); diff --git a/extension/chrome/settings/index.ts b/extension/chrome/settings/index.ts index 691762283b4..2b4697028a3 100644 --- a/extension/chrome/settings/index.ts +++ b/extension/chrome/settings/index.ts @@ -425,18 +425,16 @@ View.run(class SettingsView extends View { const created = new Date(prv.created); const date = Str.monthName(created.getMonth()) + ' ' + created.getDate() + ', ' + created.getFullYear(); const escapedFp = Xss.escape(ki.fingerprint); - let escapedPrimaryOrRm = ''; - if (ki.primary) { - escapedPrimaryOrRm = '(primary)'; - } else if (canRemoveKey) { - escapedPrimaryOrRm = `(remove)`; + let removeKeyBtn = ''; + if (canRemoveKey) { + removeKeyBtn = `(remove)`; } const escapedEmail = Xss.escape(prv.emails[0] || ''); const escapedLink = `${escapedEmail}`; const fpHtml = `fingerprint: ${Str.spaced(escapedFp)}`; const space = `        `; html += `
`; - html += `
${escapedLink} from ${Xss.escape(date)}${space}${fpHtml}${space}${escapedPrimaryOrRm}
`; + html += `
${escapedLink} from ${Xss.escape(date)}${space}${fpHtml}${space}${removeKeyBtn}
`; html += `
`; } Xss.sanitizeAppend('.key_list', html); diff --git a/extension/chrome/settings/modules/experimental.ts b/extension/chrome/settings/modules/experimental.ts index 24f6fff2673..8fc038fee9a 100644 --- a/extension/chrome/settings/modules/experimental.ts +++ b/extension/chrome/settings/modules/experimental.ts @@ -144,7 +144,6 @@ View.run(class ExperimentalView extends View { for (const keyinfo of keyinfos) { text.push(''); text.push('key_longid: ' + keyinfo.longid); - text.push('key_primary: ' + keyinfo.primary); text.push(keyinfo.private); } text.push(''); diff --git a/extension/js/common/core/crypto/key.ts b/extension/js/common/core/crypto/key.ts index 14e6a1fa28a..31cf753560f 100644 --- a/extension/js/common/core/crypto/key.ts +++ b/extension/js/common/core/crypto/key.ts @@ -76,7 +76,6 @@ export interface KeyInfo extends PrvKeyInfo { // this cannot be Pubkey has it's being passed to localstorage public: string; fingerprint: string; - primary: boolean; } export type PrvPacket = (OpenPGP.packet.SecretKey | OpenPGP.packet.SecretSubkey); diff --git a/extension/js/common/platform/store/key-store.ts b/extension/js/common/platform/store/key-store.ts index c7ef08de98c..a48958d19fe 100644 --- a/extension/js/common/platform/store/key-store.ts +++ b/extension/js/common/platform/store/key-store.ts @@ -18,9 +18,6 @@ export class KeyStore extends AbstractStore { return keys; } return keys.filter(ki => { - if (fingerprints.includes('primary') && ki.primary) { - return true; - } if (fingerprints.includes(ki.fingerprint)) { return true; } @@ -45,12 +42,12 @@ export class KeyStore extends AbstractStore { } for (const i in keyinfos) { if (prv.id === keyinfos[i].fingerprint) { // replacing a key - keyinfos[i] = await KeyStore.keyInfoObj(prv, keyinfos[i].primary); + keyinfos[i] = await KeyStore.keyInfoObj(prv); updated = true; } } if (!updated) { - keyinfos.push(await KeyStore.keyInfoObj(prv, keyinfos.length === 0)); + keyinfos.push(await KeyStore.keyInfoObj(prv)); } await AcctStore.set(acctEmail, { keys: keyinfos }); } @@ -75,10 +72,10 @@ export class KeyStore extends AbstractStore { return result; } - public static keyInfoObj = async (prv: Key, primary = false): Promise => { + public static keyInfoObj = async (prv: Key): Promise => { const pubArmor = KeyUtil.armor(await KeyUtil.asPublicKey(prv)); const longid = OpenPGPKey.fingerprintToLongid(prv.id); - return { private: KeyUtil.armor(prv), public: pubArmor, primary, longid, fingerprint: prv.id }; + return { private: KeyUtil.armor(prv), public: pubArmor, longid, fingerprint: prv.id }; } } From f916f6da38f939afc0c91a99eedc0f382a62d104 Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Mon, 26 Oct 2020 09:21:47 +0300 Subject: [PATCH 04/19] Rename 'myPrivateKeys' field to 'keysWeNeedPassPhraseFor'. --- extension/chrome/elements/passphrase.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/extension/chrome/elements/passphrase.ts b/extension/chrome/elements/passphrase.ts index 5d83babd825..23732dc1237 100644 --- a/extension/chrome/elements/passphrase.ts +++ b/extension/chrome/elements/passphrase.ts @@ -20,7 +20,7 @@ View.run(class PassphraseView extends View { private readonly parentTabId: string; private readonly longids: string[]; private readonly type: string; - private myPrivateKeys: KeyInfo[] | undefined; + private keysWeNeedPassPhraseFor: KeyInfo[] | undefined; constructor() { super(); @@ -35,7 +35,7 @@ View.run(class PassphraseView extends View { Ui.event.protect(); await initPassphraseToggle(['passphrase']); const allPrivateKeys = await KeyStore.get(this.acctEmail); - this.myPrivateKeys = allPrivateKeys.filter(ki => this.longids.includes(ki.longid)); + this.keysWeNeedPassPhraseFor = allPrivateKeys.filter(ki => this.longids.includes(ki.longid)); if (this.type === 'embedded') { $('h1').parent().css('display', 'none'); $('div.separator').css('display', 'none'); @@ -55,12 +55,12 @@ View.run(class PassphraseView extends View { $('#passphrase').focus(); if (allPrivateKeys.length > 1) { let html: string; - if (this.myPrivateKeys.length === 1) { - html = `For key Fingerprint: ${Xss.escape(Str.spaced(this.myPrivateKeys[0].fingerprint || ''))}`; + if (this.keysWeNeedPassPhraseFor.length === 1) { + html = `For key Fingerprint: ${Xss.escape(Str.spaced(this.keysWeNeedPassPhraseFor[0].fingerprint || ''))}`; } else { html = 'Pass phrase needed for any of the following keys:'; - for (const i of this.myPrivateKeys.keys()) { - html += `
Fingerprint ${String(i + 1)}: ${Xss.escape(Str.spaced(this.myPrivateKeys[i].fingerprint) || '')}
`; + for (const i of this.keysWeNeedPassPhraseFor.keys()) { + html += `
Fingerprint ${String(i + 1)}: ${Xss.escape(Str.spaced(this.keysWeNeedPassPhraseFor[i].fingerprint) || '')}
`; } } Xss.sanitizeRender('.which_key', html); @@ -106,7 +106,7 @@ View.run(class PassphraseView extends View { const pass = String($('#passphrase').val()); const storageType: StorageType = $('.forget').prop('checked') ? 'session' : 'local'; let atLeastOneMatched = false; - for (const keyinfo of this.myPrivateKeys!) { // if passphrase matches more keys, it will save the pass phrase for all keys + for (const keyinfo of this.keysWeNeedPassPhraseFor!) { // if passphrase matches more keys, it will save the pass phrase for all keys const prv = await KeyUtil.parse(keyinfo.private); try { if (await KeyUtil.decrypt(prv, pass) === true) { From 2a58e6322fe96b087d5d5caa0a408b2354fe93d4 Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Mon, 26 Oct 2020 09:43:21 +0300 Subject: [PATCH 05/19] Disallow removing the last key. --- extension/chrome/settings/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/chrome/settings/index.ts b/extension/chrome/settings/index.ts index 2b4697028a3..cef69daa28c 100644 --- a/extension/chrome/settings/index.ts +++ b/extension/chrome/settings/index.ts @@ -426,7 +426,7 @@ View.run(class SettingsView extends View { const date = Str.monthName(created.getMonth()) + ' ' + created.getDate() + ', ' + created.getFullYear(); const escapedFp = Xss.escape(ki.fingerprint); let removeKeyBtn = ''; - if (canRemoveKey) { + if (canRemoveKey && privateKeys.length > 1) { removeKeyBtn = `(remove)`; } const escapedEmail = Xss.escape(prv.emails[0] || ''); From 403d09cce8b3f064d983f4613a6d63d03827a0ce Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Mon, 26 Oct 2020 10:42:21 +0300 Subject: [PATCH 06/19] Add KeyStore.getAll method. Refactor KeyStore.get(this.view.acctEmail, ['primary']) usages. --- .../elements/compose-modules/compose-render-module.ts | 2 +- .../elements/compose-modules/compose-storage-module.ts | 2 +- .../elements/pgp_block_modules/pgp-block-decrypt-module.ts | 2 +- .../chrome/settings/modules/backup-automatic-module.ts | 2 +- extension/chrome/settings/modules/backup-manual-module.ts | 2 +- extension/js/common/platform/store/key-store.ts | 6 ++++++ 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/extension/chrome/elements/compose-modules/compose-render-module.ts b/extension/chrome/elements/compose-modules/compose-render-module.ts index 842a2fd234d..913a82f72ea 100644 --- a/extension/chrome/elements/compose-modules/compose-render-module.ts +++ b/extension/chrome/elements/compose-modules/compose-render-module.ts @@ -198,7 +198,7 @@ export class ComposeRenderModule extends ViewModule {

I was not able to read your encrypted message because it was encrypted for a wrong key.

My current public key is attached below. Please update your records and send me a new encrypted message.

Thank you`); - const [primaryKi] = await KeyStore.get(this.view.acctEmail, ['primary']); + const primaryKi = await KeyStore.getFirst(this.view.acctEmail); const att = Att.keyinfoAsPubkeyAtt(primaryKi); this.view.attsModule.attach.addFile(new File([att.getData()], att.name)); this.view.sendBtnModule.popover.toggleItemTick($('.action-toggle-encrypt-sending-option'), 'encrypt', false); // don't encrypt diff --git a/extension/chrome/elements/compose-modules/compose-storage-module.ts b/extension/chrome/elements/compose-modules/compose-storage-module.ts index 424a4e069cf..1fb9e05c634 100644 --- a/extension/chrome/elements/compose-modules/compose-storage-module.ts +++ b/extension/chrome/elements/compose-modules/compose-storage-module.ts @@ -100,7 +100,7 @@ export class ComposeStorageModule extends ViewModule { public passphraseGet = async (senderKi?: KeyInfo) => { if (!senderKi) { - [senderKi] = await KeyStore.get(this.view.acctEmail, ['primary']); + senderKi = await KeyStore.getFirst(this.view.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(senderKi); } return await PassphraseStore.get(this.view.acctEmail, senderKi.fingerprint); diff --git a/extension/chrome/elements/pgp_block_modules/pgp-block-decrypt-module.ts b/extension/chrome/elements/pgp_block_modules/pgp-block-decrypt-module.ts index b74b399f912..7d3cb546ff0 100644 --- a/extension/chrome/elements/pgp_block_modules/pgp-block-decrypt-module.ts +++ b/extension/chrome/elements/pgp_block_modules/pgp-block-decrypt-module.ts @@ -93,7 +93,7 @@ export class PgpBlockViewDecryptModule { this.view.renderModule.renderText('Decrypting...'); await this.decryptAndRender(encryptedData, optionalPwd); } else { - const [primaryKi] = await KeyStore.get(this.view.acctEmail, ['primary']); + const primaryKi = await KeyStore.getFirst(this.view.acctEmail); if (!result.longids.chosen && !primaryKi) { await this.view.errorModule.renderErr(Lang.pgpBlock.notProperlySetUp + this.view.errorModule.btnHtml('FlowCrypt settings', 'green settings'), undefined); } else if (result.error.type === DecryptErrTypes.keyMismatch) { diff --git a/extension/chrome/settings/modules/backup-automatic-module.ts b/extension/chrome/settings/modules/backup-automatic-module.ts index 742e14ffd19..0891586650d 100644 --- a/extension/chrome/settings/modules/backup-automatic-module.ts +++ b/extension/chrome/settings/modules/backup-automatic-module.ts @@ -25,7 +25,7 @@ export class BackupAutomaticModule extends ViewModule { } private setupCreateSimpleAutomaticInboxBackup = async () => { - const [primaryKi] = await KeyStore.get(this.view.acctEmail, ['primary']); + const primaryKi = await KeyStore.getFirst(this.view.acctEmail); if (!(await KeyUtil.parse(primaryKi.private)).fullyEncrypted) { await Ui.modal.warning('Key not protected with a pass phrase, skipping'); throw new UnreportableError('Key not protected with a pass phrase, skipping'); diff --git a/extension/chrome/settings/modules/backup-manual-module.ts b/extension/chrome/settings/modules/backup-manual-module.ts index e09b1199c16..44e195e15dd 100644 --- a/extension/chrome/settings/modules/backup-manual-module.ts +++ b/extension/chrome/settings/modules/backup-manual-module.ts @@ -61,7 +61,7 @@ export class BackupManualActionModule extends ViewModule { private actionManualBackupHandler = async () => { const selected = $('input[type=radio][name=input_backup_choice]:checked').val(); - const [primaryKi] = await KeyStore.get(this.view.acctEmail, ['primary']); + const primaryKi = await KeyStore.getFirst(this.view.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(primaryKi); if (! await this.isPrivateKeyEncrypted(primaryKi)) { await Ui.modal.error('Sorry, cannot back up private key because it\'s not protected with a pass phrase.'); diff --git a/extension/js/common/platform/store/key-store.ts b/extension/js/common/platform/store/key-store.ts index a48958d19fe..29c450f7b98 100644 --- a/extension/js/common/platform/store/key-store.ts +++ b/extension/js/common/platform/store/key-store.ts @@ -25,6 +25,12 @@ export class KeyStore extends AbstractStore { }); } + public static getFirst = async (acctEmail: string): Promise => { + const stored = await AcctStore.get(acctEmail, ['keys']); + const keys: KeyInfo[] = stored.keys || []; + return keys[0]; + } + public static getAllWithPp = async (acctEmail: string): Promise => { const keys = await KeyStore.get(acctEmail); for (const ki of keys) { From ea8d18df3344c263cf94ee6cc066fe00dd775deb Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Mon, 26 Oct 2020 10:54:56 +0300 Subject: [PATCH 07/19] Refactor the rest of ['primary'] usages. --- .../chrome/elements/compose-modules/compose-draft-module.ts | 5 +++-- extension/chrome/settings/modules/change_passphrase.ts | 2 +- extension/chrome/settings/modules/keyserver.ts | 4 ++-- extension/chrome/settings/modules/security.ts | 2 +- extension/chrome/settings/modules/test_passphrase.ts | 2 +- extension/chrome/settings/setup.ts | 2 +- extension/js/common/api/email-provider/sendable-msg.ts | 2 +- extension/js/common/assert.ts | 2 +- 8 files changed, 11 insertions(+), 10 deletions(-) diff --git a/extension/chrome/elements/compose-modules/compose-draft-module.ts b/extension/chrome/elements/compose-modules/compose-draft-module.ts index c160b9daaa7..39f2060bf9d 100644 --- a/extension/chrome/elements/compose-modules/compose-draft-module.ts +++ b/extension/chrome/elements/compose-modules/compose-draft-module.ts @@ -315,8 +315,9 @@ export class ComposeDraftModule extends ViewModule { } else { Xss.sanitizeRender(this.view.S.cached('prompt'), `${promptText}

close`).css({ display: 'block', height: '100%' }); } - this.view.S.cached('prompt').find('a.action_open_passphrase_dialog').click(this.view.setHandler(() => { - BrowserMsg.send.passphraseDialog(this.view.parentTabId, { type: 'draft', longids: ['primary'] }); + this.view.S.cached('prompt').find('a.action_open_passphrase_dialog').click(this.view.setHandler(async () => { + const primaryKi = await KeyStore.getFirst(this.view.acctEmail); + BrowserMsg.send.passphraseDialog(this.view.parentTabId, { type: 'draft', longids: [primaryKi.longid] }); })); this.view.S.cached('prompt').find('a.action_close').click(this.view.setHandler(() => this.view.renderModule.closeMsg())); await this.view.storageModule.whenMasterPassphraseEntered(); diff --git a/extension/chrome/settings/modules/change_passphrase.ts b/extension/chrome/settings/modules/change_passphrase.ts index 00dec66fa1e..956f09b7b10 100644 --- a/extension/chrome/settings/modules/change_passphrase.ts +++ b/extension/chrome/settings/modules/change_passphrase.ts @@ -38,7 +38,7 @@ View.run(class ChangePassPhraseView extends View { $('#step_0_enter_current #current_pass_phrase').attr('placeholder', 'Current primary key pass phrase'); $('#step_1_enter_new #new_pass_phrase').attr('placeholder', 'Enter a new primary key pass phrase'); } - const [primaryKi] = await KeyStore.get(this.acctEmail, ['primary']); + const primaryKi = await KeyStore.getFirst(this.acctEmail); this.primaryKi = primaryKi; Assert.abortAndRenderErrorIfKeyinfoEmpty(this.primaryKi); const storedOrSessionPp = await PassphraseStore.get(this.acctEmail, this.primaryKi.fingerprint); diff --git a/extension/chrome/settings/modules/keyserver.ts b/extension/chrome/settings/modules/keyserver.ts index 59d1611c7dd..d43dd5ee9d1 100644 --- a/extension/chrome/settings/modules/keyserver.ts +++ b/extension/chrome/settings/modules/keyserver.ts @@ -80,7 +80,7 @@ View.run(class KeyserverView extends View { return await Ui.modal.error('Disallowed by your organisation rules'); } Xss.sanitizeRender(target, Ui.spinner('white')); - const [primaryKi] = await KeyStore.get(this.acctEmail, ['primary']); + const primaryKi = await KeyStore.getFirst(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(primaryKi); try { await this.pubLookup.attester.initialLegacySubmit(String($(target).attr('email')), primaryKi.public); @@ -97,7 +97,7 @@ View.run(class KeyserverView extends View { return await Ui.modal.error('Disallowed by your organisation rules'); } Xss.sanitizeRender(target, Ui.spinner('white')); - const [primaryKi] = await KeyStore.get(this.acctEmail, ['primary']); + const primaryKi = await KeyStore.getFirst(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(primaryKi); try { const responseText = await this.pubLookup.attester.replacePubkey(String($(target).attr('email')), primaryKi.public); diff --git a/extension/chrome/settings/modules/security.ts b/extension/chrome/settings/modules/security.ts index 1f4780b5937..652db537225 100644 --- a/extension/chrome/settings/modules/security.ts +++ b/extension/chrome/settings/modules/security.ts @@ -37,7 +37,7 @@ View.run(class SecurityView extends View { public render = async () => { await initPassphraseToggle(['passphrase_entry']); - [this.primaryKi] = await KeyStore.get(this.acctEmail, ['primary']); + this.primaryKi = await KeyStore.getFirst(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(this.primaryKi); this.authInfo = await AcctStore.authInfo(this.acctEmail); const storage = await AcctStore.get(this.acctEmail, ['hide_message_password', 'outgoing_language']); diff --git a/extension/chrome/settings/modules/test_passphrase.ts b/extension/chrome/settings/modules/test_passphrase.ts index 79917ccf6ce..bac41c331d1 100644 --- a/extension/chrome/settings/modules/test_passphrase.ts +++ b/extension/chrome/settings/modules/test_passphrase.ts @@ -27,7 +27,7 @@ View.run(class TestPassphrase extends View { } public render = async () => { - const [keyInfo] = await KeyStore.get(this.acctEmail, ['primary']); + const keyInfo = await KeyStore.getFirst(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(keyInfo); await initPassphraseToggle(['password']); this.primaryKey = await KeyUtil.parse(keyInfo.private); diff --git a/extension/chrome/settings/setup.ts b/extension/chrome/settings/setup.ts index 41df736b0ca..2f6917850dd 100644 --- a/extension/chrome/settings/setup.ts +++ b/extension/chrome/settings/setup.ts @@ -183,7 +183,7 @@ export class SetupView extends View { } public submitPublicKeysAndFinalizeSetup = async ({ submit_main, submit_all }: { submit_main: boolean, submit_all: boolean }): Promise => { - const [primaryKi] = await KeyStore.get(this.acctEmail, ['primary']); + const primaryKi = await KeyStore.getFirst(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(primaryKi); try { await this.submitPublicKeyIfNeeded(primaryKi.public, { submit_main, submit_all }); diff --git a/extension/js/common/api/email-provider/sendable-msg.ts b/extension/js/common/api/email-provider/sendable-msg.ts index 7f89e8a3652..5512324bfb1 100644 --- a/extension/js/common/api/email-provider/sendable-msg.ts +++ b/extension/js/common/api/email-provider/sendable-msg.ts @@ -28,7 +28,7 @@ export class SendableMsg { public sign?: (signable: string) => Promise; public static create = async (acctEmail: string, { from, recipients, subject, body, atts, thread, type, isDraft }: SendableMsgDefinition): Promise => { - const [primaryKi] = await KeyStore.get(acctEmail, ['primary']); + const primaryKi = await KeyStore.getFirst(acctEmail); const headers: Dict = primaryKi ? { OpenPGP: `id=${primaryKi.longid}` } : {}; // todo - use autocrypt format return new SendableMsg( acctEmail, diff --git a/extension/js/common/assert.ts b/extension/js/common/assert.ts index a891e772bea..ad2b5679793 100644 --- a/extension/js/common/assert.ts +++ b/extension/js/common/assert.ts @@ -35,7 +35,7 @@ export class Assert { public static abortAndRenderErrOnUnprotectedKey = async (acctEmail?: string, tabId?: string) => { if (acctEmail) { - const [primaryKi] = await KeyStore.get(acctEmail, ['primary']); + const primaryKi = await KeyStore.getFirst(acctEmail); const { setup_done } = await AcctStore.get(acctEmail, ['setup_done']); if (setup_done && primaryKi && !(await KeyUtil.parse(primaryKi.private)).fullyEncrypted) { if (window.location.pathname === '/chrome/settings/index.htm') { From 1f72ceb0b9c5a63ffeff8f20a5f2788d0cb09fd7 Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Mon, 26 Oct 2020 11:24:25 +0300 Subject: [PATCH 08/19] Ensure KeyStore.getFirst returns a key or throws an error. --- extension/js/common/platform/store/key-store.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extension/js/common/platform/store/key-store.ts b/extension/js/common/platform/store/key-store.ts index 29c450f7b98..7bf4d9cc483 100644 --- a/extension/js/common/platform/store/key-store.ts +++ b/extension/js/common/platform/store/key-store.ts @@ -5,6 +5,7 @@ import { AcctStore } from './acct-store.js'; import { PassphraseStore } from './passphrase-store.js'; import { AbstractStore } from './abstract-store.js'; import { OpenPGPKey } from '../../core/crypto/pgp/openpgp-key.js'; +import { Assert } from '../../../common/assert.js'; /** * Local store of account private keys @@ -28,7 +29,9 @@ export class KeyStore extends AbstractStore { public static getFirst = async (acctEmail: string): Promise => { const stored = await AcctStore.get(acctEmail, ['keys']); const keys: KeyInfo[] = stored.keys || []; - return keys[0]; + const firstKey = keys[0]; + Assert.abortAndRenderErrorIfKeyinfoEmpty(firstKey); + return firstKey; } public static getAllWithPp = async (acctEmail: string): Promise => { From 388cca3057ccaf6339c02ff940cc253c20533446 Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Mon, 26 Oct 2020 11:29:12 +0300 Subject: [PATCH 09/19] Refactor KeyStore.get method. --- extension/js/common/platform/store/key-store.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/extension/js/common/platform/store/key-store.ts b/extension/js/common/platform/store/key-store.ts index 7bf4d9cc483..acabe4a5afb 100644 --- a/extension/js/common/platform/store/key-store.ts +++ b/extension/js/common/platform/store/key-store.ts @@ -18,12 +18,7 @@ export class KeyStore extends AbstractStore { if (!fingerprints) { return keys; } - return keys.filter(ki => { - if (fingerprints.includes(ki.fingerprint)) { - return true; - } - return false; - }); + return keys.filter(ki => fingerprints.includes(ki.fingerprint)); } public static getFirst = async (acctEmail: string): Promise => { From 26f90a9d52fde76a5a77acad986cca3851ad4ea3 Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Mon, 26 Oct 2020 12:11:36 +0300 Subject: [PATCH 10/19] Rename KeyStore.getFirst to .getFirstOrRenderError. --- .../chrome/elements/compose-modules/compose-draft-module.ts | 2 +- .../chrome/elements/compose-modules/compose-render-module.ts | 2 +- .../chrome/elements/compose-modules/compose-storage-module.ts | 2 +- .../elements/pgp_block_modules/pgp-block-decrypt-module.ts | 2 +- extension/chrome/settings/modules/backup-automatic-module.ts | 2 +- extension/chrome/settings/modules/backup-manual-module.ts | 2 +- extension/chrome/settings/modules/change_passphrase.ts | 2 +- extension/chrome/settings/modules/keyserver.ts | 4 ++-- extension/chrome/settings/modules/security.ts | 2 +- extension/chrome/settings/modules/test_passphrase.ts | 2 +- extension/chrome/settings/setup.ts | 2 +- extension/js/common/api/email-provider/sendable-msg.ts | 2 +- extension/js/common/assert.ts | 2 +- extension/js/common/platform/store/key-store.ts | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) diff --git a/extension/chrome/elements/compose-modules/compose-draft-module.ts b/extension/chrome/elements/compose-modules/compose-draft-module.ts index 39f2060bf9d..277119dce16 100644 --- a/extension/chrome/elements/compose-modules/compose-draft-module.ts +++ b/extension/chrome/elements/compose-modules/compose-draft-module.ts @@ -316,7 +316,7 @@ export class ComposeDraftModule extends ViewModule { Xss.sanitizeRender(this.view.S.cached('prompt'), `${promptText}

close`).css({ display: 'block', height: '100%' }); } this.view.S.cached('prompt').find('a.action_open_passphrase_dialog').click(this.view.setHandler(async () => { - const primaryKi = await KeyStore.getFirst(this.view.acctEmail); + const primaryKi = await KeyStore.getFirstOrRenderError(this.view.acctEmail); BrowserMsg.send.passphraseDialog(this.view.parentTabId, { type: 'draft', longids: [primaryKi.longid] }); })); this.view.S.cached('prompt').find('a.action_close').click(this.view.setHandler(() => this.view.renderModule.closeMsg())); diff --git a/extension/chrome/elements/compose-modules/compose-render-module.ts b/extension/chrome/elements/compose-modules/compose-render-module.ts index 913a82f72ea..8d40c2904cf 100644 --- a/extension/chrome/elements/compose-modules/compose-render-module.ts +++ b/extension/chrome/elements/compose-modules/compose-render-module.ts @@ -198,7 +198,7 @@ export class ComposeRenderModule extends ViewModule {

I was not able to read your encrypted message because it was encrypted for a wrong key.

My current public key is attached below. Please update your records and send me a new encrypted message.

Thank you`); - const primaryKi = await KeyStore.getFirst(this.view.acctEmail); + const primaryKi = await KeyStore.getFirstOrRenderError(this.view.acctEmail); const att = Att.keyinfoAsPubkeyAtt(primaryKi); this.view.attsModule.attach.addFile(new File([att.getData()], att.name)); this.view.sendBtnModule.popover.toggleItemTick($('.action-toggle-encrypt-sending-option'), 'encrypt', false); // don't encrypt diff --git a/extension/chrome/elements/compose-modules/compose-storage-module.ts b/extension/chrome/elements/compose-modules/compose-storage-module.ts index 1fb9e05c634..23e67862c11 100644 --- a/extension/chrome/elements/compose-modules/compose-storage-module.ts +++ b/extension/chrome/elements/compose-modules/compose-storage-module.ts @@ -100,7 +100,7 @@ export class ComposeStorageModule extends ViewModule { public passphraseGet = async (senderKi?: KeyInfo) => { if (!senderKi) { - senderKi = await KeyStore.getFirst(this.view.acctEmail); + senderKi = await KeyStore.getFirstOrRenderError(this.view.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(senderKi); } return await PassphraseStore.get(this.view.acctEmail, senderKi.fingerprint); diff --git a/extension/chrome/elements/pgp_block_modules/pgp-block-decrypt-module.ts b/extension/chrome/elements/pgp_block_modules/pgp-block-decrypt-module.ts index 7d3cb546ff0..f22410b8e48 100644 --- a/extension/chrome/elements/pgp_block_modules/pgp-block-decrypt-module.ts +++ b/extension/chrome/elements/pgp_block_modules/pgp-block-decrypt-module.ts @@ -93,7 +93,7 @@ export class PgpBlockViewDecryptModule { this.view.renderModule.renderText('Decrypting...'); await this.decryptAndRender(encryptedData, optionalPwd); } else { - const primaryKi = await KeyStore.getFirst(this.view.acctEmail); + const primaryKi = await KeyStore.getFirstOrRenderError(this.view.acctEmail); if (!result.longids.chosen && !primaryKi) { await this.view.errorModule.renderErr(Lang.pgpBlock.notProperlySetUp + this.view.errorModule.btnHtml('FlowCrypt settings', 'green settings'), undefined); } else if (result.error.type === DecryptErrTypes.keyMismatch) { diff --git a/extension/chrome/settings/modules/backup-automatic-module.ts b/extension/chrome/settings/modules/backup-automatic-module.ts index 0891586650d..9cf67ceb10f 100644 --- a/extension/chrome/settings/modules/backup-automatic-module.ts +++ b/extension/chrome/settings/modules/backup-automatic-module.ts @@ -25,7 +25,7 @@ export class BackupAutomaticModule extends ViewModule { } private setupCreateSimpleAutomaticInboxBackup = async () => { - const primaryKi = await KeyStore.getFirst(this.view.acctEmail); + const primaryKi = await KeyStore.getFirstOrRenderError(this.view.acctEmail); if (!(await KeyUtil.parse(primaryKi.private)).fullyEncrypted) { await Ui.modal.warning('Key not protected with a pass phrase, skipping'); throw new UnreportableError('Key not protected with a pass phrase, skipping'); diff --git a/extension/chrome/settings/modules/backup-manual-module.ts b/extension/chrome/settings/modules/backup-manual-module.ts index 44e195e15dd..30f01b66406 100644 --- a/extension/chrome/settings/modules/backup-manual-module.ts +++ b/extension/chrome/settings/modules/backup-manual-module.ts @@ -61,7 +61,7 @@ export class BackupManualActionModule extends ViewModule { private actionManualBackupHandler = async () => { const selected = $('input[type=radio][name=input_backup_choice]:checked').val(); - const primaryKi = await KeyStore.getFirst(this.view.acctEmail); + const primaryKi = await KeyStore.getFirstOrRenderError(this.view.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(primaryKi); if (! await this.isPrivateKeyEncrypted(primaryKi)) { await Ui.modal.error('Sorry, cannot back up private key because it\'s not protected with a pass phrase.'); diff --git a/extension/chrome/settings/modules/change_passphrase.ts b/extension/chrome/settings/modules/change_passphrase.ts index 956f09b7b10..cf18c8ca350 100644 --- a/extension/chrome/settings/modules/change_passphrase.ts +++ b/extension/chrome/settings/modules/change_passphrase.ts @@ -38,7 +38,7 @@ View.run(class ChangePassPhraseView extends View { $('#step_0_enter_current #current_pass_phrase').attr('placeholder', 'Current primary key pass phrase'); $('#step_1_enter_new #new_pass_phrase').attr('placeholder', 'Enter a new primary key pass phrase'); } - const primaryKi = await KeyStore.getFirst(this.acctEmail); + const primaryKi = await KeyStore.getFirstOrRenderError(this.acctEmail); this.primaryKi = primaryKi; Assert.abortAndRenderErrorIfKeyinfoEmpty(this.primaryKi); const storedOrSessionPp = await PassphraseStore.get(this.acctEmail, this.primaryKi.fingerprint); diff --git a/extension/chrome/settings/modules/keyserver.ts b/extension/chrome/settings/modules/keyserver.ts index d43dd5ee9d1..cad6d2e59af 100644 --- a/extension/chrome/settings/modules/keyserver.ts +++ b/extension/chrome/settings/modules/keyserver.ts @@ -80,7 +80,7 @@ View.run(class KeyserverView extends View { return await Ui.modal.error('Disallowed by your organisation rules'); } Xss.sanitizeRender(target, Ui.spinner('white')); - const primaryKi = await KeyStore.getFirst(this.acctEmail); + const primaryKi = await KeyStore.getFirstOrRenderError(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(primaryKi); try { await this.pubLookup.attester.initialLegacySubmit(String($(target).attr('email')), primaryKi.public); @@ -97,7 +97,7 @@ View.run(class KeyserverView extends View { return await Ui.modal.error('Disallowed by your organisation rules'); } Xss.sanitizeRender(target, Ui.spinner('white')); - const primaryKi = await KeyStore.getFirst(this.acctEmail); + const primaryKi = await KeyStore.getFirstOrRenderError(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(primaryKi); try { const responseText = await this.pubLookup.attester.replacePubkey(String($(target).attr('email')), primaryKi.public); diff --git a/extension/chrome/settings/modules/security.ts b/extension/chrome/settings/modules/security.ts index 652db537225..93cf1947450 100644 --- a/extension/chrome/settings/modules/security.ts +++ b/extension/chrome/settings/modules/security.ts @@ -37,7 +37,7 @@ View.run(class SecurityView extends View { public render = async () => { await initPassphraseToggle(['passphrase_entry']); - this.primaryKi = await KeyStore.getFirst(this.acctEmail); + this.primaryKi = await KeyStore.getFirstOrRenderError(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(this.primaryKi); this.authInfo = await AcctStore.authInfo(this.acctEmail); const storage = await AcctStore.get(this.acctEmail, ['hide_message_password', 'outgoing_language']); diff --git a/extension/chrome/settings/modules/test_passphrase.ts b/extension/chrome/settings/modules/test_passphrase.ts index bac41c331d1..7c1ad4e375f 100644 --- a/extension/chrome/settings/modules/test_passphrase.ts +++ b/extension/chrome/settings/modules/test_passphrase.ts @@ -27,7 +27,7 @@ View.run(class TestPassphrase extends View { } public render = async () => { - const keyInfo = await KeyStore.getFirst(this.acctEmail); + const keyInfo = await KeyStore.getFirstOrRenderError(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(keyInfo); await initPassphraseToggle(['password']); this.primaryKey = await KeyUtil.parse(keyInfo.private); diff --git a/extension/chrome/settings/setup.ts b/extension/chrome/settings/setup.ts index 2f6917850dd..a78ec625876 100644 --- a/extension/chrome/settings/setup.ts +++ b/extension/chrome/settings/setup.ts @@ -183,7 +183,7 @@ export class SetupView extends View { } public submitPublicKeysAndFinalizeSetup = async ({ submit_main, submit_all }: { submit_main: boolean, submit_all: boolean }): Promise => { - const primaryKi = await KeyStore.getFirst(this.acctEmail); + const primaryKi = await KeyStore.getFirstOrRenderError(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(primaryKi); try { await this.submitPublicKeyIfNeeded(primaryKi.public, { submit_main, submit_all }); diff --git a/extension/js/common/api/email-provider/sendable-msg.ts b/extension/js/common/api/email-provider/sendable-msg.ts index 5512324bfb1..d11a60d905d 100644 --- a/extension/js/common/api/email-provider/sendable-msg.ts +++ b/extension/js/common/api/email-provider/sendable-msg.ts @@ -28,7 +28,7 @@ export class SendableMsg { public sign?: (signable: string) => Promise; public static create = async (acctEmail: string, { from, recipients, subject, body, atts, thread, type, isDraft }: SendableMsgDefinition): Promise => { - const primaryKi = await KeyStore.getFirst(acctEmail); + const primaryKi = await KeyStore.getFirstOrRenderError(acctEmail); const headers: Dict = primaryKi ? { OpenPGP: `id=${primaryKi.longid}` } : {}; // todo - use autocrypt format return new SendableMsg( acctEmail, diff --git a/extension/js/common/assert.ts b/extension/js/common/assert.ts index ad2b5679793..d6da6411b79 100644 --- a/extension/js/common/assert.ts +++ b/extension/js/common/assert.ts @@ -35,7 +35,7 @@ export class Assert { public static abortAndRenderErrOnUnprotectedKey = async (acctEmail?: string, tabId?: string) => { if (acctEmail) { - const primaryKi = await KeyStore.getFirst(acctEmail); + const primaryKi = await KeyStore.getFirstOrRenderError(acctEmail); const { setup_done } = await AcctStore.get(acctEmail, ['setup_done']); if (setup_done && primaryKi && !(await KeyUtil.parse(primaryKi.private)).fullyEncrypted) { if (window.location.pathname === '/chrome/settings/index.htm') { diff --git a/extension/js/common/platform/store/key-store.ts b/extension/js/common/platform/store/key-store.ts index acabe4a5afb..ff5c9b7d7ca 100644 --- a/extension/js/common/platform/store/key-store.ts +++ b/extension/js/common/platform/store/key-store.ts @@ -21,7 +21,7 @@ export class KeyStore extends AbstractStore { return keys.filter(ki => fingerprints.includes(ki.fingerprint)); } - public static getFirst = async (acctEmail: string): Promise => { + public static getFirstOrRenderError = async (acctEmail: string): Promise => { const stored = await AcctStore.get(acctEmail, ['keys']); const keys: KeyInfo[] = stored.keys || []; const firstKey = keys[0]; From 099774db69673137df1fc7f754457c89bc6d75cb Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Fri, 30 Oct 2020 16:01:46 +0300 Subject: [PATCH 11/19] Revert KeyStore.getFirst -> getFirstOrRenderError (lots of tests were failing). --- .../elements/compose-modules/compose-draft-module.ts | 2 +- .../elements/compose-modules/compose-render-module.ts | 2 +- .../elements/compose-modules/compose-storage-module.ts | 2 +- .../elements/pgp_block_modules/pgp-block-decrypt-module.ts | 2 +- .../chrome/settings/modules/backup-automatic-module.ts | 2 +- extension/chrome/settings/modules/backup-manual-module.ts | 2 +- extension/chrome/settings/modules/change_passphrase.ts | 2 +- extension/chrome/settings/modules/keyserver.ts | 4 ++-- extension/chrome/settings/modules/security.ts | 2 +- extension/chrome/settings/modules/test_passphrase.ts | 2 +- extension/chrome/settings/setup.ts | 2 +- extension/js/common/api/email-provider/sendable-msg.ts | 2 +- extension/js/common/assert.ts | 2 +- extension/js/common/platform/store/key-store.ts | 7 ++----- 14 files changed, 16 insertions(+), 19 deletions(-) diff --git a/extension/chrome/elements/compose-modules/compose-draft-module.ts b/extension/chrome/elements/compose-modules/compose-draft-module.ts index 277119dce16..39f2060bf9d 100644 --- a/extension/chrome/elements/compose-modules/compose-draft-module.ts +++ b/extension/chrome/elements/compose-modules/compose-draft-module.ts @@ -316,7 +316,7 @@ export class ComposeDraftModule extends ViewModule { Xss.sanitizeRender(this.view.S.cached('prompt'), `${promptText}

close`).css({ display: 'block', height: '100%' }); } this.view.S.cached('prompt').find('a.action_open_passphrase_dialog').click(this.view.setHandler(async () => { - const primaryKi = await KeyStore.getFirstOrRenderError(this.view.acctEmail); + const primaryKi = await KeyStore.getFirst(this.view.acctEmail); BrowserMsg.send.passphraseDialog(this.view.parentTabId, { type: 'draft', longids: [primaryKi.longid] }); })); this.view.S.cached('prompt').find('a.action_close').click(this.view.setHandler(() => this.view.renderModule.closeMsg())); diff --git a/extension/chrome/elements/compose-modules/compose-render-module.ts b/extension/chrome/elements/compose-modules/compose-render-module.ts index 8d40c2904cf..913a82f72ea 100644 --- a/extension/chrome/elements/compose-modules/compose-render-module.ts +++ b/extension/chrome/elements/compose-modules/compose-render-module.ts @@ -198,7 +198,7 @@ export class ComposeRenderModule extends ViewModule {

I was not able to read your encrypted message because it was encrypted for a wrong key.

My current public key is attached below. Please update your records and send me a new encrypted message.

Thank you`); - const primaryKi = await KeyStore.getFirstOrRenderError(this.view.acctEmail); + const primaryKi = await KeyStore.getFirst(this.view.acctEmail); const att = Att.keyinfoAsPubkeyAtt(primaryKi); this.view.attsModule.attach.addFile(new File([att.getData()], att.name)); this.view.sendBtnModule.popover.toggleItemTick($('.action-toggle-encrypt-sending-option'), 'encrypt', false); // don't encrypt diff --git a/extension/chrome/elements/compose-modules/compose-storage-module.ts b/extension/chrome/elements/compose-modules/compose-storage-module.ts index 23e67862c11..1fb9e05c634 100644 --- a/extension/chrome/elements/compose-modules/compose-storage-module.ts +++ b/extension/chrome/elements/compose-modules/compose-storage-module.ts @@ -100,7 +100,7 @@ export class ComposeStorageModule extends ViewModule { public passphraseGet = async (senderKi?: KeyInfo) => { if (!senderKi) { - senderKi = await KeyStore.getFirstOrRenderError(this.view.acctEmail); + senderKi = await KeyStore.getFirst(this.view.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(senderKi); } return await PassphraseStore.get(this.view.acctEmail, senderKi.fingerprint); diff --git a/extension/chrome/elements/pgp_block_modules/pgp-block-decrypt-module.ts b/extension/chrome/elements/pgp_block_modules/pgp-block-decrypt-module.ts index f22410b8e48..7d3cb546ff0 100644 --- a/extension/chrome/elements/pgp_block_modules/pgp-block-decrypt-module.ts +++ b/extension/chrome/elements/pgp_block_modules/pgp-block-decrypt-module.ts @@ -93,7 +93,7 @@ export class PgpBlockViewDecryptModule { this.view.renderModule.renderText('Decrypting...'); await this.decryptAndRender(encryptedData, optionalPwd); } else { - const primaryKi = await KeyStore.getFirstOrRenderError(this.view.acctEmail); + const primaryKi = await KeyStore.getFirst(this.view.acctEmail); if (!result.longids.chosen && !primaryKi) { await this.view.errorModule.renderErr(Lang.pgpBlock.notProperlySetUp + this.view.errorModule.btnHtml('FlowCrypt settings', 'green settings'), undefined); } else if (result.error.type === DecryptErrTypes.keyMismatch) { diff --git a/extension/chrome/settings/modules/backup-automatic-module.ts b/extension/chrome/settings/modules/backup-automatic-module.ts index 9cf67ceb10f..0891586650d 100644 --- a/extension/chrome/settings/modules/backup-automatic-module.ts +++ b/extension/chrome/settings/modules/backup-automatic-module.ts @@ -25,7 +25,7 @@ export class BackupAutomaticModule extends ViewModule { } private setupCreateSimpleAutomaticInboxBackup = async () => { - const primaryKi = await KeyStore.getFirstOrRenderError(this.view.acctEmail); + const primaryKi = await KeyStore.getFirst(this.view.acctEmail); if (!(await KeyUtil.parse(primaryKi.private)).fullyEncrypted) { await Ui.modal.warning('Key not protected with a pass phrase, skipping'); throw new UnreportableError('Key not protected with a pass phrase, skipping'); diff --git a/extension/chrome/settings/modules/backup-manual-module.ts b/extension/chrome/settings/modules/backup-manual-module.ts index 30f01b66406..44e195e15dd 100644 --- a/extension/chrome/settings/modules/backup-manual-module.ts +++ b/extension/chrome/settings/modules/backup-manual-module.ts @@ -61,7 +61,7 @@ export class BackupManualActionModule extends ViewModule { private actionManualBackupHandler = async () => { const selected = $('input[type=radio][name=input_backup_choice]:checked').val(); - const primaryKi = await KeyStore.getFirstOrRenderError(this.view.acctEmail); + const primaryKi = await KeyStore.getFirst(this.view.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(primaryKi); if (! await this.isPrivateKeyEncrypted(primaryKi)) { await Ui.modal.error('Sorry, cannot back up private key because it\'s not protected with a pass phrase.'); diff --git a/extension/chrome/settings/modules/change_passphrase.ts b/extension/chrome/settings/modules/change_passphrase.ts index cf18c8ca350..956f09b7b10 100644 --- a/extension/chrome/settings/modules/change_passphrase.ts +++ b/extension/chrome/settings/modules/change_passphrase.ts @@ -38,7 +38,7 @@ View.run(class ChangePassPhraseView extends View { $('#step_0_enter_current #current_pass_phrase').attr('placeholder', 'Current primary key pass phrase'); $('#step_1_enter_new #new_pass_phrase').attr('placeholder', 'Enter a new primary key pass phrase'); } - const primaryKi = await KeyStore.getFirstOrRenderError(this.acctEmail); + const primaryKi = await KeyStore.getFirst(this.acctEmail); this.primaryKi = primaryKi; Assert.abortAndRenderErrorIfKeyinfoEmpty(this.primaryKi); const storedOrSessionPp = await PassphraseStore.get(this.acctEmail, this.primaryKi.fingerprint); diff --git a/extension/chrome/settings/modules/keyserver.ts b/extension/chrome/settings/modules/keyserver.ts index cad6d2e59af..d43dd5ee9d1 100644 --- a/extension/chrome/settings/modules/keyserver.ts +++ b/extension/chrome/settings/modules/keyserver.ts @@ -80,7 +80,7 @@ View.run(class KeyserverView extends View { return await Ui.modal.error('Disallowed by your organisation rules'); } Xss.sanitizeRender(target, Ui.spinner('white')); - const primaryKi = await KeyStore.getFirstOrRenderError(this.acctEmail); + const primaryKi = await KeyStore.getFirst(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(primaryKi); try { await this.pubLookup.attester.initialLegacySubmit(String($(target).attr('email')), primaryKi.public); @@ -97,7 +97,7 @@ View.run(class KeyserverView extends View { return await Ui.modal.error('Disallowed by your organisation rules'); } Xss.sanitizeRender(target, Ui.spinner('white')); - const primaryKi = await KeyStore.getFirstOrRenderError(this.acctEmail); + const primaryKi = await KeyStore.getFirst(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(primaryKi); try { const responseText = await this.pubLookup.attester.replacePubkey(String($(target).attr('email')), primaryKi.public); diff --git a/extension/chrome/settings/modules/security.ts b/extension/chrome/settings/modules/security.ts index 93cf1947450..652db537225 100644 --- a/extension/chrome/settings/modules/security.ts +++ b/extension/chrome/settings/modules/security.ts @@ -37,7 +37,7 @@ View.run(class SecurityView extends View { public render = async () => { await initPassphraseToggle(['passphrase_entry']); - this.primaryKi = await KeyStore.getFirstOrRenderError(this.acctEmail); + this.primaryKi = await KeyStore.getFirst(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(this.primaryKi); this.authInfo = await AcctStore.authInfo(this.acctEmail); const storage = await AcctStore.get(this.acctEmail, ['hide_message_password', 'outgoing_language']); diff --git a/extension/chrome/settings/modules/test_passphrase.ts b/extension/chrome/settings/modules/test_passphrase.ts index 7c1ad4e375f..bac41c331d1 100644 --- a/extension/chrome/settings/modules/test_passphrase.ts +++ b/extension/chrome/settings/modules/test_passphrase.ts @@ -27,7 +27,7 @@ View.run(class TestPassphrase extends View { } public render = async () => { - const keyInfo = await KeyStore.getFirstOrRenderError(this.acctEmail); + const keyInfo = await KeyStore.getFirst(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(keyInfo); await initPassphraseToggle(['password']); this.primaryKey = await KeyUtil.parse(keyInfo.private); diff --git a/extension/chrome/settings/setup.ts b/extension/chrome/settings/setup.ts index a78ec625876..2f6917850dd 100644 --- a/extension/chrome/settings/setup.ts +++ b/extension/chrome/settings/setup.ts @@ -183,7 +183,7 @@ export class SetupView extends View { } public submitPublicKeysAndFinalizeSetup = async ({ submit_main, submit_all }: { submit_main: boolean, submit_all: boolean }): Promise => { - const primaryKi = await KeyStore.getFirstOrRenderError(this.acctEmail); + const primaryKi = await KeyStore.getFirst(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(primaryKi); try { await this.submitPublicKeyIfNeeded(primaryKi.public, { submit_main, submit_all }); diff --git a/extension/js/common/api/email-provider/sendable-msg.ts b/extension/js/common/api/email-provider/sendable-msg.ts index d11a60d905d..5512324bfb1 100644 --- a/extension/js/common/api/email-provider/sendable-msg.ts +++ b/extension/js/common/api/email-provider/sendable-msg.ts @@ -28,7 +28,7 @@ export class SendableMsg { public sign?: (signable: string) => Promise; public static create = async (acctEmail: string, { from, recipients, subject, body, atts, thread, type, isDraft }: SendableMsgDefinition): Promise => { - const primaryKi = await KeyStore.getFirstOrRenderError(acctEmail); + const primaryKi = await KeyStore.getFirst(acctEmail); const headers: Dict = primaryKi ? { OpenPGP: `id=${primaryKi.longid}` } : {}; // todo - use autocrypt format return new SendableMsg( acctEmail, diff --git a/extension/js/common/assert.ts b/extension/js/common/assert.ts index d6da6411b79..ad2b5679793 100644 --- a/extension/js/common/assert.ts +++ b/extension/js/common/assert.ts @@ -35,7 +35,7 @@ export class Assert { public static abortAndRenderErrOnUnprotectedKey = async (acctEmail?: string, tabId?: string) => { if (acctEmail) { - const primaryKi = await KeyStore.getFirstOrRenderError(acctEmail); + const primaryKi = await KeyStore.getFirst(acctEmail); const { setup_done } = await AcctStore.get(acctEmail, ['setup_done']); if (setup_done && primaryKi && !(await KeyUtil.parse(primaryKi.private)).fullyEncrypted) { if (window.location.pathname === '/chrome/settings/index.htm') { diff --git a/extension/js/common/platform/store/key-store.ts b/extension/js/common/platform/store/key-store.ts index ff5c9b7d7ca..a64ac2e0d16 100644 --- a/extension/js/common/platform/store/key-store.ts +++ b/extension/js/common/platform/store/key-store.ts @@ -5,7 +5,6 @@ import { AcctStore } from './acct-store.js'; import { PassphraseStore } from './passphrase-store.js'; import { AbstractStore } from './abstract-store.js'; import { OpenPGPKey } from '../../core/crypto/pgp/openpgp-key.js'; -import { Assert } from '../../../common/assert.js'; /** * Local store of account private keys @@ -21,12 +20,10 @@ export class KeyStore extends AbstractStore { return keys.filter(ki => fingerprints.includes(ki.fingerprint)); } - public static getFirstOrRenderError = async (acctEmail: string): Promise => { + public static getFirst = async (acctEmail: string): Promise => { const stored = await AcctStore.get(acctEmail, ['keys']); const keys: KeyInfo[] = stored.keys || []; - const firstKey = keys[0]; - Assert.abortAndRenderErrorIfKeyinfoEmpty(firstKey); - return firstKey; + return keys[0]; } public static getAllWithPp = async (acctEmail: string): Promise => { From f541b7838e1e5e4dbcf7513ca52ef6d6742e99db Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Fri, 30 Oct 2020 16:54:04 +0300 Subject: [PATCH 12/19] Fix failing 'settings - verify key presense 1pp1' test. --- extension/chrome/settings/modules/my_key.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/extension/chrome/settings/modules/my_key.ts b/extension/chrome/settings/modules/my_key.ts index 665e49158e6..0e910c85e07 100644 --- a/extension/chrome/settings/modules/my_key.ts +++ b/extension/chrome/settings/modules/my_key.ts @@ -24,7 +24,6 @@ declare const ClipboardJS: any; View.run(class MyKeyView extends View { private readonly acctEmail: string; - private readonly fingerprint: string; private readonly myKeyUserIdsUrl: string; private readonly myKeyUpdateUrl: string; private keyInfo!: KeyInfo; @@ -36,7 +35,6 @@ View.run(class MyKeyView extends View { super(); const uncheckedUrlParams = Url.parse(['acctEmail', 'fingerprint', 'parentTabId']); this.acctEmail = Assert.urlParamRequire.string(uncheckedUrlParams, 'acctEmail'); - this.fingerprint = Assert.urlParamRequire.optionalString(uncheckedUrlParams, 'fingerprint') || 'primary'; this.myKeyUserIdsUrl = Url.create('my_key_user_ids.htm', uncheckedUrlParams); this.myKeyUpdateUrl = Url.create('my_key_update.htm', uncheckedUrlParams); } @@ -44,9 +42,9 @@ View.run(class MyKeyView extends View { public render = async () => { this.orgRules = await OrgRules.newInstance(this.acctEmail); this.pubLookup = new PubLookup(this.orgRules); - [this.keyInfo] = await KeyStore.get(this.acctEmail, [this.fingerprint]); - this.pubKey = await KeyUtil.parse(this.keyInfo.public); + this.keyInfo = await KeyStore.getFirst(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(this.keyInfo); + this.pubKey = await KeyUtil.parse(this.keyInfo.public); $('.action_view_user_ids').attr('href', this.myKeyUserIdsUrl); $('.action_view_update').attr('href', this.myKeyUpdateUrl); $('.fingerprint').text(Str.spaced(this.keyInfo.fingerprint)); From 3a9265a8c3af735026c8f6220f3a4483d0d237ad Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Fri, 30 Oct 2020 18:21:05 +0300 Subject: [PATCH 13/19] Fix my key page issue properly. --- extension/chrome/settings/modules/my_key.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extension/chrome/settings/modules/my_key.ts b/extension/chrome/settings/modules/my_key.ts index 0e910c85e07..eedf56d49c0 100644 --- a/extension/chrome/settings/modules/my_key.ts +++ b/extension/chrome/settings/modules/my_key.ts @@ -24,6 +24,7 @@ declare const ClipboardJS: any; View.run(class MyKeyView extends View { private readonly acctEmail: string; + private readonly fingerprint: string | undefined; private readonly myKeyUserIdsUrl: string; private readonly myKeyUpdateUrl: string; private keyInfo!: KeyInfo; @@ -35,6 +36,7 @@ View.run(class MyKeyView extends View { super(); const uncheckedUrlParams = Url.parse(['acctEmail', 'fingerprint', 'parentTabId']); this.acctEmail = Assert.urlParamRequire.string(uncheckedUrlParams, 'acctEmail'); + this.fingerprint = Assert.urlParamRequire.optionalString(uncheckedUrlParams, 'fingerprint'); this.myKeyUserIdsUrl = Url.create('my_key_user_ids.htm', uncheckedUrlParams); this.myKeyUpdateUrl = Url.create('my_key_update.htm', uncheckedUrlParams); } @@ -42,7 +44,7 @@ View.run(class MyKeyView extends View { public render = async () => { this.orgRules = await OrgRules.newInstance(this.acctEmail); this.pubLookup = new PubLookup(this.orgRules); - this.keyInfo = await KeyStore.getFirst(this.acctEmail); + [this.keyInfo] = await KeyStore.get(this.acctEmail, [this.fingerprint || await (await KeyStore.getFirst(this.acctEmail)).fingerprint]); Assert.abortAndRenderErrorIfKeyinfoEmpty(this.keyInfo); this.pubKey = await KeyUtil.parse(this.keyInfo.public); $('.action_view_user_ids').attr('href', this.myKeyUserIdsUrl); From e03f73bf714138779358b31abc52da390c5abab9 Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Fri, 30 Oct 2020 20:22:00 +0300 Subject: [PATCH 14/19] Fix `compose - own key expired` failing test. --- extension/chrome/settings/modules/my_key_update.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/extension/chrome/settings/modules/my_key_update.ts b/extension/chrome/settings/modules/my_key_update.ts index 7c79e963adc..87fe6dec29b 100644 --- a/extension/chrome/settings/modules/my_key_update.ts +++ b/extension/chrome/settings/modules/my_key_update.ts @@ -19,7 +19,6 @@ import { PassphraseStore } from '../../../js/common/platform/store/passphrase-st View.run(class MyKeyUpdateView extends View { private readonly acctEmail: string; - private readonly fingerprint: string; private readonly showKeyUrl: string; private readonly inputPrivateKey = $('.input_private_key'); private readonly prvHeaders = PgpArmor.headers('privateKey'); @@ -31,14 +30,13 @@ View.run(class MyKeyUpdateView extends View { super(); const uncheckedUrlParams = Url.parse(['acctEmail', 'fingerprint', 'parentTabId']); this.acctEmail = Assert.urlParamRequire.string(uncheckedUrlParams, 'acctEmail'); - this.fingerprint = Assert.urlParamRequire.optionalString(uncheckedUrlParams, 'fingerprint') || 'primary'; this.showKeyUrl = Url.create('my_key.htm', uncheckedUrlParams); } public render = async () => { this.orgRules = await OrgRules.newInstance(this.acctEmail); this.pubLookup = new PubLookup(this.orgRules); - [this.primaryKi] = await KeyStore.get(this.acctEmail, [this.fingerprint]); + this.primaryKi = await KeyStore.getFirst(this.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(this.primaryKi); $('.action_show_public_key').attr('href', this.showKeyUrl); $('.email').text(this.acctEmail); From 7c4832b0ff4b66e509911378994d44184d339b1a Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Mon, 2 Nov 2020 10:34:42 +0300 Subject: [PATCH 15/19] Made my_key page fingerprint url param required. --- extension/chrome/settings/modules/my_key.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extension/chrome/settings/modules/my_key.ts b/extension/chrome/settings/modules/my_key.ts index eedf56d49c0..0baa98c4229 100644 --- a/extension/chrome/settings/modules/my_key.ts +++ b/extension/chrome/settings/modules/my_key.ts @@ -24,7 +24,7 @@ declare const ClipboardJS: any; View.run(class MyKeyView extends View { private readonly acctEmail: string; - private readonly fingerprint: string | undefined; + private readonly fingerprint: string; private readonly myKeyUserIdsUrl: string; private readonly myKeyUpdateUrl: string; private keyInfo!: KeyInfo; @@ -36,7 +36,7 @@ View.run(class MyKeyView extends View { super(); const uncheckedUrlParams = Url.parse(['acctEmail', 'fingerprint', 'parentTabId']); this.acctEmail = Assert.urlParamRequire.string(uncheckedUrlParams, 'acctEmail'); - this.fingerprint = Assert.urlParamRequire.optionalString(uncheckedUrlParams, 'fingerprint'); + this.fingerprint = Assert.urlParamRequire.string(uncheckedUrlParams, 'fingerprint'); this.myKeyUserIdsUrl = Url.create('my_key_user_ids.htm', uncheckedUrlParams); this.myKeyUpdateUrl = Url.create('my_key_update.htm', uncheckedUrlParams); } @@ -44,7 +44,7 @@ View.run(class MyKeyView extends View { public render = async () => { this.orgRules = await OrgRules.newInstance(this.acctEmail); this.pubLookup = new PubLookup(this.orgRules); - [this.keyInfo] = await KeyStore.get(this.acctEmail, [this.fingerprint || await (await KeyStore.getFirst(this.acctEmail)).fingerprint]); + [this.keyInfo] = await KeyStore.get(this.acctEmail, [this.fingerprint]); Assert.abortAndRenderErrorIfKeyinfoEmpty(this.keyInfo); this.pubKey = await KeyUtil.parse(this.keyInfo.public); $('.action_view_user_ids').attr('href', this.myKeyUserIdsUrl); From 15d20ece277e99898ac0971aff435a07fe7cd1bd Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Mon, 2 Nov 2020 10:51:27 +0300 Subject: [PATCH 16/19] Make fingerprint URL param required for my_key_update page. --- extension/chrome/settings/modules/my_key_update.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extension/chrome/settings/modules/my_key_update.ts b/extension/chrome/settings/modules/my_key_update.ts index 87fe6dec29b..0c2372df825 100644 --- a/extension/chrome/settings/modules/my_key_update.ts +++ b/extension/chrome/settings/modules/my_key_update.ts @@ -19,6 +19,7 @@ import { PassphraseStore } from '../../../js/common/platform/store/passphrase-st View.run(class MyKeyUpdateView extends View { private readonly acctEmail: string; + private readonly fingerprint: string; private readonly showKeyUrl: string; private readonly inputPrivateKey = $('.input_private_key'); private readonly prvHeaders = PgpArmor.headers('privateKey'); @@ -30,13 +31,14 @@ View.run(class MyKeyUpdateView extends View { super(); const uncheckedUrlParams = Url.parse(['acctEmail', 'fingerprint', 'parentTabId']); this.acctEmail = Assert.urlParamRequire.string(uncheckedUrlParams, 'acctEmail'); + this.fingerprint = Assert.urlParamRequire.string(uncheckedUrlParams, 'fingerprint'); this.showKeyUrl = Url.create('my_key.htm', uncheckedUrlParams); } public render = async () => { this.orgRules = await OrgRules.newInstance(this.acctEmail); this.pubLookup = new PubLookup(this.orgRules); - this.primaryKi = await KeyStore.getFirst(this.acctEmail); + [this.primaryKi] = await KeyStore.get(this.acctEmail, [this.fingerprint]); Assert.abortAndRenderErrorIfKeyinfoEmpty(this.primaryKi); $('.action_show_public_key').attr('href', this.showKeyUrl); $('.email').text(this.acctEmail); From 61b55ff2a9462be299ecc92a5ff1c8efdeff6f92 Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Mon, 2 Nov 2020 14:11:06 +0300 Subject: [PATCH 17/19] Fix my_key page issue. --- extension/chrome/settings/index.htm | 2 +- extension/chrome/settings/index.ts | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/extension/chrome/settings/index.htm b/extension/chrome/settings/index.htm index a7c3947ab92..3ebb9a2c39c 100644 --- a/extension/chrome/settings/index.htm +++ b/extension/chrome/settings/index.htm @@ -159,7 +159,7 @@

FlowCrypt Settings

- + Your Public Keys diff --git a/extension/chrome/settings/index.ts b/extension/chrome/settings/index.ts index 504aa2f10fd..9a2823352ec 100644 --- a/extension/chrome/settings/index.ts +++ b/extension/chrome/settings/index.ts @@ -149,6 +149,16 @@ View.run(class SettingsView extends View { Catch.report(`Unknown target page in element: ${target.outerHTML}`); } })); + $('.action_open_public_key_page').click(this.setHandler(async target => { + const page = $(target).attr('page'); + if (page) { + const ki = await KeyStore.getFirst(this.acctEmail!); + const escapedFp = Xss.escape(ki.fingerprint); + await Settings.renderSubPage(this.acctEmail!, this.tabId, page, `&fingerprint=${escapedFp}`); + } else { + Catch.report(`Unknown target page in element: ${target.outerHTML}`); + } + })); $('.action_show_encrypted_inbox').click(this.setHandler(target => { window.location.href = Url.create('/chrome/settings/inbox/inbox.htm', { acctEmail: this.acctEmail! }); })); From 71df972ae5999d8695643f78653eb59e1a6da020 Mon Sep 17 00:00:00 2001 From: Yan Takushevich Date: Mon, 2 Nov 2020 14:43:22 +0300 Subject: [PATCH 18/19] Refactor my_key page click handler. --- extension/chrome/settings/index.htm | 2 +- extension/chrome/settings/index.ts | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/extension/chrome/settings/index.htm b/extension/chrome/settings/index.htm index 3ebb9a2c39c..79d0eadb14c 100644 --- a/extension/chrome/settings/index.htm +++ b/extension/chrome/settings/index.htm @@ -159,7 +159,7 @@

FlowCrypt Settings

- + Your Public Keys diff --git a/extension/chrome/settings/index.ts b/extension/chrome/settings/index.ts index 9a2823352ec..3ac06990e12 100644 --- a/extension/chrome/settings/index.ts +++ b/extension/chrome/settings/index.ts @@ -150,14 +150,9 @@ View.run(class SettingsView extends View { } })); $('.action_open_public_key_page').click(this.setHandler(async target => { - const page = $(target).attr('page'); - if (page) { - const ki = await KeyStore.getFirst(this.acctEmail!); - const escapedFp = Xss.escape(ki.fingerprint); - await Settings.renderSubPage(this.acctEmail!, this.tabId, page, `&fingerprint=${escapedFp}`); - } else { - Catch.report(`Unknown target page in element: ${target.outerHTML}`); - } + const ki = await KeyStore.getFirst(this.acctEmail!); + const escapedFp = Xss.escape(ki.fingerprint); + await Settings.renderSubPage(this.acctEmail!, this.tabId, 'modules/my_key.htm', `&fingerprint=${escapedFp}`); })); $('.action_show_encrypted_inbox').click(this.setHandler(target => { window.location.href = Url.create('/chrome/settings/inbox/inbox.htm', { acctEmail: this.acctEmail! }); From 8ed4c6ea39c2f5e6cba269242414f1ee1477ccd8 Mon Sep 17 00:00:00 2001 From: Tom J Date: Mon, 2 Nov 2020 12:43:17 +0000 Subject: [PATCH 19/19] fix key update link when expired --- .../formatters/encrypted-mail-msg-formatter.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/extension/chrome/elements/compose-modules/formatters/encrypted-mail-msg-formatter.ts b/extension/chrome/elements/compose-modules/formatters/encrypted-mail-msg-formatter.ts index fe5aa6756cc..8a59d4d6777 100644 --- a/extension/chrome/elements/compose-modules/formatters/encrypted-mail-msg-formatter.ts +++ b/extension/chrome/elements/compose-modules/formatters/encrypted-mail-msg-formatter.ts @@ -7,7 +7,7 @@ import { BaseMailFormatter } from './base-mail-formatter.js'; import { ComposerResetBtnTrigger } from '../compose-err-module.js'; import { Mime, SendableMsgBody } from '../../../../js/common/core/mime.js'; import { NewMsgData } from '../compose-types.js'; -import { Str, Value } from '../../../../js/common/core/common.js'; +import { Str, Url, Value } from '../../../../js/common/core/common.js'; import { ApiErr } from '../../../../js/common/api/shared/api-error.js'; import { Att } from '../../../../js/common/core/att.js'; import { Buf } from '../../../../js/common/core/buf.js'; @@ -143,8 +143,12 @@ export class EncryptedMsgMailFormatter extends BaseMailFormatter { return undefined; } for (const myKey of pubs.filter(ap => ap.isMine)) { - if (await myKey.pubkey.usableButExpired) { - const path = chrome.runtime.getURL(`chrome/settings/index.htm?acctEmail=${encodeURIComponent(myKey.email)}&page=%2Fchrome%2Fsettings%2Fmodules%2Fmy_key_update.htm`); + if (myKey.pubkey.usableButExpired) { + const path = Url.create(chrome.runtime.getURL('chrome/settings/index.htm'), { + acctEmail: myKey.email, + page: '/chrome/settings/modules/my_key_update.htm', + pageUrlParams: JSON.stringify({ fingerprint: myKey.pubkey.id }), + }); const errModalLines = [ 'This message could not be encrypted because your own Private Key is expired.', '',