diff --git a/extension/chrome/elements/compose-modules/compose-render-module.ts b/extension/chrome/elements/compose-modules/compose-render-module.ts index ec435e0d58f..01adf47d024 100644 --- a/extension/chrome/elements/compose-modules/compose-render-module.ts +++ b/extension/chrome/elements/compose-modules/compose-render-module.ts @@ -436,17 +436,17 @@ export class ComposeRenderModule extends ViewModule { return; // key is invalid } const key = await KeyUtil.parse(normalizedPub); - if (!key.emails.length) { - // no users is not desired + const firstUserWithEmail = key.users.find(u => u.email); + if (!firstUserWithEmail?.email) { await Ui.modal.warning(`There are no email addresses listed in this Public Key - don't know who this key belongs to.`); return; } - await ContactStore.update(undefined, key.emails[0], { - name: Str.parseEmail(key.identities[0]).name, + await ContactStore.update(undefined, firstUserWithEmail.email, { + name: firstUserWithEmail.name, pubkey: normalizedPub, pubkeyLastCheck: Date.now(), }); - this.view.S.cached('input_to').val(key.emails[0]); + this.view.S.cached('input_to').val(firstUserWithEmail.email); await this.view.recipientsModule.parseRenderRecipients(this.view.S.cached('input_to')); } }; diff --git a/extension/chrome/elements/compose-modules/formatters/general-mail-formatter.ts b/extension/chrome/elements/compose-modules/formatters/general-mail-formatter.ts index 9af42a3a3b1..08629dbf36c 100644 --- a/extension/chrome/elements/compose-modules/formatters/general-mail-formatter.ts +++ b/extension/chrome/elements/compose-modules/formatters/general-mail-formatter.ts @@ -97,7 +97,7 @@ export class GeneralMailFormatter { // This is important to consider when an email address with a email tag is present on the // signingKey, as technically it is the same email. const baseSenderEmail = senderEmail.includes('+') ? senderEmail.replace(/(.+)(?=\+).*(?=@)/, '$1') : senderEmail; - return signingKey.emails.some(email => email.includes(baseSenderEmail)); + return signingKey.users.some(u => u.email?.includes(baseSenderEmail)); } return true; }; diff --git a/extension/chrome/elements/pgp_pubkey.ts b/extension/chrome/elements/pgp_pubkey.ts index 53b5d829e57..c8ac696180d 100644 --- a/extension/chrome/elements/pgp_pubkey.ts +++ b/extension/chrome/elements/pgp_pubkey.ts @@ -77,7 +77,7 @@ View.run( } else { let emailText = ''; if (this.parsedPublicKeys.length === 1) { - const email = this.firstParsedPublicKey.emails[0]; + const email = KeyUtil.getPrimaryEmail(this.firstParsedPublicKey); if (email) { emailText = email; $('.input_email').val(email); // checked above @@ -90,7 +90,7 @@ View.run( Xss.escape( ' for ' + this.parsedPublicKeys - .map(pub => pub.emails[0]) + .map(pub => KeyUtil.getPrimaryEmail(pub)) .filter(e => !!e) .join(', ') ) @@ -193,9 +193,9 @@ View.run( $('.error_introduce_label').html(`This OpenPGP key is not usable.
(${await this.getErrorText()})`); // xss-escaped $('.hide_if_error').hide(); $('.fingerprints, .add_contact, #manual_import_warning').remove(); - const email = this.firstParsedPublicKey?.emails[0]; + const email = this.firstParsedPublicKey ? KeyUtil.getPrimaryEmail(this.firstParsedPublicKey) : undefined; if (email) { - $('.error_container .input_error_email').val(`${this.firstParsedPublicKey?.emails[0]}`); + $('.error_container .input_error_email').val(email); } else { $('.error_container .input_error_email').hide(); } @@ -208,7 +208,7 @@ View.run( const emails = new Set(); for (const pubkey of this.parsedPublicKeys!) { /* eslint-enable @typescript-eslint/no-non-null-assertion */ - const email = pubkey.emails[0]; + const email = KeyUtil.getPrimaryEmail(pubkey); if (email) { await ContactStore.update(undefined, email, { pubkey: KeyUtil.armor(pubkey) }); emails.add(email); diff --git a/extension/chrome/settings/index.ts b/extension/chrome/settings/index.ts index 3f977ee6066..81d18a933a8 100644 --- a/extension/chrome/settings/index.ts +++ b/extension/chrome/settings/index.ts @@ -537,7 +537,7 @@ View.run( removeKeyBtn = `remove`; } - const escapedEmail = Xss.escape(prv.emails[0] || ''); + const escapedEmail = Xss.escape(KeyUtil.getPrimaryEmail(prv) || ''); const escapedLink = `${escapedEmail}`; const fpHtml = `${Str.spaced(Xss.escape(ki.fingerprints[0]))}`; diff --git a/extension/chrome/settings/modules/add_key_generate_module.ts b/extension/chrome/settings/modules/add_key_generate_module.ts index 01d07d7dd1e..82dd6ef2d18 100644 --- a/extension/chrome/settings/modules/add_key_generate_module.ts +++ b/extension/chrome/settings/modules/add_key_generate_module.ts @@ -90,7 +90,10 @@ export class AddKeyGenerateModule extends ViewModule { const adminPubkey = this.view.clientConfiguration.getPublicKeyForPrivateKeyBackupToDesignatedMailbox(); if (adminPubkey) { const msgEncryptionKey = await KeyUtil.parse(adminPubkey); - const destinationEmail = msgEncryptionKey.emails[0]; + const destinationEmail = KeyUtil.getPrimaryEmail(msgEncryptionKey); + if (!destinationEmail) { + throw new Error('Admin public key does not have an email address'); + } try { const privateKey = await KeyStore.get(this.view.acctEmail); const primaryKeyId = privateKey[0].id; diff --git a/extension/chrome/settings/modules/contacts.ts b/extension/chrome/settings/modules/contacts.ts index 39620574349..8912a086d96 100644 --- a/extension/chrome/settings/modules/contacts.ts +++ b/extension/chrome/settings/modules/contacts.ts @@ -181,7 +181,7 @@ View.run( [ `Type: ${key.family}`, `Fingerprint: ${Str.spaced(key.id || 'none')}`, - `Users: ${key.emails?.join(', ')}`, + `Users: ${key.users?.map(u => u.email).filter(Boolean).join(', ')}`, `Created on: ${key.created ? new Date(key.created) : ''}`, `Expiration: ${key.expiration ? new Date(key.expiration) : 'Does not expire'}`, `Last signature: ${key.lastModified ? new Date(key.lastModified) : ''}`, diff --git a/extension/chrome/settings/modules/my_key.ts b/extension/chrome/settings/modules/my_key.ts index f99f8c76ada..e3fd46c456d 100644 --- a/extension/chrome/settings/modules/my_key.ts +++ b/extension/chrome/settings/modules/my_key.ts @@ -49,7 +49,8 @@ View.run( $('.action_view_user_ids').attr('href', this.myKeyUserIdsUrl); $('.action_view_update').attr('href', this.myKeyUpdateUrl); $('.fingerprint').text(Str.spaced(this.keyInfo.fingerprints[0])); - Xss.sanitizeRender('.email', this.pubKey.emails.map(email => `${Xss.escape(email)}`).join(', ')); + const emails = this.pubKey.users.map(u => u.email).filter((e): e is string => !!e); + Xss.sanitizeRender('.email', emails.map(email => `${Xss.escape(email)}`).join(', ')); const expiration = this.pubKey.expiration; const creation = Str.datetimeToDate(Str.fromDate(new Date(this.pubKey.created))); Xss.sanitizeRender('.key_status_contatiner', KeyUtil.statusHtml(this.keyInfo.longid, this.pubKey)); diff --git a/extension/chrome/settings/modules/my_key_update.ts b/extension/chrome/settings/modules/my_key_update.ts index 52a467c897b..0658f1601e5 100644 --- a/extension/chrome/settings/modules/my_key_update.ts +++ b/extension/chrome/settings/modules/my_key_update.ts @@ -115,7 +115,7 @@ View.run( KeyImportUi.allowReselect(); if (typeof updatedKey === 'undefined') { await Ui.modal.warning(Lang.setup.keyFormattedWell(this.prvHeaders.begin, String(this.prvHeaders.end)), Ui.getTestCompatibilityLink(this.acctEmail)); - } else if (updatedKeyEncrypted.identities.length === 0) { + } else if (updatedKeyEncrypted.users.length === 0) { throw new KeyCanBeFixed(updatedKeyEncrypted); } else if (updatedKey.isPublic) { await Ui.modal.warning( diff --git a/extension/chrome/settings/modules/my_key_user_ids.ts b/extension/chrome/settings/modules/my_key_user_ids.ts index 882ea160281..0381807aeaa 100644 --- a/extension/chrome/settings/modules/my_key_user_ids.ts +++ b/extension/chrome/settings/modules/my_key_user_ids.ts @@ -30,7 +30,7 @@ View.run( Assert.abortAndRenderErrorIfKeyinfoEmpty(this.ki ? [this.ki] : []); $('.action_show_public_key').attr('href', this.myKeyUrl); const prv = await KeyUtil.parse(this.ki.private); - Xss.sanitizeRender('.user_ids', prv.identities.map((uid: string) => `
${Xss.escape(uid)}
`).join('')); + Xss.sanitizeRender('.user_ids', prv.users.map(u => `
${Xss.escape(u.full)}
`).join('')); $('.email').text(this.acctEmail); $('.fingerprint').text(Str.spaced(this.ki.fingerprints[0])); }; diff --git a/extension/chrome/settings/setup/setup-create-key.ts b/extension/chrome/settings/setup/setup-create-key.ts index b7a1768b096..d964d9d1efd 100644 --- a/extension/chrome/settings/setup/setup-create-key.ts +++ b/extension/chrome/settings/setup/setup-create-key.ts @@ -44,7 +44,10 @@ export class SetupCreateKeyModule { const adminPubkey = this.view.clientConfiguration.getPublicKeyForPrivateKeyBackupToDesignatedMailbox(); if (adminPubkey) { const msgEncryptionKey = await KeyUtil.parse(adminPubkey); - const destinationEmail = msgEncryptionKey.emails[0]; + const destinationEmail = KeyUtil.getPrimaryEmail(msgEncryptionKey); + if (!destinationEmail) { + throw new Error('Admin public key does not have an email address'); + } try { const privateKey = await KeyStore.get(this.view.acctEmail); const primaryKeyId = privateKey[0].id; diff --git a/extension/js/common/core/common.ts b/extension/js/common/core/common.ts index 6601cdd90ee..b75e9ccab43 100644 --- a/extension/js/common/core/common.ts +++ b/extension/js/common/core/common.ts @@ -12,6 +12,7 @@ export type UrlParam = string | number | null | undefined | boolean | string[]; export type UrlParams = Dict; export type PromiseCancellation = { cancel: boolean }; export type EmailParts = { email: string; name?: string }; +export type ParsedEmail = { email: string | undefined; name: string | undefined; full: string }; export const CID_PATTERN = /^cid:(.+)/; @@ -88,7 +89,7 @@ export class Str { public static readonly ltrChars = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0370-\u0590\u0800-\u1FFF\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF'; public static readonly rtlChars = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC'; - public static parseEmail = (full: string, flag: 'VALIDATE' | 'DO-NOT-VALIDATE' = 'VALIDATE') => { + public static parseEmail = (full: string, flag: 'VALIDATE' | 'DO-NOT-VALIDATE' = 'VALIDATE'): ParsedEmail => { let email: string | undefined; let name: string | undefined; if (full.includes('<') && full.includes('>')) { diff --git a/extension/js/common/core/crypto/key.ts b/extension/js/common/core/crypto/key.ts index eb017bb436f..144f6f6db5c 100644 --- a/extension/js/common/core/crypto/key.ts +++ b/extension/js/common/core/crypto/key.ts @@ -11,7 +11,7 @@ import { OpenPGPKey } from './pgp/openpgp-key.js'; import type * as OpenPGP from 'openpgp'; import { SmimeKey } from './smime/smime-key.js'; import { MsgBlock } from '../msg-block.js'; -import { EmailParts, Str } from '../common.js'; +import { EmailParts, ParsedEmail, Str } from '../common.js'; /** * This is a common Key interface for both OpenPGP and X.509 keys. @@ -38,8 +38,7 @@ export interface Key extends KeyIdentity { usableForSigningButExpired: boolean; missingPrivateKeyForSigning: boolean; missingPrivateKeyForDecryption: boolean; - emails: string[]; - identities: string[]; + users: ParsedEmail[]; fullyDecrypted: boolean; fullyEncrypted: boolean; isPublic: boolean; // isPublic and isPrivate are mutually exclusive @@ -122,13 +121,14 @@ export class KeyUtil { public static filterKeysByTypeAndSenderEmail(keys: KeyInfoWithIdentity[], email: string, type: 'openpgp' | 'x509' | undefined): KeyInfoWithIdentity[] { let foundKeys: KeyInfoWithIdentity[] = []; + const lowerEmail = email.toLowerCase(); if (type) { - foundKeys = keys.filter(key => key.emails?.includes(email.toLowerCase()) && key.family === type); + foundKeys = keys.filter(key => key.emails?.includes(lowerEmail) && key.family === type); if (!foundKeys.length) { foundKeys = keys.filter(key => key.family === type); } } else { - foundKeys = keys.filter(key => key.emails?.includes(email.toLowerCase())); + foundKeys = keys.filter(key => key.emails?.includes(lowerEmail)); if (!foundKeys.length) { foundKeys = [...keys]; } @@ -440,7 +440,7 @@ export class KeyUtil { private: KeyUtil.armor(prv), public: KeyUtil.armor(pubkey), longid: KeyUtil.getPrimaryLongid(pubkey), - emails: prv.emails, + emails: prv.users.map(u => u.email).filter((e): e is string => !!e), fingerprints: prv.allIds, id: prv.id, family: prv.family, @@ -461,6 +461,10 @@ export class KeyUtil { return SmimeKey.getKeyLongid(pubkey); } + public static getPrimaryEmail(key: Key): string | undefined { + return key.users.find(user => user.email)?.email; + } + public static getKeyInfoLongids(ki: KeyInfoWithIdentityAndOptionalPp): string[] { if (ki.family !== 'x509') { return ki.fingerprints.map(fp => OpenPGPKey.fingerprintToLongid(fp)); @@ -488,7 +492,7 @@ export class KeyUtil { public static async parseAndArmorKeys(binaryKeysData: Uint8Array): Promise { const { keys } = await KeyUtil.readMany(Buf.fromUint8(binaryKeysData)); - return keys.map(k => ({ id: k.id, emails: k.emails, armored: KeyUtil.armor(k), family: k.family })); + return keys.map(k => ({ id: k.id, emails: k.users.map(u => u.email).filter((e): e is string => !!e), armored: KeyUtil.armor(k), family: k.family })); } public static validateChecksum(armoredText: string): boolean { diff --git a/extension/js/common/core/crypto/pgp/openpgp-key.ts b/extension/js/common/core/crypto/pgp/openpgp-key.ts index b07b054a2b9..09ca9b0325c 100644 --- a/extension/js/common/core/crypto/pgp/openpgp-key.ts +++ b/extension/js/common/core/crypto/pgp/openpgp-key.ts @@ -2,7 +2,7 @@ import { Key, PrvPacket, KeyAlgo, KeyUtil, UnexpectedKeyTypeError, PubkeyInfo } from '../key.js'; import { OpenPGPDataType, opgp } from './openpgpjs-custom.js'; import { Catch } from '../../../platform/catch.js'; -import { Str, Value } from '../../common.js'; +import { ParsedEmail, Str, Value } from '../../common.js'; import { Buf } from '../../buf.js'; import type * as OpenPGP from 'openpgp'; import { PgpMsgMethod, VerifyRes } from './msg-util.js'; @@ -188,7 +188,7 @@ export class OpenPGPKey { * is done on the original supplied object. */ public static convertExternalLibraryObjToKey = async (opgpKey: OpenPGP.Key, keyToUpdate?: Key): Promise => { - const { identities, emails } = await OpenPGPKey.getSortedUserids(opgpKey); + const users = await OpenPGPKey.getSortedUserids(opgpKey); let lastModified: undefined | number; try { lastModified = await OpenPGPKey.getLastSigTime(opgpKey); @@ -221,10 +221,7 @@ export class OpenPGPKey { usableForSigningButExpired: !signingKey && !!signingKeyIgnoringExpiration, missingPrivateKeyForSigning, missingPrivateKeyForDecryption, - // valid emails extracted from uids - emails, - // full uids that have valid emails in them - identities, + users, lastModified, expiration, created: opgpKey.getCreationTime().getTime(), @@ -541,28 +538,25 @@ export class OpenPGPKey { return expirationTime instanceof Date ? expirationTime : undefined; // we don't differ between Infinity and null } - private static async getSortedUserids(key: OpenPGP.Key) { + private static async getSortedUserids(key: OpenPGP.Key): Promise { const primaryUser = await Catch.undefinedOnException(key.getPrimaryUser()); // if there is no good enough user id to serve as primary identity, we assume other user ids are even worse if (primaryUser?.user?.userID?.userID) { const primaryUserId = primaryUser.user.userID.userID; - const identities = [ + const rawIdentities = [ primaryUserId, // put the "primary" identity first // other identities go in indeterministic order ...Value.arr.unique((await key.verifyAllUsers()).filter(x => x.valid && x.userID !== primaryUserId).map(x => x.userID)), ]; - const emails = identities - .filter(userId => !!userId) - .map(userid => Str.parseEmail(userid).email) - .filter(Boolean); - if (emails.length === identities.length || !key.isPrivate()) { - // OpenPGP.js uses RFC 5322 `email-addresses` parser, so we expect all identities to contain a valid e-mail address - // Return empty emails and identities is a way to later throw KeyCanBeFixed exception, allowing the user to reformat the key + const users = rawIdentities.filter(uid => !!uid).map(uid => Str.parseEmail(uid)); + if ((users.length === rawIdentities.length && users.every(u => u.email)) || !key.isPrivate()) { + // OpenPGP.js uses RFC 5322 `email-addresses` parser, so we expect all identities to contain a valid e-mail address. + // Returning empty users is a way to later throw KeyCanBeFixed exception, allowing the user to reformat the key // (e.g. adding signature for UserIDs etc.) But this makes sense for private keys only. - return { emails, identities }; + return users; } } - return { emails: [], identities: [] }; + return []; } // mimicks OpenPGP.helper.getLatestValidSignature diff --git a/extension/js/common/core/crypto/smime/smime-key.ts b/extension/js/common/core/crypto/smime/smime-key.ts index c2fcc3aec9c..515d91cd4ff 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 { Key, UnexpectedKeyTypeError } from '../key.js'; -import { Str } from '../../common.js'; +import { ParsedEmail, Str } from '../../common.js'; import { UnreportableError } from '../../../platform/error-report.js'; import { PgpArmor } from '../pgp/pgp-armor.js'; import { Buf } from '../../buf.js'; @@ -242,23 +242,24 @@ export class SmimeKey { return parsed.filter((c, i) => !parsed.some((other, j) => j !== i && other.certificate.isIssuer(c.certificate))); } - private static getNormalizedEmailsFromCertificate(certificate: forge.pki.Certificate): string[] { + private static getUsersFromCertificate(certificate: forge.pki.Certificate): ParsedEmail[] { const emailFromSubject = (certificate.subject.getField('CN') as { value: string }).value; - const normalizedEmail = Str.parseEmail(emailFromSubject).email; - const emails = normalizedEmail ? [normalizedEmail] : []; + const parsedSubject = Str.parseEmail(emailFromSubject); + const users: ParsedEmail[] = parsedSubject.email ? [parsedSubject] : []; // search for e-mails in subjectAltName extension const subjectAltName = certificate.getExtension('subjectAltName') as { altNames: { type: number; value: string }[]; }; if (subjectAltName?.altNames) { - const emailsFromAltNames = subjectAltName.altNames - .filter(entry => entry.type === 1) - .map(entry => Str.parseEmail(entry.value).email) - .filter(Boolean); - emails.push(...(emailsFromAltNames as string[])); + for (const entry of subjectAltName.altNames.filter(e => e.type === 1)) { + const parsed = Str.parseEmail(entry.value); + if (parsed.email && !users.some(u => u.email === parsed.email)) { + users.push(parsed); + } + } } - if (emails.length) { - return emails.filter((value, index, self) => self.indexOf(value) === index); + if (users.length) { + return users; } throw new UnreportableError(`This S/MIME x.509 certificate has an invalid recipient email: ${emailFromSubject}`); } @@ -282,7 +283,7 @@ export class SmimeKey { } } const fingerprint = this.forge.pki.getPublicKeyFingerprint(certificate.publicKey, { encoding: 'hex' }).toUpperCase(); - const emails = SmimeKey.getNormalizedEmailsFromCertificate(certificate); + const users = SmimeKey.getUsersFromCertificate(certificate); const issuerAndSerialNumberAsn1 = SmimeKey.createIssuerAndSerialNumberAsn1( this.forge.pki.distinguishedNameToAsn1(certificate.issuer), certificate.serialNumber @@ -300,8 +301,7 @@ export class SmimeKey { usableForSigning: usableIgnoringExpiration && !expired, usableForEncryptionButExpired: usableIgnoringExpiration && expired, usableForSigningButExpired: usableIgnoringExpiration && expired, - emails, - identities: emails, + users, created: SmimeKey.dateToNumber(certificate.validity.notBefore), lastModified: SmimeKey.dateToNumber(certificate.validity.notBefore), expiration, diff --git a/extension/js/common/settings.ts b/extension/js/common/settings.ts index dfa92f9c215..3ce74d4867b 100644 --- a/extension/js/common/settings.ts +++ b/extension/js/common/settings.ts @@ -214,7 +214,7 @@ export class Settings { passphrase: string, backUrl: string ): Promise { - const uids = origPrv.identities; + const uids = origPrv.users.map(u => u.full); if (!uids.length) { uids.push(acctEmail); } diff --git a/extension/js/common/ui/key-import-ui.ts b/extension/js/common/ui/key-import-ui.ts index 1db45d9b96a..bd039697aca 100644 --- a/extension/js/common/ui/key-import-ui.ts +++ b/extension/js/common/ui/key-import-ui.ts @@ -185,9 +185,10 @@ export class KeyImportUi { const prv = await Catch.undefinedOnException(KeyUtil.parse(String($(target).val()))); if (prv !== undefined) { $('.action_add_private_key').removeClass('btn_disabled').removeAttr('disabled'); - for (const email of prv.emails) { + for (const user of prv.users) { + if (!user.email) continue; for (const inputCheckboxesWithEmail of $('.input_email_alias_submit_pubkey')) { - if (String($(inputCheckboxesWithEmail).data('email')) === email) { + if (String($(inputCheckboxesWithEmail).data('email')) === user.email) { $(inputCheckboxesWithEmail).prop('checked', true); } } @@ -265,7 +266,7 @@ export class KeyImportUi { await this.decryptAndEncryptAsNeeded(decrypted, encrypted, passphrase, contactSubsentence); await this.checkEncryptionPrvIfSelected(decrypted, encrypted, contactSubsentence); await this.checkSigningIfSelected(decrypted, contactSubsentence); - if (encrypted.identities.length === 0) { + if (encrypted.users.length === 0) { throw new KeyCanBeFixed(encrypted); } // mandatory checks have passed, now display warnings diff --git a/test/source/mock/key-manager/key-manager-endpoints.ts b/test/source/mock/key-manager/key-manager-endpoints.ts index 2f72ae9659e..752331ea86e 100644 --- a/test/source/mock/key-manager/key-manager-endpoints.ts +++ b/test/source/mock/key-manager/key-manager-endpoints.ts @@ -45,9 +45,9 @@ export const getMockKeyManagerEndpoints = (oauth: OauthMock, config: KeyManagerC const prv = await KeyUtil.parseMany(privateKey); expect(prv).to.have.length(1); expect(prv[0].algo.bits).to.equal(2048); - expect(prv[0].identities).to.have.length(1); + expect(prv[0].users).to.have.length(1); if (expectation.identity) { - expect(prv[0].identities[0]).to.equal(expectation.identity); + expect(prv[0].users[0].full).to.equal(expectation.identity); } expect(prv[0].isPrivate).to.be.true; expect(prv[0].fullyDecrypted).to.be.true; diff --git a/test/source/tests/decrypt.ts b/test/source/tests/decrypt.ts index f8fdb1b09f7..cc4f784735c 100644 --- a/test/source/tests/decrypt.ts +++ b/test/source/tests/decrypt.ts @@ -1657,6 +1657,7 @@ XZ8r4OC6sguP/yozWlkG+7dDxsgKQVBENeG6Lw== await gmailPage.waitAll('iframe'); expect((await gmailPage.getFramesUrls(['pgp_block.htm'])).length).to.equal(1); expect((await gmailPage.getFramesUrls(['attachment.htm'])).length).to.equal(0); // invisible + await Util.sleep(2); await assertOutgoingPubkeyFrameIsMinimized(gmailPage); }) ); diff --git a/test/source/tests/settings.ts b/test/source/tests/settings.ts index 530d9d61196..06607e7b328 100644 --- a/test/source/tests/settings.ts +++ b/test/source/tests/settings.ts @@ -1301,8 +1301,8 @@ export const defineSettingsTests = (testVariant: TestVariant, testWithBrowser: T const newPubkey = (newContact.sortedPubkeys as PubkeyInfoWithLastCheck[])[0].pubkey; expect(newPubkey.lastModified).to.equal(1689250647000); // we now have two uids - expect(newPubkey.identities).to.eql(['Additional Name ', 'flowcrypt.test.key.multiple@gmail.com']); - expect(newPubkey.emails).to.eql(['flowcrypt.test@example.com', 'flowcrypt.test.key.multiple@gmail.com']); + expect(newPubkey.users.map(u => u.full)).to.eql(['Additional Name ', 'flowcrypt.test.key.multiple@gmail.com']); + expect(newPubkey.users.map(u => u.email)).to.eql(['flowcrypt.test@example.com', 'flowcrypt.test.key.multiple@gmail.com']); await settingsPage.close(); await dbPage.close(); }) diff --git a/test/source/tests/setup.ts b/test/source/tests/setup.ts index 720618cefa0..257ffdde63a 100644 --- a/test/source/tests/setup.ts +++ b/test/source/tests/setup.ts @@ -2620,7 +2620,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== }, }, }); - expect((await KeyUtil.parse(testConstants.keyMultiAliasedUser)).emails.length).to.equals(3); + expect((await KeyUtil.parse(testConstants.keyMultiAliasedUser)).users.length).to.equals(3); const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); await SetupPageRecipe.manualEnter( settingsPage, diff --git a/test/source/tests/unit-node.ts b/test/source/tests/unit-node.ts index ff84049154b..51709d7b634 100644 --- a/test/source/tests/unit-node.ts +++ b/test/source/tests/unit-node.ts @@ -436,10 +436,10 @@ qC2PFoU1J4aEVe5Jz2yovJnzkx/aa0Hs4g0= expect(key.usableForSigning).to.equal(true); expect(key.usableForEncryptionButExpired).to.equal(false); expect(key.usableForSigningButExpired).to.equal(false); - expect(key.emails.length).to.equal(1); - expect(key.emails[0]).to.equal('smime@recipient.com'); - expect(key.identities.length).to.equal(1); - expect(key.identities[0]).to.equal('smime@recipient.com'); + expect(key.users.length).to.equal(1); + expect(key.users[0].email).to.equal('smime@recipient.com'); + expect(key.users.length).to.equal(1); + expect(key.users[0].full).to.equal('smime@recipient.com'); expect(key.isPublic).to.equal(true); expect(key.isPrivate).to.equal(false); expect(key.expiration).to.not.equal(undefined); @@ -573,10 +573,10 @@ sOLAw7KgpiL2+0v777saxSO5vtufJCKk4OOEaVDufeijlejKTM+H7twVer4iGqiW expect(key.usableForEncryptionButExpired).equal(true); expect(key.missingPrivateKeyForDecryption).to.equal(false); expect(key.missingPrivateKeyForSigning).to.equal(false); - expect(key.emails.length).to.equal(1); - expect(key.emails[0]).to.equal('flowcrypt@metacode.biz'); - expect(key.identities.length).to.equal(1); - expect(key.identities[0]).to.equal('Testing '); + expect(key.users.length).to.equal(1); + expect(key.users[0].email).to.equal('flowcrypt@metacode.biz'); + expect(key.users.length).to.equal(1); + expect(key.users[0].full).to.equal('Testing '); expect(key.isPublic).equal(false); expect(key.isPrivate).equal(true); expect(key.expiration).to.equal(63074017000); @@ -648,10 +648,10 @@ cmKFmmDYm+rrWuAv6Q== expect(key.usableForSigningButExpired).equal(false); expect(key.missingPrivateKeyForDecryption).to.equal(false); expect(key.missingPrivateKeyForSigning).to.equal(false); - expect(key.emails.length).to.equal(1); - expect(key.emails[0]).to.equal('expiration_100years@test.com'); - expect(key.identities.length).to.equal(1); - expect(key.identities[0]).to.equal('Testing '); + expect(key.users.length).to.equal(1); + expect(key.users[0].email).to.equal('expiration_100years@test.com'); + expect(key.users.length).to.equal(1); + expect(key.users[0].full).to.equal('Testing '); expect(key.isPublic).equal(false); expect(key.isPrivate).equal(true); expect(key.expiration).to.equal(4773398996000); @@ -1045,8 +1045,8 @@ jLwe8W9IMt765T5x5oux9MmPDXF05xHfm4qfH/BMO3a802x5u2gJjJjuknrFdgXY 9Q== -----END CERTIFICATE-----`; const key = await KeyUtil.parse(pem); - expect(key.emails.length).to.equal(1); - expect(key.emails[0]).to.equal('email@embedded.in.subj.alt.name'); + expect(key.users.length).to.equal(1); + expect(key.users[0].email).to.equal('email@embedded.in.subj.alt.name'); t.pass(); }); @@ -1353,10 +1353,10 @@ jSB6A93JmnQGIkAem/kzGkKclmfAdGfc4FS+3Cn+6Q==Xmrz 0 ); const pub = await KeyUtil.parse(key.public); - expect(pub.emails[0]).to.equal('key1@test.com'); - expect(pub.identities[0]).to.equal('Key1 '); - expect(pub.emails[1]).to.equal('key2@test.com'); - expect(pub.identities[1]).to.equal('Key2 '); + expect(pub.users[0].email).to.equal('key1@test.com'); + expect(pub.users[0].full).to.equal('Key1 '); + expect(pub.users[1].email).to.equal('key2@test.com'); + expect(pub.users[1].full).to.equal('Key2 '); t.pass(); }); @@ -1467,10 +1467,10 @@ qykKoFAFwEoCtcZ3snFuWp8BAIzRBYJSfZzlvlyyPhrbXJoYSICGNy/5x7noXjp/ ByeOAQDnTbQi4XwXJrU4A8Nl9eyz16ZWUzEPwfWgahIG1eQDDA== =eyAR -----END PGP PRIVATE KEY BLOCK-----`); - expect(key.identities).to.have.length(1); - expect(key.identities).to.eql(['first@mock.test']); - expect(key.emails).to.have.length(1); - expect(key.emails).to.eql(['first@mock.test']); + expect(key.users).to.have.length(1); + expect(key.users.map(u => u.full)).to.eql(['first@mock.test']); + expect(key.users).to.have.length(1); + expect(key.users.map(u => u.email)).to.eql(['first@mock.test']); const result = await KeyUtil.diagnose(key, ''); expect(result.get('Primary User')).to.equal('first@mock.test'); expect(result.get('User id 0')).to.equal('* REVOKED, INVALID OR MISSING SIGNATURE * '); @@ -2388,8 +2388,8 @@ PBcqDCjq5jgMhU1oyVclRK7jJdmu0Azvwo2lleLAFLdCzHEXWXUz expect(parsed.length).to.be.equal(1); expect(parsed[0].id).to.be.equal('60EFFE4DF7B2114A77021459C273F0AA864AFF7F'); expect(parsed[0].family).to.be.equal('x509'); - expect(parsed[0].emails.length).to.be.equal(1); - expect(parsed[0].emails[0]).to.be.equal('test@example.com'); + expect(parsed[0].users.length).to.be.equal(1); + expect(parsed[0].users[0].email).to.be.equal('test@example.com'); expect(parsed[0].isPrivate).to.be.equal(true); expect(parsed[0].isPublic).to.be.equal(false); t.pass(); @@ -2400,8 +2400,8 @@ PBcqDCjq5jgMhU1oyVclRK7jJdmu0Azvwo2lleLAFLdCzHEXWXUz let parsed = await KeyUtil.parse(p8); expect(parsed.id).to.equal('9B5FCFF576A032495AFE77805354351B39AB3BC6'); expect(parsed.family).to.equal('x509'); - expect(parsed.emails.length).to.equal(1); - expect(parsed.emails[0]).to.equal('human@flowcrypt.com'); + expect(parsed.users.length).to.equal(1); + expect(parsed.users[0].email).to.equal('human@flowcrypt.com'); expect(parsed.isPrivate).to.equal(true); expect(parsed.isPublic).to.equal(false); expect(parsed.fullyDecrypted).to.equal(false); @@ -2419,8 +2419,8 @@ PBcqDCjq5jgMhU1oyVclRK7jJdmu0Azvwo2lleLAFLdCzHEXWXUz parsed = await KeyUtil.parse(armoredAfterDecryption); expect(parsed.id).to.equal('9B5FCFF576A032495AFE77805354351B39AB3BC6'); expect(parsed.family).to.equal('x509'); - expect(parsed.emails.length).to.equal(1); - expect(parsed.emails[0]).to.equal('human@flowcrypt.com'); + expect(parsed.users.length).to.equal(1); + expect(parsed.users[0].email).to.equal('human@flowcrypt.com'); expect(parsed.isPrivate).to.equal(true); expect(parsed.isPublic).to.equal(false); expect(parsed.fullyDecrypted).to.equal(true); @@ -2432,8 +2432,8 @@ PBcqDCjq5jgMhU1oyVclRK7jJdmu0Azvwo2lleLAFLdCzHEXWXUz let parsed = await KeyUtil.parse(p8); expect(parsed.id).to.equal('9B5FCFF576A032495AFE77805354351B39AB3BC6'); expect(parsed.family).to.equal('x509'); - expect(parsed.emails.length).to.equal(1); - expect(parsed.emails[0]).to.equal('human@flowcrypt.com'); + expect(parsed.users.length).to.equal(1); + expect(parsed.users[0].email).to.equal('human@flowcrypt.com'); expect(parsed.isPrivate).to.equal(true); expect(parsed.isPublic).to.equal(false); expect(parsed.fullyDecrypted).to.equal(false); @@ -2451,8 +2451,8 @@ PBcqDCjq5jgMhU1oyVclRK7jJdmu0Azvwo2lleLAFLdCzHEXWXUz parsed = await KeyUtil.parse(armoredAfterDecryption); expect(parsed.id).to.equal('9B5FCFF576A032495AFE77805354351B39AB3BC6'); expect(parsed.family).to.equal('x509'); - expect(parsed.emails.length).to.equal(1); - expect(parsed.emails[0]).to.equal('human@flowcrypt.com'); + expect(parsed.users.length).to.equal(1); + expect(parsed.users[0].email).to.equal('human@flowcrypt.com'); expect(parsed.isPrivate).to.equal(true); expect(parsed.isPublic).to.equal(false); expect(parsed.fullyDecrypted).to.equal(true); @@ -2464,8 +2464,8 @@ PBcqDCjq5jgMhU1oyVclRK7jJdmu0Azvwo2lleLAFLdCzHEXWXUz let parsed = await KeyUtil.parse(p8); expect(parsed.id).to.equal('9B5FCFF576A032495AFE77805354351B39AB3BC6'); expect(parsed.family).to.equal('x509'); - expect(parsed.emails.length).to.equal(1); - expect(parsed.emails[0]).to.equal('human@flowcrypt.com'); + expect(parsed.users.length).to.equal(1); + expect(parsed.users[0].email).to.equal('human@flowcrypt.com'); expect(parsed.isPrivate).to.equal(true); expect(parsed.isPublic).to.equal(false); expect(parsed.fullyDecrypted).to.equal(true); @@ -2480,8 +2480,8 @@ PBcqDCjq5jgMhU1oyVclRK7jJdmu0Azvwo2lleLAFLdCzHEXWXUz parsed = await KeyUtil.parse(armoredAfterEncryption); expect(parsed.id).to.equal('9B5FCFF576A032495AFE77805354351B39AB3BC6'); expect(parsed.family).to.equal('x509'); - expect(parsed.emails.length).to.equal(1); - expect(parsed.emails[0]).to.equal('human@flowcrypt.com'); + expect(parsed.users.length).to.equal(1); + expect(parsed.users[0].email).to.equal('human@flowcrypt.com'); expect(parsed.isPrivate).to.equal(true); expect(parsed.isPublic).to.equal(false); expect(parsed.fullyDecrypted).to.equal(false); @@ -2690,19 +2690,19 @@ oYO0H2wzxUfJQIcXL8HfNs70eXJwD7U9F6gnIeUKA8+1NsQMgQE= =hLUh -----END PGP PUBLIC KEY BLOCK-----`; const pubKey = await KeyUtil.parse(armoredPublicKey); - expect(pubKey.identities.length).to.equal(2); - expect(pubKey.identities[0]).to.equal('First '); - expect(pubKey.identities[1]).to.equal('Second '); - expect(pubKey.emails.length).to.equal(2); - expect(pubKey.emails[0]).to.equal('first@example.com'); - expect(pubKey.emails[1]).to.equal('second@example.com'); + expect(pubKey.users.length).to.equal(2); + expect(pubKey.users[0].full).to.equal('First '); + expect(pubKey.users[1].full).to.equal('Second '); + expect(pubKey.users.length).to.equal(2); + expect(pubKey.users[0].email).to.equal('first@example.com'); + expect(pubKey.users[1].email).to.equal('second@example.com'); const prvKey = await KeyUtil.parse(armoredPrivateKey); - expect(prvKey.identities.length).to.equal(2); - expect(prvKey.identities[0]).to.equal('First '); - expect(prvKey.identities[1]).to.equal('Second '); - expect(prvKey.emails.length).to.equal(2); - expect(prvKey.emails[0]).to.equal('first@example.com'); - expect(prvKey.emails[1]).to.equal('second@example.com'); + expect(prvKey.users.length).to.equal(2); + expect(prvKey.users[0].full).to.equal('First '); + expect(prvKey.users[1].full).to.equal('Second '); + expect(prvKey.users.length).to.equal(2); + expect(prvKey.users[0].email).to.equal('first@example.com'); + expect(prvKey.users[1].email).to.equal('second@example.com'); t.pass(); });