Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions extension/chrome/settings/index.htm
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ <h1 class="text-center">FlowCrypt Settings</h1>
<script src="/lib/jquery.min.js"></script>
<script src="/lib/sweetalert2.js"></script>
<script src="/lib/openpgp.js"></script>
<script src="/lib/forge.js"></script>
<script src="/lib/bootstrap/bootstrap.min.js"></script>
<script src="index.js" type="module"></script>

Expand Down
1 change: 1 addition & 0 deletions extension/chrome/settings/modules/my_key.htm
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
<script src="/lib/jquery.min.js"></script>
<script src="/lib/sweetalert2.js"></script>
<script src="/lib/openpgp.js"></script>
<script src="/lib/forge.js"></script>
<script src="/lib/clipboard.js"></script>
<script src="my_key.js" type="module"></script>
</body>
Expand Down
1 change: 1 addition & 0 deletions extension/chrome/settings/setup.htm
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ <h1>Set Up FlowCrypt</h1>
<script src="/lib/sweetalert2.js"></script>
<script src="/lib/zxcvbn.js"></script>
<script src="/lib/openpgp.js"></script>
<script src="/lib/forge.js"></script>
<script src="setup.js" type="module"></script>
<script src="../settings/index.js" type="module"></script>

Expand Down
7 changes: 7 additions & 0 deletions extension/chrome/settings/setup/setup-import-key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 6 additions & 6 deletions extension/js/common/core/crypto/key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,13 @@ export class KeyUtil {
return `[-] ${String(value)}`;
}

public static asPublicKey = async (pubkey: Key): Promise<Key> => {
// TODO: Delegate to appropriate key type
if (pubkey.type === 'openpgp') {
return await OpenPGPKey.asPublicKey(pubkey);
public static asPublicKey = async (key: Key): Promise<Key> => {
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 => {
Expand Down
12 changes: 11 additions & 1 deletion extension/js/common/core/crypto/smime/smime-key.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions test/source/tests/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
36 changes: 19 additions & 17 deletions test/source/tests/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
})
);

}

Expand Down