diff --git a/extension/chrome/elements/attachment.ts b/extension/chrome/elements/attachment.ts index c36e61ef80a..d5b001bf72d 100644 --- a/extension/chrome/elements/attachment.ts +++ b/extension/chrome/elements/attachment.ts @@ -192,7 +192,7 @@ export class AttachmentDownloadView extends View { private processAsPublicKeyAndHideAttIfAppropriate = async () => { if (this.attachment.msgId && this.attachment.id && this.attachment.treatAs() === 'publicKey') { // this is encrypted public key - download && decrypt & parse & render const { data } = await this.gmail.attGet(this.attachment.msgId, this.attachment.id); - const decrRes = await MsgUtil.decryptMessage({ kisWithPp: await KeyStore.getAllWithPp(this.acctEmail), encryptedData: data }); + const decrRes = await MsgUtil.decryptMessage({ kisWithPp: await KeyStore.getAllWithOptionalPassPhrase(this.acctEmail), encryptedData: data }); if (decrRes.success && decrRes.content) { const openpgpType = await MsgUtil.type({ data: decrRes.content }); if (openpgpType && openpgpType.type === 'publicKey' && openpgpType.armored) { // 'openpgpType.armored': could potentially process unarmored pubkey files, maybe later @@ -241,7 +241,7 @@ export class AttachmentDownloadView extends View { } private decryptAndSaveAttToDownloads = async () => { - const result = await MsgUtil.decryptMessage({ kisWithPp: await KeyStore.getAllWithPp(this.acctEmail), encryptedData: this.attachment.getData() }); + const result = await MsgUtil.decryptMessage({ kisWithPp: await KeyStore.getAllWithOptionalPassPhrase(this.acctEmail), encryptedData: this.attachment.getData() }); Xss.sanitizeRender(this.downloadButton, this.originalButtonHTML || ''); if (result.success) { if (!result.filename || ['msg.txt', 'null'].includes(result.filename)) { diff --git a/extension/chrome/elements/attachment_preview.ts b/extension/chrome/elements/attachment_preview.ts index 204899550ce..a572b932b77 100644 --- a/extension/chrome/elements/attachment_preview.ts +++ b/extension/chrome/elements/attachment_preview.ts @@ -82,7 +82,7 @@ View.run(class AttachmentPreviewView extends AttachmentDownloadView { } private decrypt = async () => { - const result = await MsgUtil.decryptMessage({ kisWithPp: await KeyStore.getAllWithPp(this.acctEmail), encryptedData: this.attachment.getData() }); + const result = await MsgUtil.decryptMessage({ kisWithPp: await KeyStore.getAllWithOptionalPassPhrase(this.acctEmail), encryptedData: this.attachment.getData() }); if ((result as DecryptSuccess).content) { return result.content; } else if ((result as DecryptError).error.type === DecryptErrTypes.needPassphrase) { diff --git a/extension/chrome/elements/compose-modules/compose-draft-module.ts b/extension/chrome/elements/compose-modules/compose-draft-module.ts index 1a7daa5006e..fa94cb3a08e 100644 --- a/extension/chrome/elements/compose-modules/compose-draft-module.ts +++ b/extension/chrome/elements/compose-modules/compose-draft-module.ts @@ -262,7 +262,7 @@ export class ComposeDraftModule extends ViewModule { const encryptedData = rawBlock.content instanceof Buf ? rawBlock.content : Buf.fromUtfStr(rawBlock.content); const passphrase = await this.view.storageModule.passphraseGet(); if (typeof passphrase !== 'undefined') { - const decrypted = await MsgUtil.decryptMessage({ kisWithPp: await KeyStore.getAllWithPp(this.view.acctEmail), encryptedData }); + const decrypted = await MsgUtil.decryptMessage({ kisWithPp: await KeyStore.getAllWithOptionalPassPhrase(this.view.acctEmail), encryptedData }); if (!decrypted.success) { return await this.abortAndRenderReplyMsgComposeTableIfIsReplyBox('!decrypted.success'); } diff --git a/extension/chrome/elements/compose-modules/compose-my-pubkey-module.ts b/extension/chrome/elements/compose-modules/compose-my-pubkey-module.ts index 0f9b9145437..cc2778148e5 100644 --- a/extension/chrome/elements/compose-modules/compose-my-pubkey-module.ts +++ b/extension/chrome/elements/compose-modules/compose-my-pubkey-module.ts @@ -4,7 +4,7 @@ import { ApiErr } from '../../../js/common/api/shared/api-error.js'; import { Catch } from '../../../js/common/platform/catch.js'; -import { KeyInfo, KeyUtil } from '../../../js/common/core/crypto/key.js'; +import { KeyInfo } from '../../../js/common/core/crypto/key.js'; import { Lang } from '../../../js/common/lang.js'; import { Ui } from '../../../js/common/browser/ui.js'; import { ViewModule } from '../../../js/common/view-module.js'; @@ -33,8 +33,7 @@ export class ComposeMyPubkeyModule extends ViewModule { public chooseMyPublicKeyBySenderEmail = async (keys: KeyInfo[], email: string) => { for (const key of keys) { - const parsedkey = await KeyUtil.parse(key.public); - if (parsedkey.emails.includes(email.toLowerCase())) { + if (key.emails.includes(email.toLowerCase())) { return key; } } diff --git a/extension/chrome/elements/compose-modules/compose-quote-module.ts b/extension/chrome/elements/compose-modules/compose-quote-module.ts index 5e771852a1c..3f89fa9bf7b 100644 --- a/extension/chrome/elements/compose-modules/compose-quote-module.ts +++ b/extension/chrome/elements/compose-modules/compose-quote-module.ts @@ -123,7 +123,7 @@ export class ComposeQuoteModule extends ViewModule { let attMeta: { content: Buf, filename?: string } | undefined; if (block.type === 'encryptedAtt') { this.setQuoteLoaderProgress('decrypting...'); - const result = await MsgUtil.decryptMessage({ kisWithPp: await KeyStore.getAllWithPp(this.view.acctEmail), encryptedData: block.attMeta.data }); + const result = await MsgUtil.decryptMessage({ kisWithPp: await KeyStore.getAllWithOptionalPassPhrase(this.view.acctEmail), encryptedData: block.attMeta.data }); if (result.success) { attMeta = { content: result.content, filename: result.filename }; } @@ -160,7 +160,7 @@ export class ComposeQuoteModule extends ViewModule { } private decryptMessage = async (encryptedData: Buf): Promise => { - const decryptRes = await MsgUtil.decryptMessage({ kisWithPp: await KeyStore.getAllWithPp(this.view.acctEmail), encryptedData }); + const decryptRes = await MsgUtil.decryptMessage({ kisWithPp: await KeyStore.getAllWithOptionalPassPhrase(this.view.acctEmail), encryptedData }); if (decryptRes.success) { return decryptRes.content.toUtfStr(); } else if (decryptRes.error && decryptRes.error.type === DecryptErrTypes.needPassphrase) { diff --git a/extension/chrome/elements/compose-modules/compose-storage-module.ts b/extension/chrome/elements/compose-modules/compose-storage-module.ts index ba29b81ff0c..247b79d81d8 100644 --- a/extension/chrome/elements/compose-modules/compose-storage-module.ts +++ b/extension/chrome/elements/compose-modules/compose-storage-module.ts @@ -102,7 +102,7 @@ export class ComposeStorageModule extends ViewModule { senderKi = await KeyStore.getFirst(this.view.acctEmail); Assert.abortAndRenderErrorIfKeyinfoEmpty(senderKi); } - return await PassphraseStore.get(this.view.acctEmail, senderKi.fingerprint); + return await PassphraseStore.get(this.view.acctEmail, senderKi.fingerprints[0]); } public lookupPubkeyFromDbOrKeyserverAndUpdateDbIfneeded = async (email: string, name: string | undefined): Promise => { diff --git a/extension/chrome/elements/passphrase.ts b/extension/chrome/elements/passphrase.ts index 23732dc1237..0c6fd89373d 100644 --- a/extension/chrome/elements/passphrase.ts +++ b/extension/chrome/elements/passphrase.ts @@ -56,11 +56,11 @@ View.run(class PassphraseView extends View { if (allPrivateKeys.length > 1) { let html: string; if (this.keysWeNeedPassPhraseFor.length === 1) { - html = `For key Fingerprint: ${Xss.escape(Str.spaced(this.keysWeNeedPassPhraseFor[0].fingerprint || ''))}`; + html = `For key Fingerprint: ${Xss.escape(Str.spaced(this.keysWeNeedPassPhraseFor[0].fingerprints[0] || ''))}`; } else { html = 'Pass phrase needed for any of the following keys:'; for (const i of this.keysWeNeedPassPhraseFor.keys()) { - html += `
Fingerprint ${String(i + 1)}: ${Xss.escape(Str.spaced(this.keysWeNeedPassPhraseFor[i].fingerprint) || '')}
`; + html += `
Fingerprint ${String(i + 1)}: ${Xss.escape(Str.spaced(this.keysWeNeedPassPhraseFor[i].fingerprints[0]) || '')}
`; } } Xss.sanitizeRender('.which_key', html); @@ -110,7 +110,7 @@ View.run(class PassphraseView extends View { const prv = await KeyUtil.parse(keyinfo.private); try { if (await KeyUtil.decrypt(prv, pass) === true) { - await PassphraseStore.set(storageType, this.acctEmail, keyinfo.fingerprint, pass); + await PassphraseStore.set(storageType, this.acctEmail, keyinfo.fingerprints[0], pass); atLeastOneMatched = true; if (storageType === 'session') { // TODO: change to 'broadcast' when issue with 'broadcast' is fixed @@ -119,7 +119,7 @@ View.run(class PassphraseView extends View { } } catch (e) { if (e instanceof Error && e.message === 'Unknown s2k type.') { - let msg = `Your key with fingerprint ${keyinfo.fingerprint} is not supported yet (${String(e)}).`; + let msg = `Your key with fingerprint ${keyinfo.fingerprints[0]} is not supported yet (${String(e)}).`; msg += '\n\nPlease write human@flowcrypt.com with details about how was this key created.'; await Ui.modal.error(msg); } else { diff --git a/extension/chrome/elements/pgp_block_modules/pgp-block-attachmens-module.ts b/extension/chrome/elements/pgp_block_modules/pgp-block-attachmens-module.ts index 98bab8490b0..846f8a4b2ed 100644 --- a/extension/chrome/elements/pgp_block_modules/pgp-block-attachmens-module.ts +++ b/extension/chrome/elements/pgp_block_modules/pgp-block-attachmens-module.ts @@ -68,7 +68,7 @@ export class PgpBlockViewAttachmentsModule { } private decryptAndSaveAttToDownloads = async (encrypted: Attachment) => { - const kisWithPp = await KeyStore.getAllWithPp(this.view.acctEmail); + const kisWithPp = await KeyStore.getAllWithOptionalPassPhrase(this.view.acctEmail); const decrypted = await BrowserMsg.send.bg.await.pgpMsgDecrypt({ kisWithPp, encryptedData: encrypted.getData() }); if (decrypted.success) { const attachment = new Attachment({ name: encrypted.name.replace(/\.(pgp|gpg)$/, ''), type: encrypted.type, data: decrypted.content }); 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..c189319de30 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 @@ -64,7 +64,7 @@ export class PgpBlockViewDecryptModule { private decryptAndRender = async (encryptedData: Buf, optionalPwd?: string, plainSubject?: string) => { if (typeof this.view.signature !== 'string') { - const kisWithPp = await KeyStore.getAllWithPp(this.view.acctEmail); + const kisWithPp = await KeyStore.getAllWithOptionalPassPhrase(this.view.acctEmail); const result = await BrowserMsg.send.bg.await.pgpMsgDecrypt({ kisWithPp, encryptedData }); if (typeof result === 'undefined') { await this.view.errorModule.renderErr(Lang.general.restartBrowserAndTryAgain, undefined); diff --git a/extension/chrome/settings/index.ts b/extension/chrome/settings/index.ts index a41bd5dc1ad..b8a9d0b6dbe 100644 --- a/extension/chrome/settings/index.ts +++ b/extension/chrome/settings/index.ts @@ -153,7 +153,7 @@ View.run(class SettingsView extends View { })); $('.action_open_public_key_page').click(this.setHandler(async () => { const ki = await KeyStore.getFirst(this.acctEmail!); - const escapedFp = Xss.escape(ki.fingerprint); + const escapedFp = Xss.escape(ki.fingerprints[0]); await Settings.renderSubPage(this.acctEmail!, this.tabId, 'modules/my_key.htm', `&fingerprint=${escapedFp}`); })); $('.action_show_encrypted_inbox').click(this.setHandler(() => { @@ -432,7 +432,7 @@ View.run(class SettingsView extends View { const prv = await KeyUtil.parse(ki.private); const created = new Date(prv.created); const date = Str.monthName(created.getMonth()) + ' ' + created.getDate() + ', ' + created.getFullYear(); - const escapedFp = Xss.escape(ki.fingerprint); + const escapedFp = Xss.escape(ki.fingerprints[0]); let removeKeyBtn = ''; if (canRemoveKey && privateKeys.length > 1) { removeKeyBtn = `(remove)`; diff --git a/extension/chrome/settings/modules/backup-manual-module.ts b/extension/chrome/settings/modules/backup-manual-module.ts index 6315ccbaac4..d096a9389aa 100644 --- a/extension/chrome/settings/modules/backup-manual-module.ts +++ b/extension/chrome/settings/modules/backup-manual-module.ts @@ -78,7 +78,7 @@ export class BackupManualActionModule extends ViewModule { } private backupOnEmailProviderAndUpdateUi = async (primaryKi: KeyInfo) => { - const pp = await PassphraseStore.get(this.view.acctEmail, primaryKi.fingerprint); + const pp = await PassphraseStore.get(this.view.acctEmail, primaryKi.fingerprints[0]); if (!this.view.parentTabId) { await Ui.modal.error(`Missing parentTabId. Please restart your browser and try again.`); return; diff --git a/extension/chrome/settings/modules/change_passphrase.ts b/extension/chrome/settings/modules/change_passphrase.ts index 8d22fcf26a7..6a1dcb8707c 100644 --- a/extension/chrome/settings/modules/change_passphrase.ts +++ b/extension/chrome/settings/modules/change_passphrase.ts @@ -41,7 +41,7 @@ View.run(class ChangePassPhraseView extends View { const primaryKi = await KeyStore.getFirst(this.acctEmail); this.primaryKi = primaryKi; Assert.abortAndRenderErrorIfKeyinfoEmpty(this.primaryKi); - const storedOrSessionPp = await PassphraseStore.get(this.acctEmail, this.primaryKi.fingerprint); + const storedOrSessionPp = await PassphraseStore.get(this.acctEmail, this.primaryKi.fingerprints[0]); const key = await KeyUtil.parse(this.primaryKi.private); this.primaryPrv = key; if (this.primaryPrv.fullyDecrypted || (storedOrSessionPp && await KeyUtil.decrypt(this.primaryPrv, storedOrSessionPp))) { @@ -108,9 +108,9 @@ View.run(class ChangePassPhraseView extends View { return; } await KeyStore.add(this.acctEmail, KeyUtil.armor(this.primaryPrv!)); - const persistentlyStoredPp = await PassphraseStore.get(this.acctEmail, this.primaryKi!.fingerprint, true); - await PassphraseStore.set('local', this.acctEmail, this.primaryKi!.fingerprint, typeof persistentlyStoredPp === 'undefined' ? undefined : newPp); - await PassphraseStore.set('session', this.acctEmail, this.primaryKi!.fingerprint, typeof persistentlyStoredPp === 'undefined' ? newPp : undefined); + const persistentlyStoredPp = await PassphraseStore.get(this.acctEmail, this.primaryKi!.fingerprints[0], true); + await PassphraseStore.set('local', this.acctEmail, this.primaryKi!.fingerprints[0], typeof persistentlyStoredPp === 'undefined' ? undefined : newPp); + await PassphraseStore.set('session', this.acctEmail, this.primaryKi!.fingerprints[0], typeof persistentlyStoredPp === 'undefined' ? newPp : undefined); await Ui.modal.info('Now that you changed your pass phrase, you should back up your key. New backup will be protected with new passphrase.'); Settings.redirectSubPage(this.acctEmail, this.parentTabId, '/chrome/settings/modules/backup.htm', '&action=backup_manual'); } diff --git a/extension/chrome/settings/modules/decrypt.ts b/extension/chrome/settings/modules/decrypt.ts index 269400972ed..2410e5d9482 100644 --- a/extension/chrome/settings/modules/decrypt.ts +++ b/extension/chrome/settings/modules/decrypt.ts @@ -55,7 +55,7 @@ View.run(class ManualDecryptView extends View { } private decryptAndDownload = async (encrypted: Attachment) => { // todo - this is more or less copy-pasted from attachment.js, should use common function - const result = await MsgUtil.decryptMessage({ kisWithPp: await KeyStore.getAllWithPp(this.acctEmail), encryptedData: encrypted.getData() }); + const result = await MsgUtil.decryptMessage({ kisWithPp: await KeyStore.getAllWithOptionalPassPhrase(this.acctEmail), encryptedData: encrypted.getData() }); if (result.success) { const attachment = new Attachment({ name: encrypted.name.replace(/\.(pgp|gpg|asc)$/i, ''), type: encrypted.type, data: result.content }); Browser.saveToDownloads(attachment); diff --git a/extension/chrome/settings/modules/keyserver.ts b/extension/chrome/settings/modules/keyserver.ts index d43dd5ee9d1..7bd8b1cc158 100644 --- a/extension/chrome/settings/modules/keyserver.ts +++ b/extension/chrome/settings/modules/keyserver.ts @@ -114,7 +114,7 @@ View.run(class KeyserverView extends View { const diagnosis: AttesterKeyserverDiagnosis = { hasPubkeyMissing: false, hasPubkeyMismatch: false, results: {} }; const { sendAs } = await AcctStore.get(this.acctEmail, ['sendAs']); const storedKeys = await KeyStore.get(this.acctEmail); - const storedKeysIds = storedKeys.map(ki => ki.fingerprint); + const storedKeysIds = storedKeys.map(ki => ki.fingerprints[0]); const results = await this.pubLookup.attester.lookupEmails(sendAs ? Object.keys(sendAs) : [this.acctEmail]); for (const email of Object.keys(results)) { const pubkeySearchResult = results[email]; diff --git a/extension/chrome/settings/modules/my_key.ts b/extension/chrome/settings/modules/my_key.ts index d0255fbd274..3c5169fefa8 100644 --- a/extension/chrome/settings/modules/my_key.ts +++ b/extension/chrome/settings/modules/my_key.ts @@ -49,7 +49,7 @@ View.run(class MyKeyView extends View { 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)); + $('.fingerprint').text(Str.spaced(this.keyInfo.fingerprints[0])); Xss.sanitizeRender('.email', this.pubKey.emails.map(email => `${Xss.escape(email)}`).join(', ')); const expiration = this.pubKey.expiration; $('.key_expiration').text(expiration && expiration !== Infinity ? Str.datetimeToDate(Str.fromDate(new Date(expiration))) : 'Key does not expire'); @@ -72,7 +72,7 @@ View.run(class MyKeyView extends View { try { const result = await this.pubLookup.attester.lookupEmail(this.acctEmail); const url = FlowCryptWebsite.url('pubkey', this.acctEmail); - if (result.pubkey && (await KeyUtil.parse(result.pubkey)).id === this.keyInfo.fingerprint) { + if (result.pubkey && (await KeyUtil.parse(result.pubkey)).id === this.keyInfo.fingerprints[0]) { $('.pubkey_link_container a').text(url.replace('https://', '')).attr('href', url).parent().css('display', ''); } else { $('.pubkey_link_container').remove(); @@ -86,7 +86,7 @@ View.run(class MyKeyView extends View { private downloadRevocationCert = async (enteredPP?: string) => { const prv = await KeyUtil.parse(this.keyInfo.private); if (!prv.fullyDecrypted) { - const passphrase = await PassphraseStore.get(this.acctEmail, this.keyInfo.fingerprint) || enteredPP; + const passphrase = await PassphraseStore.get(this.acctEmail, this.keyInfo.fingerprints[0]) || enteredPP; if (passphrase) { if (! await KeyUtil.decrypt(prv, passphrase) && enteredPP) { await Ui.modal.error('Pass phrase did not match, please try again.'); diff --git a/extension/chrome/settings/modules/my_key_update.ts b/extension/chrome/settings/modules/my_key_update.ts index 27ff2242968..305f441b71a 100644 --- a/extension/chrome/settings/modules/my_key_update.ts +++ b/extension/chrome/settings/modules/my_key_update.ts @@ -42,8 +42,8 @@ View.run(class MyKeyUpdateView extends View { Assert.abortAndRenderErrorIfKeyinfoEmpty(this.primaryKi); $('.action_show_public_key').attr('href', this.showKeyUrl); $('.email').text(this.acctEmail); - $('.fingerprint').text(Str.spaced(this.primaryKi.fingerprint)); - this.inputPrivateKey.attr('placeholder', this.inputPrivateKey.attr('placeholder') + ' (' + this.primaryKi.fingerprint + ')'); + $('.fingerprint').text(Str.spaced(this.primaryKi.fingerprints[0])); + this.inputPrivateKey.attr('placeholder', this.inputPrivateKey.attr('placeholder') + ' (' + this.primaryKi.fingerprints[0] + ')'); } public setHandlers = () => { @@ -52,10 +52,10 @@ View.run(class MyKeyUpdateView extends View { } private storeUpdatedKeyAndPassphrase = async (updatedPrv: Key, updatedPrvPassphrase: string) => { - const storedPassphrase = await PassphraseStore.get(this.acctEmail, this.primaryKi!.fingerprint, true); + const storedPassphrase = await PassphraseStore.get(this.acctEmail, this.primaryKi!.fingerprints[0], true); await KeyStore.add(this.acctEmail, KeyUtil.armor(updatedPrv)); - await PassphraseStore.set('local', this.acctEmail, this.primaryKi!.fingerprint, typeof storedPassphrase !== 'undefined' ? updatedPrvPassphrase : undefined); - await PassphraseStore.set('session', this.acctEmail, this.primaryKi!.fingerprint, typeof storedPassphrase !== 'undefined' ? undefined : updatedPrvPassphrase); + await PassphraseStore.set('local', this.acctEmail, this.primaryKi!.fingerprints[0], typeof storedPassphrase !== 'undefined' ? updatedPrvPassphrase : undefined); + await PassphraseStore.set('session', this.acctEmail, this.primaryKi!.fingerprints[0], typeof storedPassphrase !== 'undefined' ? undefined : updatedPrvPassphrase); if (this.orgRules.canSubmitPubToAttester() && await Ui.modal.confirm('Public and private key updated locally.\n\nUpdate public records with new Public Key?')) { try { await Ui.modal.info(await this.pubLookup.attester.updatePubkey(this.primaryKi!.longid, KeyUtil.armor(await KeyUtil.asPublicKey(updatedPrv)))); @@ -76,7 +76,7 @@ View.run(class MyKeyUpdateView extends View { } else if (updatedKey.isPublic) { await Ui.modal.warning('This was a public key. Please insert a private key instead. It\'s a block of text starting with "' + this.prvHeaders.begin + '"'); } else if (updatedKey.id !== (await KeyUtil.parse(this.primaryKi!.public)).id) { - await Ui.modal.warning(`This key ${Str.spaced(updatedKey.id || 'err')} does not match your current key ${Str.spaced(this.primaryKi!.fingerprint)}`); + await Ui.modal.warning(`This key ${Str.spaced(updatedKey.id || 'err')} does not match your current key ${Str.spaced(this.primaryKi!.fingerprints[0])}`); } else if (await KeyUtil.decrypt(updatedKey, uddatedKeyPassphrase) !== true) { await Ui.modal.error('The pass phrase does not match.\n\nPlease enter pass phrase of the newly updated key.'); } else { diff --git a/extension/chrome/settings/modules/my_key_user_ids.ts b/extension/chrome/settings/modules/my_key_user_ids.ts index e70a7f1b472..74e3a04c0cc 100644 --- a/extension/chrome/settings/modules/my_key_user_ids.ts +++ b/extension/chrome/settings/modules/my_key_user_ids.ts @@ -32,7 +32,7 @@ View.run(class MyKeyUserIdsView extends View { const prv = await KeyUtil.parse(this.primaryKi.private); Xss.sanitizeRender('.user_ids', prv.identities.map((uid: string) => `
${Xss.escape(uid)}
`).join('')); $('.email').text(this.acctEmail); - $('.fingerprint').text(Str.spaced(this.primaryKi.fingerprint)); + $('.fingerprint').text(Str.spaced(this.primaryKi.fingerprints[0])); } public setHandlers = () => { diff --git a/extension/chrome/settings/modules/security.ts b/extension/chrome/settings/modules/security.ts index f0c4396f911..0d2b176e2eb 100644 --- a/extension/chrome/settings/modules/security.ts +++ b/extension/chrome/settings/modules/security.ts @@ -69,11 +69,11 @@ View.run(class SecurityView extends View { $('.passphrase_entry_container').css('display', ''); })); $('.confirm_passphrase_requirement_change').click(this.setHandler(async () => { - const primaryKiPP = await PassphraseStore.get(this.acctEmail, this.primaryKi!.fingerprint); + const primaryKiPP = await PassphraseStore.get(this.acctEmail, this.primaryKi!.fingerprints[0]); if ($('input#passphrase_entry').val() === primaryKiPP) { for (const key of keys) { - await PassphraseStore.set('local', this.acctEmail, key.fingerprint, undefined); - await PassphraseStore.set('session', this.acctEmail, key.fingerprint, undefined); + await PassphraseStore.set('local', this.acctEmail, key.fingerprints[0], undefined); + await PassphraseStore.set('session', this.acctEmail, key.fingerprints[0], undefined); } window.location.reload(); } else { @@ -134,7 +134,7 @@ View.run(class SecurityView extends View { private isAnyPassPhraseStoredPermanently = async (keys: KeyInfo[]) => { for (const key of keys) { - if (await PassphraseStore.get(this.acctEmail, key.fingerprint, true)) { + if (await PassphraseStore.get(this.acctEmail, key.fingerprints[0], true)) { return true; } } diff --git a/extension/js/background_page/migrations.ts b/extension/js/background_page/migrations.ts index 802dfa8b3bd..0d2dcc31477 100644 --- a/extension/js/background_page/migrations.ts +++ b/extension/js/background_page/migrations.ts @@ -2,6 +2,27 @@ 'use strict'; +import { KeyInfo, KeyUtil } from '../common/core/crypto/key.js'; +import { GlobalStore } from '../common/platform/store/global-store.js'; +import { KeyStore } from '../common/platform/store/key-store.js'; + +const addKeyInfoFingerprints = async () => { + for (const acctEmail of await GlobalStore.acctEmailsGet()) { + const originalKis = await KeyStore.get(acctEmail); + const updated: KeyInfo[] = []; + for (const originalKi of originalKis) { + updated.push(await KeyUtil.keyInfoObj(await KeyUtil.parse(originalKi.private))); + } + await KeyStore.set(acctEmail, updated); + } +}; + export const migrateGlobal = async () => { - // noop + const globalStore = await GlobalStore.get(['key_info_store_fingerprints_added']); + if (!globalStore.key_info_store_fingerprints_added) { + console.info('migrating KeyStorage to add fingerprints and emails of each key...'); + await addKeyInfoFingerprints(); + await GlobalStore.set({ key_info_store_fingerprints_added: true }); + console.info('done migrating'); + } }; diff --git a/extension/js/common/api/email-provider/gmail/gmail.ts b/extension/js/common/api/email-provider/gmail/gmail.ts index 1c9c7e9b743..5bee8ef46a3 100644 --- a/extension/js/common/api/email-provider/gmail/gmail.ts +++ b/extension/js/common/api/email-provider/gmail/gmail.ts @@ -340,7 +340,7 @@ export class Gmail extends EmailProviderApi implements EmailProviderInterface { } await this.fetchAttachments(attachments); const { keys: foundBackupKeys } = await KeyUtil.readMany(Buf.fromUtfStr(attachments.map(a => a.getData().toUtfStr()).join('\n'))); - const backups = await Promise.all(foundBackupKeys.map(k => KeyStore.keyInfoObj(k))); + const backups = await Promise.all(foundBackupKeys.map(k => KeyUtil.keyInfoObj(k))); const imported = await KeyStore.get(this.acctEmail); const importedLongids = imported.map(ki => ki.longid); const backedUpLongids = backups.map(ki => ki.longid); diff --git a/extension/js/common/core/crypto/key.ts b/extension/js/common/core/crypto/key.ts index 04d8163a67f..e1b0ea5e18b 100644 --- a/extension/js/common/core/crypto/key.ts +++ b/extension/js/common/core/crypto/key.ts @@ -65,22 +65,20 @@ export type Contact = { expiresOn: number | null; }; -export interface PrvKeyInfo { +export interface KeyInfo { private: string; + public: string; // this cannot be Pubkey has it's being passed to localstorage longid: string; + fingerprints: string[]; + emails: string[]; +} + +export interface KeyInfoWithOptionalPp extends KeyInfo { passphrase?: string; - decrypted?: Key; // only for internal use in this file - parsed?: Key; // only for internal use in this file } export type KeyAlgo = 'curve25519' | 'rsa2048' | 'rsa4096'; -export interface KeyInfo extends PrvKeyInfo { - // this cannot be Pubkey has it's being passed to localstorage - public: string; - fingerprint: string; -} - export type PrvPacket = (OpenPGP.packet.SecretKey | OpenPGP.packet.SecretSubkey); export class UnexpectedKeyTypeError extends Error { } @@ -297,4 +295,17 @@ export class KeyUtil { } } + public static keyInfoObj = async (prv: Key): Promise => { + if (!prv.isPrivate) { + throw new Error('Key passed into KeyUtil.keyInfoObj must be a Private Key'); + } + return { + private: KeyUtil.armor(prv), + public: KeyUtil.armor(await KeyUtil.asPublicKey(prv)), + longid: OpenPGPKey.fingerprintToLongid(prv.id), + emails: prv.emails, + fingerprints: prv.allIds, + }; + } + } diff --git a/extension/js/common/core/crypto/pgp/msg-util.ts b/extension/js/common/core/crypto/pgp/msg-util.ts index 9ce5a3793e0..57595870243 100644 --- a/extension/js/common/core/crypto/pgp/msg-util.ts +++ b/extension/js/common/core/crypto/pgp/msg-util.ts @@ -1,7 +1,7 @@ /* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com */ 'use strict'; -import { Contact, Key, PrvKeyInfo, KeyUtil } from '../key.js'; +import { Contact, Key, KeyInfo, KeyInfoWithOptionalPp, KeyUtil } from '../key.js'; import { MsgBlockType, ReplaceableMsgBlockType } from '../../msg-block.js'; import { Value } from '../../common.js'; import { Buf } from '../../buf.js'; @@ -17,7 +17,7 @@ export namespace PgpMsgMethod { export namespace Arg { export type Encrypt = { pubkeys: Key[], signingPrv?: Key, pwd?: string, data: Uint8Array, filename?: string, armor: boolean, date?: Date }; export type Type = { data: Uint8Array | string }; - export type Decrypt = { kisWithPp: PrvKeyInfo[], encryptedData: Uint8Array, msgPwd?: string }; + export type Decrypt = { kisWithPp: KeyInfoWithOptionalPp[], encryptedData: Uint8Array, msgPwd?: string }; export type DiagnosePubkeys = { armoredPubs: string[], message: Uint8Array }; export type VerifyDetached = { plaintext: Uint8Array, sigText: Uint8Array }; } @@ -44,10 +44,10 @@ type SortedKeysForDecrypt = { forVerification: OpenPGP.key.Key[]; encryptedFor: string[]; signedBy: string[]; - prvMatching: PrvKeyInfo[]; - prvForDecrypt: PrvKeyInfo[]; - prvForDecryptDecrypted: PrvKeyInfo[]; - prvForDecryptWithoutPassphrases: PrvKeyInfo[]; + prvMatching: KeyInfoWithOptionalPp[]; + prvForDecrypt: KeyInfoWithOptionalPp[]; + prvForDecryptDecrypted: { ki: KeyInfoWithOptionalPp, decrypted: Key }[]; + prvForDecryptWithoutPassphrases: KeyInfo[]; }; export type DecryptSuccess = { success: true; signature?: VerifyRes; isEncrypted?: boolean, filename?: string, content: Buf }; @@ -194,7 +194,7 @@ export class MsgUtil { const keys = await MsgUtil.getSortedKeys(kisWithPp, prepared.message); longids.message = keys.encryptedFor; longids.matching = keys.prvForDecrypt.map(ki => ki.longid); - longids.chosen = keys.prvForDecryptDecrypted.map(ki => ki.longid); + longids.chosen = keys.prvForDecryptDecrypted.map(decrypted => decrypted.ki.longid); longids.needPassphrase = keys.prvForDecryptWithoutPassphrases.map(ki => ki.longid); const isEncrypted = !prepared.isCleartext; if (!isEncrypted) { @@ -214,7 +214,7 @@ export class MsgUtil { return { success: false, error: { type: DecryptErrTypes.usePassword, message: 'Use message password' }, longids, isEncrypted }; } const passwords = msgPwd ? [msgPwd] : undefined; - const privateKeys = keys.prvForDecryptDecrypted.map(ki => ki.decrypted!); + const privateKeys = keys.prvForDecryptDecrypted.map(decrypted => decrypted.decrypted); const decrypted = await OpenPGPKey.decryptMessage(prepared.message as OpenPGP.message.Message, privateKeys, passwords); await MsgUtil.cryptoMsgGetSignedBy(decrypted, keys); // we can only figure out who signed the msg once it's decrypted const signature = keys.signedBy.length ? await MsgUtil.verify(decrypted, keys.forVerification, keys.verificationContacts[0]) : undefined; @@ -276,7 +276,7 @@ export class MsgUtil { } } - private static getSortedKeys = async (kiWithPp: PrvKeyInfo[], msg: OpenpgpMsgOrCleartext): Promise => { + private static getSortedKeys = async (kiWithPp: KeyInfoWithOptionalPp[], msg: OpenpgpMsgOrCleartext): Promise => { const keys: SortedKeysForDecrypt = { verificationContacts: [], forVerification: [], @@ -291,33 +291,26 @@ export class MsgUtil { keys.encryptedFor = encryptionKeyids.map(kid => OpenPGPKey.bytesToLongid(kid.bytes)); await MsgUtil.cryptoMsgGetSignedBy(msg, keys); if (keys.encryptedFor.length) { - for (const ki of kiWithPp) { - ki.parsed = await KeyUtil.parse(ki.private); // todo - // this is inefficient because we are doing unnecessary parsing of all keys here - // better would be to compare to already stored KeyInfo, however KeyInfo currently only holds primary id, not ids of subkeys - // while messages are typically encrypted for subkeys, thus we have to parse the key to get the info - // we are filtering here to avoid a significant performance issue of having to attempt decrypting with all keys simultaneously - for (const id of ki.parsed.allIds) { - if (keys.encryptedFor.includes(OpenPGPKey.fingerprintToLongid(id))) { - keys.prvMatching.push(ki); - break; - } - } - } + keys.prvMatching = kiWithPp.filter(ki => ki.fingerprints.some( + fp => keys.encryptedFor.includes(OpenPGPKey.fingerprintToLongid(fp)))); keys.prvForDecrypt = keys.prvMatching.length ? keys.prvMatching : kiWithPp; } else { // prvs not needed for signed msgs keys.prvForDecrypt = []; } for (const ki of keys.prvForDecrypt) { - const matchingKeyids = MsgUtil.matchingKeyids(ki.parsed!, encryptionKeyids); + const matchingKeyids = MsgUtil.matchingKeyids(ki.fingerprints, encryptionKeyids); const cachedKey = KeyCache.getDecrypted(ki.longid); if (cachedKey && MsgUtil.isKeyDecryptedFor(cachedKey, matchingKeyids)) { - ki.decrypted = cachedKey; - keys.prvForDecryptDecrypted.push(ki); - } else if (MsgUtil.isKeyDecryptedFor(ki.parsed!, matchingKeyids) || await MsgUtil.decryptKeyFor(ki.parsed!, ki.passphrase!, matchingKeyids) === true) { - KeyCache.setDecrypted(ki.parsed!); - ki.decrypted = ki.parsed!; - keys.prvForDecryptDecrypted.push(ki); + keys.prvForDecryptDecrypted.push({ ki, decrypted: cachedKey }); + continue; + } + const parsed = await KeyUtil.parse(ki.private); + // todo - the `ki.passphrase || ''` used to be `ki.passphrase!` which could have actually allowed an undefined to be passed + // as fixed currently it appears better, but it may be best to instead check `ki.passphrase && await MsgUtil.decryptKeyFor(...)` + // but that is a larger change that would require separate PR and testing + if (MsgUtil.isKeyDecryptedFor(parsed, matchingKeyids) || await MsgUtil.decryptKeyFor(parsed, ki.passphrase || '', matchingKeyids) === true) { + KeyCache.setDecrypted(parsed); + keys.prvForDecryptDecrypted.push({ ki, decrypted: parsed }); } else { keys.prvForDecryptWithoutPassphrases.push(ki); } @@ -325,8 +318,8 @@ export class MsgUtil { return keys; } - private static matchingKeyids = (key: Key, encryptedForKeyids: OpenPGP.Keyid[]): OpenPGP.Keyid[] => { - const allKeyLongids = key.allIds.map(id => OpenPGPKey.fingerprintToLongid(id)); + private static matchingKeyids = (fingerprints: string[], encryptedForKeyids: OpenPGP.Keyid[]): OpenPGP.Keyid[] => { + const allKeyLongids = fingerprints.map(fp => OpenPGPKey.fingerprintToLongid(fp)); return encryptedForKeyids.filter(kid => allKeyLongids.includes(OpenPGPKey.bytesToLongid(kid.bytes))); } diff --git a/extension/js/common/platform/store/global-store.ts b/extension/js/common/platform/store/global-store.ts index 839e72a7513..f197b22f426 100644 --- a/extension/js/common/platform/store/global-store.ts +++ b/extension/js/common/platform/store/global-store.ts @@ -17,10 +17,11 @@ export type GlobalStoreDict = { dev_outlook_allow?: boolean; admin_codes?: Dict; install_mobile_app_notification_dismissed?: boolean; + key_info_store_fingerprints_added?: boolean; }; export type GlobalIndex = 'version' | 'account_emails' | 'settings_seen' | 'hide_pass_phrases' | - 'dev_outlook_allow' | 'admin_codes' | 'install_mobile_app_notification_dismissed'; + 'dev_outlook_allow' | 'admin_codes' | 'install_mobile_app_notification_dismissed' | 'key_info_store_fingerprints_added'; /** * Locally stored data that is not associated with any email account diff --git a/extension/js/common/platform/store/key-store.ts b/extension/js/common/platform/store/key-store.ts index a64ac2e0d16..f8f649a64f4 100644 --- a/extension/js/common/platform/store/key-store.ts +++ b/extension/js/common/platform/store/key-store.ts @@ -1,10 +1,9 @@ /* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com */ -import { KeyInfo, Key, KeyUtil } from '../../core/crypto/key.js'; +import { KeyInfo, KeyInfoWithOptionalPp, KeyUtil } from '../../core/crypto/key.js'; 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'; /** * Local store of account private keys @@ -17,21 +16,23 @@ export class KeyStore extends AbstractStore { if (!fingerprints) { return keys; } - return keys.filter(ki => fingerprints.includes(ki.fingerprint)); + // filters by primary fingerprint - subkey fingerprints are ignored + // todo - could consider also filtering by subkey fingerprints, but need to think about impact + return keys.filter(ki => fingerprints.includes(ki.fingerprints[0])); } public static getFirst = async (acctEmail: string): Promise => { - const stored = await AcctStore.get(acctEmail, ['keys']); - const keys: KeyInfo[] = stored.keys || []; + const keys = await KeyStore.get(acctEmail); return keys[0]; } - public static getAllWithPp = async (acctEmail: string): Promise => { + public static getAllWithOptionalPassPhrase = async (acctEmail: string): Promise => { const keys = await KeyStore.get(acctEmail); + const withPp: KeyInfoWithOptionalPp[] = []; for (const ki of keys) { - ki.passphrase = await PassphraseStore.get(acctEmail, ki.fingerprint); + withPp.push({ ...ki, passphrase: await PassphraseStore.get(acctEmail, ki.fingerprints[0]) }); } - return keys; + return withPp; } public static add = async (acctEmail: string, newKeyArmored: string) => { @@ -42,21 +43,25 @@ export class KeyStore extends AbstractStore { throw new Error('Cannot import plain, unprotected key.'); } for (const i in keyinfos) { - if (prv.id === keyinfos[i].fingerprint) { // replacing a key - keyinfos[i] = await KeyStore.keyInfoObj(prv); + if (prv.id === keyinfos[i].fingerprints[0]) { // replacing a key + keyinfos[i] = await KeyUtil.keyInfoObj(prv); updated = true; } } if (!updated) { - keyinfos.push(await KeyStore.keyInfoObj(prv)); + keyinfos.push(await KeyUtil.keyInfoObj(prv)); } + await KeyStore.set(acctEmail, keyinfos); + } + + public static set = async (acctEmail: string, keyinfos: KeyInfo[]) => { await AcctStore.set(acctEmail, { keys: keyinfos }); } public static remove = async (acctEmail: string, removeFingerprint: string): Promise => { const privateKeys = await KeyStore.get(acctEmail); - const filteredPrivateKeys = privateKeys.filter(ki => ki.fingerprint !== removeFingerprint); - await AcctStore.set(acctEmail, { keys: filteredPrivateKeys }); + const filteredPrivateKeys = privateKeys.filter(ki => ki.fingerprints[0] !== removeFingerprint); + await KeyStore.set(acctEmail, filteredPrivateKeys); } /** @@ -66,17 +71,10 @@ export class KeyStore extends AbstractStore { const keys = await KeyStore.get(acctEmail); const result: string[] = []; for (const key of keys) { - if (! await PassphraseStore.get(acctEmail, key.fingerprint, true) && await PassphraseStore.get(acctEmail, key.fingerprint, false)) { + if (! await PassphraseStore.get(acctEmail, key.fingerprints[0], true) && await PassphraseStore.get(acctEmail, key.fingerprints[0], false)) { result.push(key.longid); } } return result; } - - 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, longid, fingerprint: prv.id }; - } - } diff --git a/extension/js/common/settings.ts b/extension/js/common/settings.ts index e1ec2cdc293..ff3fafd16c9 100644 --- a/extension/js/common/settings.ts +++ b/extension/js/common/settings.ts @@ -116,9 +116,9 @@ export class Settings { const destAccountPrivateKeys = await KeyStore.get(newAcctEmail); const destAcctPassPhrases: Dict = {}; for (const ki of destAccountPrivateKeys) { - const pp = await PassphraseStore.get(newAcctEmail, ki.fingerprint, true); + const pp = await PassphraseStore.get(newAcctEmail, ki.fingerprints[0], true); if (pp) { - destAcctPassPhrases[ki.fingerprint] = pp; + destAcctPassPhrases[ki.fingerprints[0]] = pp; } } if (!oldAcctEmailIndexPrefix) { diff --git a/extension/js/common/ui/key-import-ui.ts b/extension/js/common/ui/key-import-ui.ts index 1e94c347e6b..64d9bbc66fb 100644 --- a/extension/js/common/ui/key-import-ui.ts +++ b/extension/js/common/ui/key-import-ui.ts @@ -242,7 +242,7 @@ export class KeyImportUi { private rejectKnownIfSelected = async (acctEmail: string, k: Key) => { if (this.rejectKnown) { const keyinfos = await KeyStore.get(acctEmail); - const privateKeysIds = keyinfos.map(ki => ki.fingerprint); + const privateKeysIds = keyinfos.map(ki => ki.fingerprints[0]); if (privateKeysIds.includes(k.id)) { throw new UserAlert('This is one of your current keys, try another one.'); } diff --git a/test/source/test.ts b/test/source/test.ts index 08438ff00f7..27cc1b137bb 100644 --- a/test/source/test.ts +++ b/test/source/test.ts @@ -49,6 +49,7 @@ const mockApiLogs: string[] = []; ava.before('set config and mock api', async t => { standaloneTestTimeout(t, consts.TIMEOUT_EACH_RETRY, t.title); Config.extensionId = await browserPool.getExtensionId(t); + await Config.setupSecrets(); console.info(`Extension url: chrome-extension://${Config.extensionId}`); if (isMock) { const mockApi = await mock(line => mockApiLogs.push(line)); diff --git a/test/source/tests/unit-node.ts b/test/source/tests/unit-node.ts index 6882dc7f2e4..1b05005a717 100644 --- a/test/source/tests/unit-node.ts +++ b/test/source/tests/unit-node.ts @@ -7,7 +7,7 @@ import { MsgBlockParser } from '../core/msg-block-parser'; import { PgpHash } from '../core/crypto/pgp/pgp-hash'; import { TestVariant } from '../util'; import { expect } from 'chai'; -import { KeyUtil, PrvKeyInfo } from '../core/crypto/key'; +import { KeyUtil, KeyInfoWithOptionalPp } from '../core/crypto/key'; import { UnreportableError } from '../platform/catch.js'; import { Buf } from '../core/buf'; import { OpenPGPKey } from '../core/crypto/pgp/openpgp-key'; @@ -668,17 +668,17 @@ eg== }); ava.default('[unit][MsgUtil.getSortedKeys,matchingKeyids] must be able to find matching keys', async t => { - const pp = 'some pass for testing'; - const key1 = await OpenPGPKey.create([{ name: 'Key1', email: 'key1@test.com' }], 'curve25519', pp, 0); - const key2 = await OpenPGPKey.create([{ name: 'Key2', email: 'key2@test.com' }], 'curve25519', pp, 0); + const passphrase = 'some pass for testing'; + const key1 = await OpenPGPKey.create([{ name: 'Key1', email: 'key1@test.com' }], 'curve25519', passphrase, 0); + const key2 = await OpenPGPKey.create([{ name: 'Key2', email: 'key2@test.com' }], 'curve25519', passphrase, 0); const pub1 = await KeyUtil.parse(key1.public); const pub2 = await KeyUtil.parse(key2.public); // only encrypt with pub1 const { data } = await MsgUtil.encryptMessage({ pubkeys: [pub1], data: Buf.fromUtfStr('anything'), armor: true }) as PgpMsgMethod.EncryptPgpArmorResult; const m = await opgp.message.readArmored(Buf.fromUint8(data).toUtfStr()); - const kisWithPp: PrvKeyInfo[] = [ // supply both pub1 and pub2 for decrypt - { private: key1.private, longid: OpenPGPKey.fingerprintToLongid(pub1.id), passphrase: pp }, - { private: key2.private, longid: OpenPGPKey.fingerprintToLongid(pub2.id), passphrase: pp } + const kisWithPp: KeyInfoWithOptionalPp[] = [ // supply both key1 and key2 for decrypt + { ... await KeyUtil.keyInfoObj(await KeyUtil.parse(key1.private)), passphrase }, + { ... await KeyUtil.keyInfoObj(await KeyUtil.parse(key2.private)), passphrase }, ]; // we are testing a private method here because the outcome of this method is not directly testable from the // public method that uses it. It only makes the public method faster, which is hard to test. @@ -691,13 +691,13 @@ eg== expect(sortedKeys.prvForDecrypt.length).to.equal(1); expect(sortedKeys.prvForDecryptDecrypted.length).to.equal(1); // specifically the pub1 - expect(sortedKeys.prvForDecryptDecrypted[0].longid).to.equal(OpenPGPKey.fingerprintToLongid(pub1.id)); + expect(sortedKeys.prvForDecryptDecrypted[0].ki.longid).to.equal(OpenPGPKey.fingerprintToLongid(pub1.id)); // also test MsgUtil.matchingKeyids // @ts-ignore - const matching1 = await MsgUtil.matchingKeyids(pub1, m.getEncryptionKeyIds()); + const matching1 = await MsgUtil.matchingKeyids(pub1.allIds, m.getEncryptionKeyIds()); expect(matching1.length).to.equal(1); // @ts-ignore - const matching2 = await MsgUtil.matchingKeyids(pub2, m.getEncryptionKeyIds()); + const matching2 = await MsgUtil.matchingKeyids(pub2.allIds, m.getEncryptionKeyIds()); expect(matching2.length).to.equal(0); t.pass(); }); @@ -763,7 +763,7 @@ eg== const justPrimaryPub = tmpPub.armor(); const pubkeys = [await KeyUtil.parse(justPrimaryPub)]; const encrypted = await MsgUtil.encryptMessage({ pubkeys, data, armor: true }) as PgpMsgMethod.EncryptPgpArmorResult; - const kisWithPp: PrvKeyInfo[] = [{ private: prvEncryptForSubkeyOnlyProtected, longid: 'F90C76AE611AFDEE', passphrase }]; + const kisWithPp: KeyInfoWithOptionalPp[] = [{ ... await KeyUtil.keyInfoObj(await KeyUtil.parse(prvEncryptForSubkeyOnlyProtected)), passphrase }]; const decrypted = await MsgUtil.decryptMessage({ kisWithPp, encryptedData: encrypted.data }); // todo - later we'll have an org rule for ignoring this, and then it will be expected to pass as follows: // expect(decrypted.success).to.equal(true); diff --git a/test/source/util/index.ts b/test/source/util/index.ts index d4bf8d22e3a..4636387482f 100644 --- a/test/source/util/index.ts +++ b/test/source/util/index.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; -import { KeyInfo } from '../core/crypto/key.js'; +import { KeyInfoWithOptionalPp, KeyUtil } from '../core/crypto/key.js'; export type TestVariant = 'CONSUMER-MOCK' | 'ENTERPRISE-MOCK' | 'CONSUMER-LIVE-GMAIL' | 'UNIT-TESTS'; @@ -46,7 +46,7 @@ interface TestSecretsInterface { data_encryption_password: string; auth: { google: { email: string, password?: string, secret_2fa?: string }[], }; keys: { title: string, passphrase: string, armored: string | null, longid: string | null }[]; - keyInfo: Array<{ email: string, key: KeyInfo[] }>; + keyInfo: Array<{ email: string, key: KeyInfoWithOptionalPp[] }>; } export class Config { @@ -71,6 +71,24 @@ export class Config { return Config.secrets().keys.filter(k => k.title === title)[0]; } + public static setupSecrets = async (): Promise => { + await Config.fixKeyInfo(Config._secrets); + } + + public static fixKeyInfo = async (secrets: TestSecretsInterface): Promise => { + // The keys in test secrets file used to have different structure, + // this does a migration so that we can continue using the file as is + // without distributing an updated secrets file to everyone + secrets.keyInfo = await Promise.all(secrets.keyInfo.map(async original => { + const kisWithPp: KeyInfoWithOptionalPp[] = []; + for (const ki of original.key) { + const reParsed = await KeyUtil.keyInfoObj(await KeyUtil.parse(ki.private)); + kisWithPp.push({ ...reParsed, passphrase: ki.passphrase }); + } + return { email: original.email, key: kisWithPp }; + })); + } + } Config.secrets().auth.google.push( // these don't contain any secrets, so not worth syncing through secrets file