Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Tests for the display of OpenPGP signed/encrypted state in opened messages.
*/
"use strict";
requestLongerTimeout(2);
const {
get_about_message,
open_message_from_file,
wait_for_message_display_completion,
} = ChromeUtils.importESModule(
);
const { promise_new_window } = ChromeUtils.importESModule(
);
const { OpenPGPTestUtils } = ChromeUtils.importESModule(
);
const { MailServices } = ChromeUtils.importESModule(
);
var { MailConsts } = ChromeUtils.importESModule(
);
const MSG_TEXT = "Sundays are nothing without callaloo.";
function getMsgBodyTxt(msgc) {
const msgPane = get_about_message(msgc).getMessagePaneBrowser();
return msgPane.contentDocument.documentElement.textContent;
}
/**
* When testing a scenario that should automatically process the OpenPGP
* contents (it's not suppressed e.g. because of a partial content),
* then we need to wait for the automatic processing to complete.
*/
async function openpgpProcessed() {
const [subject] = await TestUtils.topicObserved(
"document-element-inserted",
document => {
return document.documentGlobal?.location == "about:message";
}
);
return BrowserTestUtils.waitForEvent(subject, "openpgpprocessed");
}
var aliceAcct;
/**
* Set up the base account, identity and keys needed for the tests.
*/
add_setup(async function () {
// This test assumes the standalone message window.
await SpecialPowers.pushPrefEnv({
set: [
["mail.openMessageBehavior", MailConsts.OpenMessageBehavior.NEW_WINDOW],
],
});
aliceAcct = MailServices.accounts.createAccount();
aliceAcct.incomingServer = MailServices.accounts.createIncomingServer(
"alice",
"openpgp.example",
"pop3"
);
const aliceIdentity = MailServices.accounts.createIdentity();
aliceIdentity.email = "alice@openpgp.example";
aliceAcct.addIdentity(aliceIdentity);
// Set up the alice's private key.
const [id] = await OpenPGPTestUtils.importPrivateKey(
window,
new FileUtils.File(
getTestFilePath(
"data/keys/alice@openpgp.example-0xf231550c4f47e38e-secret.asc"
)
)
);
aliceIdentity.setUnicharAttribute("openpgp_key_id", id);
// Import and accept the public key for Bob, our verified sender.
await OpenPGPTestUtils.importPublicKey(
window,
new FileUtils.File(
getTestFilePath(
"data/keys/bob@openpgp.example-0xfbfcc82a015e7330-pub.asc"
)
)
);
});
/**
* Test that an unsigned unencrypted message do not show as signed nor encrypted.
*/
add_task(async function testOpenNoPGPSecurity() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/unsigned-unencrypted-from-bob-to-alice.eml")
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should NOT be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that a signed (only) message, signed by a verified key, shows as such.
*/
add_task(async function testOpenSignedByVerifiedUnencrypted() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath(
"data/eml/signed-by-0xfbfcc82a015e7330-to-0xf231550c4f47e38e-unencrypted.eml"
)
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasSignedIconState(aboutMessage.document, "verified"),
"signed icon should be shown for status verified"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should NOT be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that a signed (only) message, signed by a verified key,
* but with an mismatching email date, is shown with invalid signature.
*/
add_task(async function testOpenSignedDateMismatch() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/signed-mismatch-email-date.eml")
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown for status mismatch"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should NOT be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that opening an unsigned encrypted message shows as such.
*/
add_task(async function testOpenVerifiedUnsignedEncrypted() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath(
"data/eml/unsigned-encrypted-to-0xf231550c4f47e38e-from-0xfbfcc82a015e7330.eml"
)
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that we can decrypt a message to a hidden recipient
* (recipient key ID is zero in PKESK).
*/
add_task(async function testEncryptedToHiddenRecpient() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/encrypted-to-alice-as-hidden-recipient.eml")
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(
getMsgBodyTxt(msgc).includes("hidden recipient encryption"),
"expected message text should be in body"
);
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that opening an attached encrypted message has no effect
* on security status icons of the parent message window.
*/
add_task(async function testOpenForwardedEncrypted() {
const msgc = await open_message_from_file(
new FileUtils.File(getTestFilePath("data/eml/fwd-unsigned-encrypted.eml"))
);
const aboutMessage = get_about_message(msgc);
Assert.ok(
getMsgBodyTxt(msgc).includes("wrapper message with plain text"),
"wrapper message text should be shown"
);
Assert.ok(
!getMsgBodyTxt(msgc).includes(MSG_TEXT),
"message text should not be shown"
);
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should NOT be shown"
);
// Delete should not work.
EventUtils.synthesizeMouseAtCenter(
aboutMessage.document.getElementById("attachmentBar"),
{ clickCount: 1 },
aboutMessage
);
EventUtils.synthesizeKey("KEY_Delete", {}, aboutMessage);
await TestUtils.waitForTick();
const newWindowPromise = promise_new_window("mail:messageWindow");
EventUtils.synthesizeMouseAtCenter(
aboutMessage.document.getElementById("attachmentName"),
{ clickCount: 1 },
aboutMessage
);
const mc2 = await newWindowPromise;
await wait_for_message_display_completion(mc2, true);
if (Services.focus.activeWindow != mc2) {
await new Promise(resolve =>
mc2.addEventListener("activate", resolve, { once: true })
);
}
const aboutMessage2 = get_about_message(mc2);
// Check properties of the opened attachment window.
Assert.ok(
getMsgBodyTxt(mc2).includes(MSG_TEXT),
"message text should be shown"
);
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage2.document),
"signed icon should NOT be shown"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage2.document, "ok"),
"encrypted icon should be shown"
);
await BrowserTestUtils.closeWindow(mc2);
// Ensure there were no side effects for the primary window.
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon is still not displayed"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon is still not displayed"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that opening an attached signed message has no effect
* on security status icons of the parent message window, and that the
* opened attachment displays the text of the signed message.
*/
add_task(async function testOpenForwardedSigned() {
const msgc = await open_message_from_file(
new FileUtils.File(getTestFilePath("data/eml/fwd-signed.eml"))
);
const aboutMessage = get_about_message(msgc);
const SIGNED_TEXT = "signed message";
Assert.ok(
getMsgBodyTxt(msgc).includes("outer text"),
"wrapper message text should be shown"
);
Assert.ok(
getMsgBodyTxt(msgc).includes(SIGNED_TEXT),
"message text should be shown, because inline viewing of attachments"
);
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should NOT be shown"
);
const newWindowPromise = promise_new_window("mail:messageWindow");
EventUtils.synthesizeMouseAtCenter(
aboutMessage.document.getElementById("attachmentName"),
{ clickCount: 1 },
aboutMessage
);
const mc2 = await newWindowPromise;
await wait_for_message_display_completion(mc2, true);
const aboutMessage2 = get_about_message(mc2);
// Check properties of the opened attachment window.
Assert.ok(
getMsgBodyTxt(mc2).includes(SIGNED_TEXT),
"message text should be shown"
);
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown for status unknown"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage2.document, "ok"),
"encrypted icon should NOT be shown"
);
await BrowserTestUtils.closeWindow(mc2);
// Ensure there were no side effects for the primary window.
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon is still not displayed"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon is still not displayed"
);
await BrowserTestUtils.closeWindow(msgc);
});
// TODO: the above tests that an encrypted .eml can be opened from an unencrypted message.
// We should also test/handle:
// - other attachment (like .doc) in an encrypted message
// - unencrypted .eml attachment in encrypted message (currently broken - bug 1926607)
// - encrypted .eml in an encrypted message (currently broken - bug 1926608)
/**
* Test that opening a message that is signed by a verified key shows as such.
*/
add_task(async function testOpenSignedByVerifiedEncrypted() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath(
"data/eml/signed-by-0xfbfcc82a015e7330-encrypted-to-0xf231550c4f47e38e.eml"
)
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasSignedIconState(aboutMessage.document, "verified"),
"signed icon should be shown for status verified"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that opening a message that is signed by a verified key, but the From
* is not what it should be due to multiple From headers, will show mismatch.
* Here it's signed by Bob, but Eve inserted an From: Eve <eve@openpgp.example>
* header first. Only first From is used. The second From should not
* be used for verification.
*/
add_task(async function testOpenSignedEncryptedMultiFrom() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath(
"data/eml/signed-by-0xfbfcc82a015e7330-encrypted-to-0xf231550c4f47e38e-multi-from.eml"
)
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasSignedIconState(aboutMessage.document, "mismatch"),
"mismatch icon should be displayed"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should be displayed"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that opening a message signed (only) by an unverified key shows as such.
*/
add_task(async function testOpenSignedByUnverifiedUnencrypted() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath(
"data/eml/signed-by-0x3099ff1238852b9f-to-0xf231550c4f47e38e-unencrypted.eml"
)
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown for status unknown"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should NOT be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that opening a message signed (only) with extra outer layer
* doesn't show signature state.
*/
add_task(async function testOpenSignedWithOuterLayer() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/signed-with-mailman-footer.eml")
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that opening a message encrypted (only) shows as such.
*/
add_task(async function testOpenUnverifiedUnsignedEncrypted() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath(
"data/eml/unsigned-encrypted-to-0xf231550c4f47e38e-from-0x3099ff1238852b9f.eml"
)
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that we decrypt a nested OpenPGP encrypted message
* (with outer S/MIME signature that is ignored).
*/
add_task(async function testOuterSmimeSigInnerPgpUnverifiedUnsignedEncrypted() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/outer-smime-bad-sig-inner-pgp-enc.eml")
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that we decrypt a nested OpenPGP encrypted message
* (with outer OpenPGP signature that is ignored).
*/
add_task(async function testOuterPgpSigInnerPgpUnverifiedUnsignedEncrypted() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/outer-pgp-sig-inner-pgp-enc.eml")
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that we DO NOT decrypt a nested OpenPGP encrypted message
* at MIME level 3, with an outer signature layer (level 1) and a
* multipart/mixed in between (level 2).
* We should not ignore the outer signature in this scenario.
*/
add_task(async function testOuterPgpSigInnerPgpEncryptedInsideMixed() {
const openpgpprocessed = openpgpProcessed();
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/outer-pgp-sig-inner-pgp-enc-with-mixed.eml")
)
);
const aboutMessage = get_about_message(msgc);
await openpgpprocessed;
await TestUtils.waitForTick();
Assert.ok(!getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown for status unknown"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should NOT be shown"
);
const partsMessageWindowPromise = BrowserTestUtils.domWindowOpenedAndLoaded(
undefined,
async win =>
win.document.documentURI ==
);
const button = aboutMessage.document.querySelector(
`button[data-l10n-id="openpgp-show-encrypted-parts"]`
);
const openpgpprocessed2 = openpgpProcessed();
EventUtils.synthesizeMouseAtCenter(
button,
{ clickCount: 1 },
button.documentGlobal
);
const win2 = await partsMessageWindowPromise;
const aboutMessage2 = get_about_message(win2);
await openpgpprocessed2;
Assert.stringContains(
getMsgBodyTxt(win2),
"Sundays are nothing without callaloo.",
"message text is in body"
);
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage2.document),
"the part should not show signed"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage2.document, "ok"),
"the parts should show as encrypted ok"
);
await BrowserTestUtils.closeWindow(win2);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test "Open and show" an attached signed .eml
*/
add_task(async function testOpenAndShowAttachedEml() {
const openpgpprocessed = openpgpProcessed();
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/forwarded-eml-signed-by-alice.eml")
)
);
const aboutMessage = get_about_message(msgc);
await openpgpprocessed;
await TestUtils.waitForTick();
Assert.stringContains(
getMsgBodyTxt(msgc),
"See the attached note from alice.",
"message text is in body"
);
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon for outer message should NOT be shown"
);
const partsMessageWindowPromise = BrowserTestUtils.domWindowOpenedAndLoaded(
undefined,
async win =>
win.document.documentURI ==
);
const openpgpprocessed2 = openpgpProcessed();
const button = aboutMessage.document.querySelector(
`button[data-l10n-id="openpgp-show-signed-parts"]`
);
EventUtils.synthesizeMouseAtCenter(
button,
{ clickCount: 1 },
button.documentGlobal
);
const win2 = await partsMessageWindowPromise;
const aboutMessage2 = get_about_message(win2);
await openpgpprocessed2;
Assert.ok(
getMsgBodyTxt(win2).includes("Cryptography is not magic"),
"message text is in body"
);
Assert.ok(
OpenPGPTestUtils.hasSignedIconState(aboutMessage2.document, "ok"),
"the part should show valid signature for your own personal key"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage2.document, "ok"),
"the parts should not show as encrypted"
);
await BrowserTestUtils.closeWindow(win2);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that opening a message signed (only) with extra outer layer
* renders message content, only, not showing headers (bug 2016119)
*/
add_task(async function testOpenAndShowAttachedEmlWithFooter() {
const openpgpprocessed = openpgpProcessed();
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/signed-with-mailman-footer.eml")
)
);
const aboutMessage = get_about_message(msgc);
await openpgpprocessed;
await TestUtils.waitForTick();
const partsMessageWindowPromise = BrowserTestUtils.domWindowOpenedAndLoaded(
undefined,
async win =>
win.document.documentURI ==
);
const openpgpprocessed2 = openpgpProcessed();
const button = aboutMessage.document.querySelector(
`button[data-l10n-id="openpgp-show-signed-parts"]`
);
EventUtils.synthesizeMouseAtCenter(
button,
{ clickCount: 1 },
button.documentGlobal
);
const win2 = await partsMessageWindowPromise;
await openpgpprocessed2;
const msgBody = getMsgBodyTxt(win2);
Assert.ok(msgBody.includes(MSG_TEXT), "message text is in body");
Assert.ok(
!msgBody.toLowerCase().includes("Content-Type".toLowerCase()),
"rendered message must not show content-type header"
);
await BrowserTestUtils.closeWindow(win2);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that opening an encrypted message signed by an unverified key is shown
* as it should.
*/
add_task(async function testOpenSignedByUnverifiedEncrypted() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath(
"data/eml/signed-by-0x3099ff1238852b9f-encrypted-to-0xf231550c4f47e38e.eml"
)
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasSignedIconState(aboutMessage.document, "unknown"),
"signed icon should be shown for status unknown on encrypted message"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that we decrypt a nested OpenPGP encrypted+signed message
* (with outer S/MIME signature that is ignored).
*/
add_task(async function testOuterSmimeSigInnerPgpSignedByUnverifiedEncrypted() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/outer-smime-bad-sig-inner-pgp-enc-sig.eml")
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasSignedIconState(aboutMessage.document, "unknown"),
"signed icon should be shown for status unknown on encrypted message"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that the content of an S/MIME signed part, which is nested inside
* an OpenPGP encrypted message, is displayed. The S/MIME signature in the
* test message is invalid, only displaying the signed content matters.
*/
add_task(async function testInnerSmimeSigInsidePgpEncrypted() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/inner-smime-sig-in-pgp-encrypted.eml")
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(
getMsgBodyTxt(msgc).includes(MSG_TEXT),
"decrypted text of the S/MIME signed part should be in body"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that we DO NOT decrypt a nested OpenPGP encrypted message
* at MIME level 3, with an outer signature layer (level 1) and a
* multipart/mixed in between (level 2).
* We should not ignore the outer signature in this scenario.
*/
add_task(async function testOuterSmimeSigInnerPgpEncryptedInsideMixed() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath(
"data/eml/outer-smime-bad-sig-inner-pgp-enc-sig-with-mixed.eml"
)
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(!getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
// Note this is an S/MIME signature status, at the time of writing
// this test, string "mismatch" is used for status "notok".
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown for status mismatch"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should NOT be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that processing a nested OpenPGP signed message with an outer S/MIME
* signature does not result in an endless reload loop, if the content type
* parameters are ordered in an unusual way.
*/
add_task(async function testUncommonContentType() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/dual-signed-uncommon-content-type.eml")
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(
getMsgBodyTxt(msgc).includes(
"The order of the content type parameters is unusual"
),
"message text is in body"
);
// Note this is an S/MIME signature status, at the time of writing
// this test, string "mismatch" is used for status "notok".
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown for status mismatch"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should NOT be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that we decrypt a nested OpenPGP encrypted+signed message
* (with outer OpenPGP signature that is ignored).
*/
add_task(async function testOuterPgpSigOpenSignedByUnverifiedEncrypted() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/outer-pgp-sig-inner-pgp-enc-sig.eml")
)
);
const aboutMessage = get_about_message(msgc);
Assert.ok(getMsgBodyTxt(msgc).includes(MSG_TEXT), "message text is in body");
Assert.ok(
OpenPGPTestUtils.hasSignedIconState(aboutMessage.document, "unknown"),
"signed icon should be shown for status unknown on encrypted message"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that processing a mailing list digest containing multiple different
* signatures does not result in an endless reload loop, see bug 1946168.
*/
add_task(async function testMultipleSignatures() {
const msgc = await open_message_from_file(
new FileUtils.File(getTestFilePath("data/eml/multiple-signatures.eml"))
);
const aboutMessage = get_about_message(msgc);
Assert.ok(
getMsgBodyTxt(msgc).includes("Send dovecot mailing list submissions to"),
"message text should be in body"
);
Assert.ok(
getMsgBodyTxt(msgc).includes("hello2"),
"body of the nested unsigned message should be in body"
);
Assert.ok(
getMsgBodyTxt(msgc).includes("hello3"),
"body of the nested S/MIME signed message should be in body"
);
// This is an S/MIME signature status; at the time of writing this test,
// the string "mismatch" is used for status "notok".
Assert.ok(
!OpenPGPTestUtils.hasSignedIconState(aboutMessage.document, "mismatch"),
"signed icon should not be displayed"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should not be displayed"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that the message is properly reloaded and the message security icon is
* updated if the user changes the signature acceptance level.
*/
add_task(async function testUpdateMessageSignature() {
// Setup the message.
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath(
"data/eml/signed-by-0xfbfcc82a015e7330-to-0xf231550c4f47e38e-unencrypted.eml"
)
)
);
const aboutMessage = get_about_message(msgc);
// Verify current signature acceptance.
Assert.ok(
OpenPGPTestUtils.hasSignedIconState(aboutMessage.document, "verified"),
"signed icon should be shown for status verified"
);
const popupshown = BrowserTestUtils.waitForEvent(
aboutMessage.document.getElementById("messageSecurityPanel"),
"popupshown"
);
EventUtils.synthesizeMouseAtCenter(
aboutMessage.document.getElementById("encryptionTechBtn"),
{ clickCount: 1 },
aboutMessage
);
// Wait for the popup panel and signature button to become visible otherwise
// we can't click on it.
await popupshown;
// Open the Key Properties dialog and change the signature acceptance.
const dialogPromise = BrowserTestUtils.domWindowOpened(null, async win => {
await BrowserTestUtils.waitForEvent(win, "load");
if (
win.document.documentURI !=
) {
return false;
}
if (Services.focus.activeWindow != win) {
await BrowserTestUtils.waitForEvent(win, "focus");
}
EventUtils.synthesizeMouseAtCenter(
win.document.querySelector("#acceptUnverified"),
{},
win
);
const closedPromise = BrowserTestUtils.domWindowClosed(win);
win.document.documentElement.querySelector("dialog").acceptDialog();
await closedPromise;
return true;
});
// This will open the key details, the domWindowOpened handler
// will catch it and execute the changes.
EventUtils.synthesizeMouseAtCenter(
aboutMessage.document.getElementById("viewSignatureKey"),
{ clickCount: 1 },
aboutMessage
);
// Wait until we are done with keyDetailsDlg.
await dialogPromise;
// Wait for the signedHdrIcon state to change.
// Verify the new acceptance level is correct.
await TestUtils.waitForCondition(
() =>
OpenPGPTestUtils.hasSignedIconState(aboutMessage.document, "unverified"),
"signed unverified icon should be displayed"
);
await BrowserTestUtils.closeWindow(msgc);
});
// After test testUpdateMessageSignature acceptance of Bob's key
// has changed from verified to unverified.
/**
* Test that a signed (only) inline PGP message with UTF-8 characters
* can be correctly verified.
*/
add_task(async function testOpenSignedInlineWithUTF8() {
const opengpgprocessed = openpgpProcessed();
const msgc = await open_message_from_file(
new FileUtils.File(getTestFilePath("data/eml/alice-utf.eml"))
);
const aboutMessage = get_about_message(msgc);
await opengpgprocessed;
Assert.ok(
getMsgBodyTxt(msgc).includes("£35.00"),
"UTF-8 £35.00 should be found in message"
);
await TestUtils.waitForCondition(
() =>
OpenPGPTestUtils.hasSignedIconState(aboutMessage.document, "unverified"),
"signed icon should be shown for status unverified on encrypted message"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should NOT be shown"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that a signed (only) inline PGP message with leading whitespace
* can be correctly verified.
*/
add_task(async function testOpenSignedInlineWithLeadingWS() {
const opengpgprocessed = openpgpProcessed();
const msgc = await open_message_from_file(
new FileUtils.File(getTestFilePath("data/eml/signed-inline-indented.eml"))
);
const aboutMessage = get_about_message(msgc);
await opengpgprocessed;
Assert.ok(
getMsgBodyTxt(msgc).includes("indent test with £"),
"expected text 'indent test with £' should be found in message"
);
await TestUtils.waitForCondition(
() =>
OpenPGPTestUtils.hasSignedIconState(aboutMessage.document, "unverified"),
"signed unverified icon should display"
);
Assert.ok(
!OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should not display"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that an encrypted inline message, with nbsp encoded as qp
* in the PGP separator line, is trimmed and decrypted.
*/
add_task(async function testDecryptInlineWithNBSPasQP() {
const opengpgprocessed = openpgpProcessed();
const msgc = await open_message_from_file(
new FileUtils.File(getTestFilePath("data/eml/bob-enc-inline-nbsp-qp.eml"))
);
const aboutMessage = get_about_message(msgc);
await opengpgprocessed;
Assert.ok(
getMsgBodyTxt(msgc).includes("My real name is not Bob."),
"Secret text should be contained in message"
);
await TestUtils.waitForCondition(
() => OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"Encrypted icon should be displayed"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that an inline message, encoded as html message, with nbsp
* encoded as qp in the PGP separator line, is trimmed and decrypted.
*/
add_task(async function testDecryptHtmlWithNBSP() {
const opengpgprocessed = openpgpProcessed();
const msgc = await open_message_from_file(
new FileUtils.File(getTestFilePath("data/eml/bob-enc-html-nbsp.eml"))
);
const aboutMessage = get_about_message(msgc);
await opengpgprocessed;
Assert.ok(
getMsgBodyTxt(msgc).includes("My real name is not Bob."),
"Secret text should be contained in message"
);
await TestUtils.waitForCondition(
() => OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"Encrypted icon should be displayed"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that opening an encrypted (and signed) message with non-ascii subject
* and body works.
*/
add_task(async function testOpenAliceToBobEncryptedNonASCII() {
const opengpgprocessed = openpgpProcessed();
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/encrypted-and-signed-alice-to-bob-nonascii.eml")
)
);
const aboutMessage = get_about_message(msgc);
await opengpgprocessed;
// Check the subject was properly updated (from ...) in the message header.
Assert.equal(
aboutMessage.document.getElementById("expandedsubjectBox").textContent,
"Subject:Re: kod blå",
"Non-ascii subject should correct"
);
Assert.ok(getMsgBodyTxt(msgc).includes("Detta är krypterat!"));
Assert.ok(
OpenPGPTestUtils.hasSignedIconState(aboutMessage.document, "ok"),
"signed verified icon should be displayed"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should be displayed"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that it's possible to decrypt an OpenPGP encrypted message
* using a revoked key. (Also signed, unknown signer key.)
*/
add_task(async function testOpenEncryptedForRevokedKey() {
await OpenPGPTestUtils.importPrivateKey(
window,
new FileUtils.File(
getTestFilePath(
"data/keys/carol@pgp.icu-0xEF2FD01608AFD744-revoked-secret.asc"
)
)
);
const opengpgprocessed = openpgpProcessed();
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath("data/eml/enc-to-carol@pgp.icu-revoked.eml")
)
);
const aboutMessage = get_about_message(msgc);
await opengpgprocessed;
Assert.ok(
getMsgBodyTxt(msgc).includes("billie-jean"),
"message text is in body"
);
Assert.ok(
OpenPGPTestUtils.hasEncryptedIconState(aboutMessage.document, "ok"),
"encrypted icon should be displayed"
);
await BrowserTestUtils.closeWindow(msgc);
await OpenPGPTestUtils.removeKeyById("0xEF2FD01608AFD744", true);
});
/**
* Test that a git commit isn't confusing signed/encrypted states.
* The MIME content type headers claim that we have an encrypted OpenPGP
* message, while in fact we have a signed message.
* The important part is to test that the message isn't incorrectly shown as
* being encrypted.
*/
add_task(async function testGitCommitSpoof() {
await OpenPGPTestUtils.importPublicKey(
window,
new FileUtils.File(
getTestFilePath("data/keys/benny-hashwell-0xc55ccd9c5ab482b2-pub.asc")
)
);
const opengpgprocessed = openpgpProcessed();
const msgc = await open_message_from_file(
new FileUtils.File(getTestFilePath("data/eml/sigspoof-git-commit.eml"))
);
const aboutMessage = get_about_message(msgc);
await opengpgprocessed;
// This (text#1) should be visible, and not confused with mail headers.
Assert.ok(
getMsgBodyTxt(msgc).includes(
"tree 870fc0aaa5383d845fa9431c9d08cfd486e11a65"
),
"message text#1 should be in body"
);
// This (text#2), the normal text should be visible too.
Assert.ok(
getMsgBodyTxt(msgc).includes("Hope you find something good elsewhere!"),
"message text#2 should be in body"
);
// The signing status is currently "mismatch" though it's debatable
// whether that is correct.
// Assert.ok(
// OpenPGPTestUtils.hasSignedIconState(aboutMessage.document, "mismatch"),
// "should say signed mismatch"
// );
Assert.ok(
OpenPGPTestUtils.hasNoEncryptedIconState(aboutMessage.document),
"should not be say encrypted"
);
await BrowserTestUtils.closeWindow(msgc);
await OpenPGPTestUtils.removeKeyById("0xc55ccd9c5ab482b2", false);
});
/**
* Test that a missing signature is treated as bad signature.
*/
add_task(async function testStrippedSig() {
const opengpgprocessed = openpgpProcessed();
const msgc = await open_message_from_file(
new FileUtils.File(getTestFilePath("data/eml/openpgp__stripped_sig.eml"))
);
const aboutMessage = get_about_message(msgc);
await opengpgprocessed;
// This (text#1) should be visible, and not confused with mail headers.
Assert.ok(
getMsgBodyTxt(msgc).includes("This is a test"),
"message text should be in body"
);
Assert.ok(
OpenPGPTestUtils.hasNoSignedIconState(aboutMessage.document),
"signed icon should NOT be shown for status mismatch"
);
Assert.ok(
OpenPGPTestUtils.hasNoEncryptedIconState(aboutMessage.document),
"should not be say encrypted"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Open a message with a single attached public key, shown as #attachmentName
* in the attachment bar, and click it. Returns the opened message window, the
* about:message window, and a spy array capturing HandleMultipleAttachments
* calls (the attachment-open entry point).
*/
async function openKeyAttachmentMessage() {
const msgc = await open_message_from_file(
new FileUtils.File(
getTestFilePath(
"data/eml/unsigned-unencrypted-key-0x1f10171bfb881b1c-attached.eml"
)
)
);
const aboutMessage = get_about_message(msgc);
// A single attachment is shown as #attachmentName in the attachment bar.
const attachmentName = aboutMessage.document.getElementById("attachmentName");
Assert.ok(
!attachmentName.hidden,
"the single attachment should be shown in the attachment bar"
);
const opened = [];
const origHandleMultiple = aboutMessage.HandleMultipleAttachments;
aboutMessage.HandleMultipleAttachments = (attachments, action) => {
opened.push({ attachments, action });
};
const restore = () => {
aboutMessage.HandleMultipleAttachments = origHandleMultiple;
};
return { msgc, aboutMessage, attachmentName, opened, restore };
}
/**
* Test that choosing "Open" in the prompt shown for an attached public key
* opens that specific attachment. Regression test: the attachment-bar path
* used to open the attachment list's (empty) selection, so nothing happened.
*/
add_task(async function testOpenKeyAttachmentFromBar() {
const { msgc, aboutMessage, attachmentName, opened, restore } =
await openKeyAttachmentMessage();
try {
// Clicking the key attachment shows the import/open prompt; choose "Open".
// In the common dialog the third confirmEx button (BUTTON_POS_2, our
// "Open" label) maps to the "extra1" button.
const dialogPromise = BrowserTestUtils.promiseAlertDialog("extra1");
EventUtils.synthesizeMouseAtCenter(
attachmentName,
{ clickCount: 1 },
aboutMessage
);
await dialogPromise;
await TestUtils.waitForCondition(
() => opened.length == 1,
"the attachment should be opened once after choosing Open"
);
} finally {
restore();
}
Assert.equal(opened[0].action, "open", "the open action should be requested");
Assert.equal(
opened[0].attachments.length,
1,
"exactly one attachment should be opened"
);
Assert.ok(
opened[0].attachments[0].name.endsWith(".asc"),
"the clicked key attachment should be opened, not the empty list selection"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that choosing "Cancel" in the prompt shown for an attached public key
* does nothing (neither imports nor opens the attachment).
*/
add_task(async function testCancelKeyAttachmentFromBar() {
const { msgc, aboutMessage, attachmentName, opened, restore } =
await openKeyAttachmentMessage();
try {
// Clicking the key attachment shows the import/open prompt; choose
// "Cancel", which maps to the common dialog's "cancel" button.
const dialogPromise = BrowserTestUtils.promiseAlertDialog("cancel");
EventUtils.synthesizeMouseAtCenter(
attachmentName,
{ clickCount: 1 },
aboutMessage
);
await dialogPromise;
// Give the (async) attachment handling a chance to run, then confirm it
// did not open anything.
await TestUtils.waitForTick();
await TestUtils.waitForTick();
Assert.equal(
opened.length,
0,
"cancelling the prompt should not open the attachment"
);
} finally {
restore();
}
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that dismissing the prompt shown for an attached public key with the
* Escape key does nothing (Cancel is the dialog's cancel button, so Escape
* maps to it and does not open the attachment).
*/
add_task(async function testEscapeKeyAttachmentFromBar() {
const { msgc, aboutMessage, attachmentName, opened, restore } =
await openKeyAttachmentMessage();
try {
// Dismiss the prompt with the Escape key instead of clicking a button.
const dialogPromise = BrowserTestUtils.promiseAlertDialog(null, undefined, {
callback: win => EventUtils.synthesizeKey("KEY_Escape", {}, win),
});
EventUtils.synthesizeMouseAtCenter(
attachmentName,
{ clickCount: 1 },
aboutMessage
);
await dialogPromise;
await TestUtils.waitForTick();
await TestUtils.waitForTick();
Assert.equal(
opened.length,
0,
"pressing Escape should not open the attachment"
);
} finally {
restore();
}
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Test that pressing Enter while a key attachment is focused in
* #attachmentList opens it the same way a double click does.
*/
add_task(async function testEnterOpensKeyAttachment() {
const { msgc, aboutMessage, opened, restore } =
await openKeyAttachmentMessage();
// Expand the attachment list and focus its (single) attachment.
aboutMessage.toggleAttachmentList(true);
const attachmentList = aboutMessage.document.getElementById("attachmentList");
attachmentList.focus();
attachmentList.selectedIndex = 0;
try {
// Pressing Enter shows the import/open prompt; choose "Open" (BUTTON_POS_2,
// the common dialog's "extra1" button) to open the attachment.
const dialogPromise = BrowserTestUtils.promiseAlertDialog("extra1");
EventUtils.synthesizeKey("KEY_Enter", {}, aboutMessage);
await dialogPromise;
await TestUtils.waitForCondition(
() => opened.length == 1,
"the focused attachment should be opened once after pressing Enter"
);
} finally {
restore();
}
Assert.equal(opened[0].action, "open", "the open action should be requested");
Assert.ok(
opened[0].attachments[0].name.endsWith(".asc"),
"the focused key attachment should be opened"
);
await BrowserTestUtils.closeWindow(msgc);
});
/**
* Open the key attachment prompt for the bar attachment, read the label of the
* non-import (Open/Save) button, then dismiss the prompt with Cancel.
*/
async function readKeyAttachmentOpenButtonLabel() {
const { msgc, aboutMessage, attachmentName, opened, restore } =
await openKeyAttachmentMessage();
let label;
try {
const dialogPromise = BrowserTestUtils.promiseAlertDialog(null, undefined, {
callback: win => {
const button = win.document.querySelector("dialog").getButton("extra1");
label = button.label;
// Click the Open/Save button to close the dialog reliably; the spy on
// HandleMultipleAttachments swallows the resulting open.
button.click();
},
});
EventUtils.synthesizeMouseAtCenter(
attachmentName,
{ clickCount: 1 },
aboutMessage
);
await dialogPromise;
// Let the (async) open handling settle before closing the window.
await TestUtils.waitForCondition(
() => opened.length == 1,
"the attachment open should have been handled"
);
} finally {
restore();
await BrowserTestUtils.closeWindow(msgc);
}
return label;
}
/**
* Test that the non-import button is labelled "Open…" when opening a key
* attachment with an external application is allowed.
*/
add_task(async function testOpenButtonLabelWhenOpenAllowed() {
await SpecialPowers.pushPrefEnv({
set: [["browser.download.forbid_open_with", false]],
});
Assert.equal(
await readKeyAttachmentOpenButtonLabel(),
"Open…",
"the button should be labelled Open… when open-with is allowed"
);
await SpecialPowers.popPrefEnv();
});
/**
* Test that the non-import button is labelled "Save" when opening a key
* attachment with an external application is forbidden.
*/
add_task(async function testSaveButtonLabelWhenOpenForbidden() {
await SpecialPowers.pushPrefEnv({
set: [["browser.download.forbid_open_with", true]],
});
Assert.equal(
await readKeyAttachmentOpenButtonLabel(),
"Save",
"the button should be labelled Save when open-with is forbidden"
);
await SpecialPowers.popPrefEnv();
});
registerCleanupFunction(async function tearDown() {
MailServices.accounts.removeAccount(aliceAcct, true);
await OpenPGPTestUtils.removeKeyById("0xf231550c4f47e38e", true);
});