From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- comm/mailnews/mime/test/unit/test_smime_decrypt.js | 701 +++++++++++++++++++++ 1 file changed, 701 insertions(+) create mode 100644 comm/mailnews/mime/test/unit/test_smime_decrypt.js (limited to 'comm/mailnews/mime/test/unit/test_smime_decrypt.js') diff --git a/comm/mailnews/mime/test/unit/test_smime_decrypt.js b/comm/mailnews/mime/test/unit/test_smime_decrypt.js new file mode 100644 index 0000000000..815f786224 --- /dev/null +++ b/comm/mailnews/mime/test/unit/test_smime_decrypt.js @@ -0,0 +1,701 @@ +/* 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 to ensure signed and/or encrypted S/MIME messages are + * processed correctly, and the signature status is treated as good + * or bad as expected. + */ + +var { MessageInjection } = ChromeUtils.import( + "resource://testing-common/mailnews/MessageInjection.jsm" +); +var { PromiseTestUtils } = ChromeUtils.import( + "resource://testing-common/mailnews/PromiseTestUtils.jsm" +); +var { PromiseUtils } = ChromeUtils.importESModule( + "resource://gre/modules/PromiseUtils.sys.mjs" +); +var { SmimeUtils } = ChromeUtils.import( + "resource://testing-common/mailnews/smimeUtils.jsm" +); + +add_setup(function () { + let messageInjection = new MessageInjection({ mode: "local" }); + gInbox = messageInjection.getInboxFolder(); + SmimeUtils.ensureNSS(); + + SmimeUtils.loadPEMCertificate( + do_get_file(smimeDataDirectory + "TestCA.pem"), + Ci.nsIX509Cert.CA_CERT + ); + SmimeUtils.loadCertificateAndKey( + do_get_file(smimeDataDirectory + "Alice.p12"), + "nss" + ); + SmimeUtils.loadCertificateAndKey( + do_get_file(smimeDataDirectory + "Bob.p12"), + "nss" + ); + SmimeUtils.loadCertificateAndKey( + do_get_file(smimeDataDirectory + "Dave.p12"), + "nss" + ); +}); + +add_task(async function verifyTestCertsStillValid() { + // implementation of nsIDoneFindCertForEmailCallback + var doneFindCertForEmailCallback = { + findCertDone(email, cert) { + Assert.notEqual(cert, null); + if (!cert) { + Assert.ok( + false, + "The S/MIME test certificates are invalid today.\n" + + "Please look at the expiration date in file comm/mailnews/test/data/smime/expiration.txt\n" + + "If that date is in the past, new certificates need to be generated and committed.\n" + + "Follow the instructions in comm/mailnews/test/data/smime/README.md\n" + + "If that date is in the future, the test failure is unrelated to expiration and indicates " + + "an error in certificate validation." + ); + } + }, + + QueryInterface: ChromeUtils.generateQI(["nsIDoneFindCertForEmailCallback"]), + }; + + let composeSecure = Cc[ + "@mozilla.org/messengercompose/composesecure;1" + ].createInstance(Ci.nsIMsgComposeSecure); + composeSecure.asyncFindCertByEmailAddr( + "Alice@example.com", + doneFindCertForEmailCallback + ); +}); + +var gInbox; + +var smimeDataDirectory = "../../../data/smime/"; + +let smimeHeaderSink = { + expectResults(maxLen) { + // dump("Restarting for next test\n"); + this._deferred = PromiseUtils.defer(); + this._expectedEvents = maxLen; + this.countReceived = 0; + this._results = []; + this.haveSignedBad = false; + this.haveEncryptionBad = false; + this.resultSig = null; + this.resultEnc = null; + this.resultSigFirst = undefined; + return this._deferred.promise; + }, + signedStatus(aNestingLevel, aSignedStatus, aSignerCert) { + console.log("signedStatus " + aSignedStatus + " level " + aNestingLevel); + // dump("Signed message\n"); + Assert.equal(aNestingLevel, 1); + if (!this.haveSignedBad) { + // override with newer allowed + this.resultSig = { + type: "signed", + status: aSignedStatus, + certificate: aSignerCert, + }; + if (aSignedStatus != 0) { + this.haveSignedBad = true; + } + if (this.resultSigFirst == undefined) { + this.resultSigFirst = true; + } + } + this.countReceived++; + this.checkFinished(); + }, + encryptionStatus(aNestingLevel, aEncryptedStatus, aRecipientCert) { + console.log( + "encryptionStatus " + aEncryptedStatus + " level " + aNestingLevel + ); + // dump("Encrypted message\n"); + Assert.equal(aNestingLevel, 1); + if (!this.haveEncryptionBad) { + // override with newer allowed + this.resultEnc = { + type: "encrypted", + status: aEncryptedStatus, + certificate: aRecipientCert, + }; + if (aEncryptedStatus != 0) { + this.haveEncryptionBad = true; + } + if (this.resultSigFirst == undefined) { + this.resultSigFirst = false; + } + } + this.countReceived++; + this.checkFinished(); + }, + checkFinished() { + if (this.countReceived == this._expectedEvents) { + if (this.resultSigFirst) { + this._results.push(this.resultSig); + if (this.resultEnc != null) { + this._results.push(this.resultEnc); + } + } else { + this._results.push(this.resultEnc); + if (this.resultSig != null) { + this._results.push(this.resultSig); + } + } + this._deferred.resolve(this._results); + } + }, + QueryInterface: ChromeUtils.generateQI(["nsIMsgSMIMEHeaderSink"]), +}; + +/** + * Note on FILENAMES taken from the NSS test suite: + * - env: CMS enveloped (encrypted) + * - dsig: CMS detached signature (with multipart MIME) + * - sig: CMS opaque signature (content embedded inside signature) + * - bad: message text does not match signature + * - mismatch: embedded content is different + * + * Control variables used for checking results: + * - env: If true, we expect a report to encryptionStatus() that message + * is encrypted. + * - sig: If true, we expect a report to signedStatus() that message + * is signed. + * - sig_good: If true, we expect that the reported signature has a + * good status. + * If false, we expect a report of bad status. + * Because of the sequential processing caused by nested + * messages, additional calls to signedStatus() might + * override an earlier decision. + * (An earlier bad status report cannot be overridden by a + * later report of a good status.) + * - extra: If set to a number > 0, we expect that nested processing of + * MIME parts will trigger the given number of additional + * status calls. + * (default is 0.) + * - dave: If true, we expect that the outermost message was done by + * Dave's certificate. + * (default is false, which means we expect Alice's cert.) + */ + +var gMessages = [ + { + filename: "alice.env.eml", + enc: true, + sig: false, + sig_good: false, + check_text: true, + }, + { + filename: "alice.dsig.SHA1.multipart.bad.eml", + enc: false, + sig: true, + sig_good: false, + }, + { + filename: "alice.dsig.SHA1.multipart.env.eml", + enc: true, + sig: true, + sig_good: false, + check_text: true, + }, + { + filename: "alice.dsig.SHA1.multipart.eml", + enc: false, + sig: true, + sig_good: false, + check_text: true, + }, + { + filename: "alice.dsig.SHA1.multipart.mismatch-econtent.eml", + enc: false, + sig: true, + sig_good: false, + }, + { + filename: "alice.dsig.SHA256.multipart.bad.eml", + enc: false, + sig: true, + sig_good: false, + }, + { + filename: "alice.dsig.SHA256.multipart.eml", + enc: false, + sig: true, + sig_good: true, + }, + { + filename: "alice.future.dsig.SHA256.multipart.eml", + enc: false, + sig: true, + sig_good: false, + }, + { + filename: "alice.dsig.SHA256.multipart.env.eml", + enc: true, + sig: true, + sig_good: true, + }, + { + filename: "alice.dsig.SHA256.multipart.mismatch-econtent.eml", + enc: false, + sig: true, + sig_good: false, + }, + { + filename: "alice.dsig.SHA384.multipart.bad.eml", + enc: false, + sig: true, + sig_good: false, + }, + { + filename: "alice.dsig.SHA384.multipart.eml", + enc: false, + sig: true, + sig_good: true, + }, + { + filename: "alice.dsig.SHA384.multipart.env.eml", + enc: true, + sig: true, + sig_good: true, + }, + { + filename: "alice.dsig.SHA384.multipart.mismatch-econtent.eml", + enc: false, + sig: true, + sig_good: false, + }, + { + filename: "alice.dsig.SHA512.multipart.bad.eml", + enc: false, + sig: true, + sig_good: false, + }, + { + filename: "alice.dsig.SHA512.multipart.eml", + enc: false, + sig: true, + sig_good: true, + }, + { + filename: "alice.dsig.SHA512.multipart.env.eml", + enc: true, + sig: true, + sig_good: true, + }, + { + filename: "alice.dsig.SHA512.multipart.mismatch-econtent.eml", + enc: false, + sig: true, + sig_good: false, + }, + { + filename: "alice.sig.SHA1.opaque.eml", + enc: false, + sig: true, + sig_good: false, + check_text: true, + }, + { + filename: "alice.sig.SHA1.opaque.env.eml", + enc: true, + sig: true, + sig_good: false, + check_text: true, + }, + { + filename: "alice.sig.SHA256.opaque.eml", + enc: false, + sig: true, + sig_good: true, + }, + { + filename: "alice.sig.SHA256.opaque.env.eml", + enc: true, + sig: true, + sig_good: true, + }, + { + filename: "alice.sig.SHA384.opaque.eml", + enc: false, + sig: true, + sig_good: true, + }, + { + filename: "alice.sig.SHA384.opaque.env.eml", + enc: true, + sig: true, + sig_good: true, + }, + { + filename: "alice.sig.SHA512.opaque.eml", + enc: false, + sig: true, + sig_good: true, + }, + { + filename: "alice.sig.SHA512.opaque.env.eml", + enc: true, + sig: true, + sig_good: true, + }, + + // encrypt-then-sign + { + filename: "alice.env.sig.SHA1.opaque.eml", + enc: false, + sig: true, + sig_good: false, + extra: 1, + }, + { + filename: "alice.env.dsig.SHA1.multipart.eml", + enc: false, + sig: true, + sig_good: false, + }, + { + filename: "alice.env.sig.SHA256.opaque.eml", + enc: false, + sig: true, + sig_good: false, + extra: 1, + }, + { + filename: "alice.env.dsig.SHA256.multipart.eml", + enc: false, + sig: true, + sig_good: false, + extra: 1, + }, + { + filename: "alice.env.sig.SHA384.opaque.eml", + enc: false, + sig: true, + sig_good: false, + extra: 1, + }, + { + filename: "alice.env.dsig.SHA384.multipart.eml", + enc: false, + sig: true, + sig_good: false, + extra: 1, + }, + { + filename: "alice.env.sig.SHA512.opaque.eml", + enc: false, + sig: true, + sig_good: false, + extra: 1, + }, + { + filename: "alice.env.dsig.SHA512.multipart.eml", + enc: false, + sig: true, + sig_good: false, + extra: 1, + }, + + // encrypt-then-sign, then sign again + { + filename: "alice.env.sig.SHA1.opaque.dave.sig.SHA1.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.env.dsig.SHA1.multipart.dave.sig.SHA1.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.env.sig.SHA256.opaque.dave.sig.SHA256.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.env.dsig.SHA256.multipart.dave.sig.SHA256.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.env.sig.SHA384.opaque.dave.sig.SHA384.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.env.dsig.SHA384.multipart.dave.sig.SHA384.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.env.sig.SHA512.opaque.dave.sig.SHA512.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.env.dsig.SHA512.multipart.dave.sig.SHA512.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + + // sign, then sign again + { + filename: "alice.plain.sig.SHA1.opaque.dave.sig.SHA1.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.plain.dsig.SHA1.multipart.dave.sig.SHA1.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.plain.sig.SHA256.opaque.dave.sig.SHA256.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.plain.dsig.SHA256.multipart.dave.sig.SHA256.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.plain.sig.SHA384.opaque.dave.sig.SHA384.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.plain.dsig.SHA384.multipart.dave.sig.SHA384.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.plain.sig.SHA512.opaque.dave.sig.SHA512.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.plain.dsig.SHA512.multipart.dave.sig.SHA512.opaque.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + + { + filename: "alice.plain.sig.SHA1.opaque.dave.dsig.SHA1.multipart.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + }, + { + filename: "alice.plain.dsig.SHA1.multipart.dave.dsig.SHA1.multipart.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.plain.sig.SHA256.opaque.dave.dsig.SHA256.multipart.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: + "alice.plain.dsig.SHA256.multipart.dave.dsig.SHA256.multipart.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.plain.sig.SHA384.opaque.dave.dsig.SHA384.multipart.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: + "alice.plain.dsig.SHA384.multipart.dave.dsig.SHA384.multipart.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: "alice.plain.sig.SHA512.opaque.dave.dsig.SHA512.multipart.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, + { + filename: + "alice.plain.dsig.SHA512.multipart.dave.dsig.SHA512.multipart.eml", + enc: false, + sig: true, + sig_good: false, + dave: 1, + extra: 1, + }, +]; + +let gCopyWaiter = PromiseUtils.defer(); + +add_task(async function copy_messages() { + for (let msg of gMessages) { + let promiseCopyListener = new PromiseTestUtils.PromiseCopyListener(); + + MailServices.copy.copyFileMessage( + do_get_file(smimeDataDirectory + msg.filename), + gInbox, + null, + true, + 0, + "", + promiseCopyListener, + null + ); + + await promiseCopyListener.promise; + promiseCopyListener = null; + } + gCopyWaiter.resolve(); +}); + +add_task(async function check_smime_message() { + await gCopyWaiter.promise; + + let hdrIndex = 0; + + for (let msg of gMessages) { + console.log("checking " + msg.filename); + + let numExpected = 1; + if (msg.enc && msg.sig) { + numExpected++; + } + + let eventsExpected = numExpected; + if ("extra" in msg) { + eventsExpected += msg.extra; + } + + let hdr = mailTestUtils.getMsgHdrN(gInbox, hdrIndex); + let uri = hdr.folder.getUriForMsg(hdr); + let sinkPromise = smimeHeaderSink.expectResults(eventsExpected); + + let conversion = apply_mime_conversion(uri, smimeHeaderSink); + await conversion.promise; + + let contents = conversion._data; + // dump("contents: " + contents + "\n"); + + if (!msg.sig || msg.sig_good || "check_text" in msg) { + let expected = "This is a test message from Alice to Bob."; + Assert.ok(contents.includes(expected)); + } + // Check that we're also using the display output. + Assert.ok(contents.includes("")); + + await sinkPromise; + + let r = smimeHeaderSink._results; + Assert.equal(r.length, numExpected); + + let sigIndex = 0; + + if (msg.enc) { + Assert.equal(r[0].type, "encrypted"); + Assert.equal(r[0].status, 0); + Assert.equal(r[0].certificate, null); + sigIndex = 1; + } + if (msg.sig) { + Assert.equal(r[sigIndex].type, "signed"); + let cert = r[sigIndex].certificate; + if (msg.sig_good) { + Assert.notEqual(cert, null); + } + if (cert) { + if ("dave" in msg) { + Assert.equal(cert.emailAddress, "dave@example.com"); + } else { + Assert.equal(cert.emailAddress, "alice@example.com"); + } + } + if (msg.sig_good) { + Assert.equal(r[sigIndex].status, 0); + } else { + Assert.notEqual(r[sigIndex].status, 0); + } + } + + hdrIndex++; + } +}); -- cgit v1.2.3