diff --git a/extension/chrome/settings/index.htm b/extension/chrome/settings/index.htm index cb87250cbf8..4c0d7dbff29 100644 --- a/extension/chrome/settings/index.htm +++ b/extension/chrome/settings/index.htm @@ -269,6 +269,7 @@

FlowCrypt Settings

+ diff --git a/extension/chrome/settings/modules/my_key.htm b/extension/chrome/settings/modules/my_key.htm index 338d2a8d43f..6cf87aba20d 100644 --- a/extension/chrome/settings/modules/my_key.htm +++ b/extension/chrome/settings/modules/my_key.htm @@ -57,6 +57,7 @@ + diff --git a/extension/chrome/settings/setup.htm b/extension/chrome/settings/setup.htm index c2cfc326b61..89175f5dd8a 100644 --- a/extension/chrome/settings/setup.htm +++ b/extension/chrome/settings/setup.htm @@ -327,6 +327,7 @@

Set Up FlowCrypt

+ diff --git a/extension/chrome/settings/setup/setup-import-key.ts b/extension/chrome/settings/setup/setup-import-key.ts index 8d566170069..856132e8afa 100644 --- a/extension/chrome/settings/setup/setup-import-key.ts +++ b/extension/chrome/settings/setup/setup-import-key.ts @@ -30,6 +30,13 @@ export class SetupImportKeyModule { }; try { const checked = await this.view.keyImportUi.checkPrv(this.view.acctEmail, String($('#step_2b_manual_enter .input_private_key').val()), options.passphrase); + if (checked.decrypted.type === 'x509') { + if (!await Ui.modal.confirm('Using S/MIME as the only key on account is experimental. ' + + 'You should instead import an OpenPGP key here, and then add S/MIME keys as additional keys in FlowCrypt Settings.' + + '\n\nContinue anyway? (not recommented).')) { + return; + } + } Xss.sanitizeRender('#step_2b_manual_enter .action_add_private_key', Ui.spinner('white')); await this.view.saveKeysAndPassPhrase([checked.encrypted], options); await this.view.preFinalizeSetup(options); diff --git a/extension/js/common/core/crypto/key.ts b/extension/js/common/core/crypto/key.ts index 76862c341d1..38a6e81341d 100644 --- a/extension/js/common/core/crypto/key.ts +++ b/extension/js/common/core/crypto/key.ts @@ -220,13 +220,13 @@ export class KeyUtil { return `[-] ${String(value)}`; } - public static asPublicKey = async (pubkey: Key): Promise => { - // TODO: Delegate to appropriate key type - if (pubkey.type === 'openpgp') { - return await OpenPGPKey.asPublicKey(pubkey); + public static asPublicKey = async (key: Key): Promise => { + if (key.type === 'openpgp') { + return await OpenPGPKey.asPublicKey(key); + } else if (key.type === 'x509') { + return SmimeKey.asPublicKey(key); } - // TODO: Assuming S/MIME keys are already public: this should be fixed. - return pubkey; + throw new UnexpectedKeyTypeError(`Key type is ${key.type}, expecting OpenPGP or x509 S/MIME`); } public static expired = (key: Key): boolean => { diff --git a/extension/js/common/core/crypto/smime/smime-key.ts b/extension/js/common/core/crypto/smime/smime-key.ts index 0874b869bd4..f2db1a96880 100644 --- a/extension/js/common/core/crypto/smime/smime-key.ts +++ b/extension/js/common/core/crypto/smime/smime-key.ts @@ -1,6 +1,6 @@ /* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com */ import * as forge from 'node-forge'; -import { Key } from '../key.js'; +import { Key, UnexpectedKeyTypeError } from '../key.js'; import { Str } from '../../common.js'; import { UnreportableError } from '../../../platform/catch.js'; import { PgpArmor } from '../pgp/pgp-armor.js'; @@ -129,6 +129,16 @@ export class SmimeKey { key.fullyEncrypted = true; } + public static asPublicKey = (key: Key): Key => { + if (key.type !== 'x509') { + throw new UnexpectedKeyTypeError(`Key type is ${key.type}, expecting x509 S/MIME`); + } + if (key.isPrivate) { + return SmimeKey.getKeyFromCertificate(SmimeKey.getArmoredCertificate(key), undefined); + } + return key; + } + private static getLeafCertificates = (msgBlocks: MsgBlock[]): { pem: string, certificate: forge.pki.Certificate }[] => { const parsed = msgBlocks.map(cert => { return { pem: cert.content as string, certificate: forge.pki.certificateFromPem(cert.content as string) }; }); // Note: no signature check is performed. diff --git a/test/source/tests/settings.ts b/test/source/tests/settings.ts index 95453cbde74..95e0e2a3e21 100644 --- a/test/source/tests/settings.ts +++ b/test/source/tests/settings.ts @@ -472,6 +472,12 @@ export let defineSettingsTests = (testVariant: TestVariant, testWithBrowser: Tes { isSavePassphraseChecked: true, isSavePassphraseHidden: false }); })); + ava.default('settings - add unprotected s/mime key', testWithBrowser('ci.tests.gmail', async (t, browser) => { + const unprotectedPrvKey = fs.readFileSync('test/samples/smime/human-unprotected-pem.txt', 'utf8'); + await SettingsPageRecipe.addKeyTest(t, browser, 'ci.tests.gmail@flowcrypt.test', unprotectedPrvKey, 'this is a new passphrase to protect previously unprotected key', + { isSavePassphraseChecked: true, isSavePassphraseHidden: false }); + })); + ava.default('settings - error modal when page parameter invalid', testWithBrowser('ci.tests.gmail', async (t, browser) => { const invalidParamModalPage = await browser.newPage(t, TestUrls.extension(`chrome/settings/index.htm?acctEmail=ci.tests.gmail@gmail.com&page=invalid`)); await Util.sleep(3); diff --git a/test/source/tests/setup.ts b/test/source/tests/setup.ts index 8c5864a1e60..bed3e31aab1 100644 --- a/test/source/tests/setup.ts +++ b/test/source/tests/setup.ts @@ -722,23 +722,25 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== } })); - // todo - change to an "add key" instead of initial import test - // todo - disable initial import of s/mime key - // todo - disable import of encrypted s/mime key, require decrypted? - // ava.default.only( - // 'setup - s/mime private key', - // testWithBrowser(undefined, async (t, browser) => { - // const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, 'flowcrypt.test.key.imported@gmail.com'); - // const key = { - // title: 's/mime pkcs12 encrypted key', - // filePath: 'test/samples/smime/human-unprotected-PKCS12.p12', - // armored: null, - // passphrase: 'test pp to encrypt unprotected key', - // longid: null - // }; - // await SetupPageRecipe.manualEnter(settingsPage, key.title, { submitPubkey: false, usedPgpBefore: false, key }); - // }) - // ); + ava.default( + 'setup - s/mime private key', + testWithBrowser(undefined, async (t, browser) => { + const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, 'flowcrypt.test.key.imported@gmail.com'); + const key = { + title: 's/mime pkcs12 unprotected key', + filePath: 'test/samples/smime/human-unprotected-PKCS12.p12', + armored: null, // tslint:disable-line:no-null-keyword + passphrase: 'test pp to encrypt unprotected key', + longid: null // tslint:disable-line:no-null-keyword + }; + await SetupPageRecipe.manualEnter(settingsPage, key.title, { fillOnly: true, submitPubkey: false, usedPgpBefore: false, key }); + await settingsPage.waitAndClick('@input-step2bmanualenter-save', { delay: 1 }); + await Util.sleep(1); + await settingsPage.waitAndRespondToModal('confirm', 'confirm', 'Using S/MIME as the only key on account is experimental.'); + await settingsPage.waitAndClick('@action-step4done-account-settings', { delay: 1 }); + await SettingsPageRecipe.ready(settingsPage); + }) + ); }