Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#5811 Added ability to generate keys in the extension #5823

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
71 changes: 71 additions & 0 deletions extension/chrome/elements/shared/create_key.template.htm
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<div id="step_2a_manual_create" class="manual">
<div class="line left">
Choose pass phrase to protect your encrypted emails. <a href="#" data-swal-page="/chrome/texts/passphrase_help.htm">choosing secure pass phrases</a>
</div>
<div class="line left">
<input
class="input_password"
type="text"
placeholder="Pass phrase"
style="margin-bottom: 0"
id="step_2a_manual_create_input_password"
data-test="input-step2bmanualcreate-passphrase-1"
maxlength="256"
/>
</div>
<div class="line left">
<input
class="input_password2"
type="text"
placeholder="Repeat pass phrase"
id="step_2a_manual_create_input_password2"
data-test="input-step2bmanualcreate-passphrase-2"
maxlength="256"
/>
</div>

<div class="line left">
<label class="input_passphrase_save_label hidden" data-test="input-step2bmanualcreate-save-passphrase-label">
<input type="checkbox" class="input_passphrase_save" data-test="input-step2bmanualcreate-save-passphrase" value="" name="check" />
Remember the pass phrase after closing the browser
</label>
</div>
<div class="line left remove_if_backup_not_allowed">
<label data-test="input-step2bmanualcreate-backup-inbox">
<input type="checkbox" class="input_backup_inbox" value="" name="check" checked="" />
Back up encrypted private key in inbox (recommended)
</label>
</div>

<div class="line left remove_if_pubkey_submitting_not_user_configurable">
<label data-test="input-step2bmanualcreate-submit-pubkey">
<input type="checkbox" class="input_submit_key" value="" name="check" checked="" />
Submit corresponding pubkey to FlowCrypt Attester (recommended)
</label>
</div>
<div class="line left display_none remove_if_pubkey_submitting_not_user_configurable also_submit_alias_key_view">
<label>
<input type="checkbox" class="input_submit_all" disabled="disabled" />
<span>
Also submit the same pubkey for:
<br /><span class="addresses"></span>
</span>
</label>
</div>

<div class="line left">
<label>
Encryption key type:
<select class="key_type" data-test="input-step2bmanualcreate-key-type">
<option value="curve25519">ECC Curve25519</option>
<option value="rsa2048">RSA 2048bit</option>
<option value="rsa3072">RSA 3072bit</option>
<option value="rsa4096">RSA 4096bit</option>
</select>
</label>
</div>

<div class="line left">
<button class="button longer gray action_proceed_private" data-test="input-step2bmanualcreate-create-and-save">CREATE AND SAVE</button>
</div>
</div>
6 changes: 4 additions & 2 deletions extension/chrome/settings/index.htm
Original file line number Diff line number Diff line change
Expand Up @@ -196,11 +196,13 @@ <h1 class="text-center">FlowCrypt Settings</h1>
<div class="row my-keys-row" data-test="container-settings-keys-list">
<div class="settings-border">
<div class="row abs-row">
<div class="col-12" style="padding-left: 0">
<div class="col-12 flex" style="padding-left: 0">
<img src="/img/svgs/key-icon.svg" class="key-icon" alt="key icon" />
<span class="my-keys-copy">My Keys</span>
<span class="text-right keys-link-top-right-container">
<a href="#" class="show_settings_page add_key" data-test="action-open-add-key-page" page="/chrome/settings/modules/add_key.htm">Add Key</a>
<a href="#" class="show_settings_page my_key_link add_key" data-test="action-open-add-key-page" page="/chrome/settings/modules/add_key.htm"
>Add Key</a
>
</span>
</div>
</div>
Expand Down
19 changes: 17 additions & 2 deletions extension/chrome/settings/modules/add_key.htm
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@
<div class="line">
<h1>Add Private Key <span id="spinner_container"></span></h1>
</div>
<div class="line">
<div class="line" id="add_key_option_container">
<ul style="list-style: none; margin-left: 0; padding-left: 0" class="source_selector display_none">
<li>
<input type="radio" name="source" id="source_generate" data-test="source-generate" value="generate" />
<label for="source_generate"> Generate new key </label>
</li>
<li>
<input type="radio" name="source" id="source_backup" value="backup" />
<label for="source_backup"> Load from backup </label>
Expand All @@ -37,6 +41,10 @@ <h1>Add Private Key <span id="spinner_container"></span></h1>
</li>
</ul>
</div>
<div class="source_generate_container display_none">
<div id="generate-key-container"></div>
<div id="backup-template-container"></div>
</div>
<div class="source_file_container display_none">
<a href="#" id="fineuploader_button" class="display_none">file</a>
<div class="line display_none" id="fineuploader"></div>
Expand All @@ -55,7 +63,7 @@ <h1>Add Private Key <span id="spinner_container"></span></h1>
</div>
<div class="line">
<label class="input_passphrase_save_label hidden" data-test="input-save-passphrase-label">
<input type="checkbox" value="" name="check" class="input_passphrase_save" data-test="input-save-passphrase" />
<input type="checkbox" value="" name="check" class="import_input_passphrase_save" data-test="input-save-passphrase" />
Remember the pass phrase after closing the browser
</label>
</div>
Expand All @@ -76,6 +84,13 @@ <h1>Add Private Key <span id="spinner_container"></span></h1>
<script src="/lib/openpgp.js"></script>
<script src="/lib/forge.js"></script>
<script src="/lib/zxcvbn.js"></script>
<script src="/lib/emailjs/punycode.js"></script>
<script src="/lib/emailjs/emailjs-stringencoding.js"></script>
<script src="/lib/emailjs/emailjs-mime-codec.js"></script>
<script src="/lib/emailjs/emailjs-mime-types.js"></script>
<script src="/lib/emailjs/emailjs-addressparser.js"></script>
<script src="/lib/emailjs/emailjs-mime-builder.js"></script>
<script src="/lib/emailjs/emailjs-mime-parser.js"></script>
<script src="add_key.js" type="module"></script>
</body>
</html>
187 changes: 96 additions & 91 deletions extension/chrome/settings/modules/add_key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,109 +17,114 @@ import { ClientConfiguration } from '../../../js/common/client-configuration.js'
import { AcctStore } from '../../../js/common/platform/store/acct-store.js';
import { saveKeysAndPassPhrase, setPassphraseForPrvs } from '../../../js/common/helpers.js';
import { KeyErrors } from '../../elements/shared/key_errors.js';
import { AddKeyGenerateModule } from './add_key_generate_module.js';

View.run(
class AddKeyView extends View {
protected fesUrl?: string;
private readonly acctEmail: string;
private readonly parentTabId: string;
private readonly keyImportUi = new KeyImportUi({ rejectKnown: true });
private readonly gmail: Gmail;
private keyErrors: KeyErrors | undefined;
private clientConfiguration!: ClientConfiguration;
export class AddKeyView extends View {
public readonly acctEmail: string;
public readonly parentTabId: string;
public clientConfiguration!: ClientConfiguration;
public fesUrl?: string;
private readonly keyImportUi = new KeyImportUi({ rejectKnown: true });
private readonly gmail: Gmail;
private keyErrors: KeyErrors | undefined;
private readonly addKeyGenerateModule: AddKeyGenerateModule;

public constructor() {
super();
const uncheckedUrlParams = Url.parse(['acctEmail', 'parentTabId']);
this.acctEmail = Assert.urlParamRequire.string(uncheckedUrlParams, 'acctEmail');
this.parentTabId = Assert.urlParamRequire.string(uncheckedUrlParams, 'parentTabId');
this.gmail = new Gmail(this.acctEmail);
}
public constructor() {
super();
const uncheckedUrlParams = Url.parse(['acctEmail', 'parentTabId']);
this.acctEmail = Assert.urlParamRequire.string(uncheckedUrlParams, 'acctEmail');
this.parentTabId = Assert.urlParamRequire.string(uncheckedUrlParams, 'parentTabId');
this.gmail = new Gmail(this.acctEmail);
this.addKeyGenerateModule = new AddKeyGenerateModule(this);
}

public render = async () => {
const storage = await AcctStore.get(this.acctEmail, ['fesUrl']);
this.fesUrl = storage.fesUrl;
this.clientConfiguration = await ClientConfiguration.newInstance(this.acctEmail);
if (!this.clientConfiguration.forbidStoringPassPhrase()) {
$('.input_passphrase_save_label').removeClass('hidden');
$('.input_passphrase_save').prop('checked', true);
}
if (this.clientConfiguration.usesKeyManager()) {
Xss.sanitizeRender(
'body',
`
public render = async () => {
const storage = await AcctStore.get(this.acctEmail, ['fesUrl']);
this.fesUrl = storage.fesUrl;
this.clientConfiguration = await ClientConfiguration.newInstance(this.acctEmail);
await this.addKeyGenerateModule.initGenerateKeyView();
if (!this.clientConfiguration.forbidStoringPassPhrase()) {
$('.input_passphrase_save_label').removeClass('hidden');
$('.import_input_passphrase_save').prop('checked', true);
}
if (this.clientConfiguration.usesKeyManager()) {
Xss.sanitizeRender(
'body',
`
<br><br>
<div data-test="container-err-text">Please contact your IT staff if you wish to update your keys.</div>
<br><br>
`
);
} else {
$('#content').show();
if (!this.clientConfiguration.forbidStoringPassPhrase()) {
$('.input_passphrase_save').prop('checked', true).prop('disabled', false);
}
await initPassphraseToggle(['input_passphrase']);
this.keyImportUi.initPrvImportSrcForm(this.acctEmail, this.parentTabId);
Xss.sanitizeRender('#spinner_container', Ui.spinner('green') + ' loading..');
await this.loadAndRenderKeyBackupsOrRenderError();
$('.source_selector').css('display', 'block');
$('#spinner_container').text('');
);
} else {
$('#content').show();
if (!this.clientConfiguration.forbidStoringPassPhrase()) {
$('.import_input_passphrase_save').prop('checked', true).prop('disabled', false);
}
};
await initPassphraseToggle(['input_passphrase']);
this.keyImportUi.initPrvImportSrcForm(this.acctEmail, this.parentTabId);
Xss.sanitizeRender('#spinner_container', Ui.spinner('green') + ' loading..');
await this.loadAndRenderKeyBackupsOrRenderError();
$('.source_selector').css('display', 'block');
$('#spinner_container').text('');
}
};

public setHandlers = () => {
$('.action_add_private_key').on('click', this.setHandlerPrevent('double', this.addPrivateKeyHandler));
$('#input_passphrase').keydown(this.setEnterHandlerThatClicks('.action_add_private_key'));
};
public setHandlers = () => {
$('.action_add_private_key').on('click', this.setHandlerPrevent('double', this.addPrivateKeyHandler));
$('#input_passphrase').keydown(this.setEnterHandlerThatClicks('.action_add_private_key'));
this.addKeyGenerateModule.setHandlers();
};

private loadAndRenderKeyBackupsOrRenderError = async () => {
try {
const backups = await this.gmail.fetchKeyBackups();
if (!backups.longids.backups.length) {
$('label[for=source_backup]').text('Load from backup (no backups found)').css('color', '#AAA');
$('#source_backup').prop('disabled', true);
} else if (backups.longids.backupsNotImported.length) {
$('label[for=source_backup]').text(`Load from backup (${backups.longids.backupsNotImported.length} new to import)`);
} else {
$('label[for=source_backup]').text(`Load from backup (${backups.longids.backups.length} already loaded)`).css('color', '#AAA');
$('#source_backup').prop('disabled', true);
}
} catch (e) {
if (ApiErr.isAuthErr(e)) {
BrowserMsg.send.notificationShowAuthPopupNeeded(this.parentTabId, { acctEmail: this.acctEmail });
}
$('label[for=source_backup]').text('Load from backup (error checking backups)').css('color', '#AAA');
private loadAndRenderKeyBackupsOrRenderError = async () => {
try {
const backups = await this.gmail.fetchKeyBackups();
if (!backups.longids.backups.length) {
$('label[for=source_backup]').text('Load from backup (no backups found)').css('color', '#AAA');
$('#source_backup').prop('disabled', true);
} else if (backups.longids.backupsNotImported.length) {
$('label[for=source_backup]').text(`Load from backup (${backups.longids.backupsNotImported.length} new to import)`);
} else {
$('label[for=source_backup]').text(`Load from backup (${backups.longids.backups.length} already loaded)`).css('color', '#AAA');
$('#source_backup').prop('disabled', true);
}
} catch (e) {
if (ApiErr.isAuthErr(e)) {
BrowserMsg.send.notificationShowAuthPopupNeeded(this.parentTabId, { acctEmail: this.acctEmail });
}
};
$('label[for=source_backup]').text('Load from backup (error checking backups)').css('color', '#AAA');
$('#source_backup').prop('disabled', true);
}
};

private saveKeyAndContinue = async (key: Key) => {
await saveKeysAndPassPhrase(this.acctEmail, [key]); // resulting new_key checked above
/* eslint-disable @typescript-eslint/naming-convention */
await setPassphraseForPrvs(this.clientConfiguration, this.acctEmail, [key], {
passphrase: String($('.input_passphrase').val()),
passphrase_save: !!$('.input_passphrase_save').prop('checked'),
passphrase_ensure_single_copy: false, // we require KeyImportUi to rejectKnown keys
});
/* eslint-enable @typescript-eslint/naming-convention */
BrowserMsg.send.reload(this.parentTabId, { advanced: true });
};
private saveKeyAndContinue = async (key: Key) => {
await saveKeysAndPassPhrase(this.acctEmail, [key]); // resulting new_key checked above
/* eslint-disable @typescript-eslint/naming-convention */
await setPassphraseForPrvs(this.clientConfiguration, this.acctEmail, [key], {
passphrase: String($('.input_passphrase').val()),
passphrase_save: !!$('.import_input_passphrase_save').prop('checked'),
passphrase_ensure_single_copy: false, // we require KeyImportUi to rejectKnown keys
});
/* eslint-enable @typescript-eslint/naming-convention */
BrowserMsg.send.reload(this.parentTabId, { advanced: true });
};

private addPrivateKeyHandler = async (submitBtn: HTMLElement) => {
if (submitBtn.className.includes('gray')) {
await Ui.modal.warning('Please double check the pass phrase input field for any issues.');
return;
}
try {
const checked = await this.keyImportUi.checkPrv(this.acctEmail, String($('.input_private_key').val()), String($('.input_passphrase').val()));
if (checked) {
await this.saveKeyAndContinue(checked.encrypted);
}
} catch (e) {
this.keyErrors = new KeyErrors(this.fesUrl || '', this.acctEmail, this.parentTabId, this.clientConfiguration);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
await this.keyErrors.handlePrivateKeyError(e, e.encrypted, undefined);
private addPrivateKeyHandler = async (submitBtn: HTMLElement) => {
if (submitBtn.className.includes('gray')) {
await Ui.modal.warning('Please double check the pass phrase input field for any issues.');
return;
}
try {
const checked = await this.keyImportUi.checkPrv(this.acctEmail, String($('.input_private_key').val()), String($('.input_passphrase').val()));
if (checked) {
await this.saveKeyAndContinue(checked.encrypted);
}
};
}
);
} catch (e) {
this.keyErrors = new KeyErrors(this.fesUrl || '', this.acctEmail, this.parentTabId, this.clientConfiguration);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
await this.keyErrors.handlePrivateKeyError(e, e.encrypted, undefined);
}
};
}

View.run(AddKeyView);
Loading
Loading