michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: /** michael@0: * This file tests signature extraction using Windows Authenticode APIs of michael@0: * downloaded files. michael@0: */ michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Globals michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", michael@0: "resource://gre/modules/FileUtils.jsm"); michael@0: XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", michael@0: "resource://gre/modules/NetUtil.jsm"); michael@0: XPCOMUtils.defineLazyModuleGetter(this, "Promise", michael@0: "resource://gre/modules/Promise.jsm"); michael@0: XPCOMUtils.defineLazyModuleGetter(this, "Task", michael@0: "resource://gre/modules/Task.jsm"); michael@0: michael@0: const BackgroundFileSaverOutputStream = Components.Constructor( michael@0: "@mozilla.org/network/background-file-saver;1?mode=outputstream", michael@0: "nsIBackgroundFileSaver"); michael@0: michael@0: const StringInputStream = Components.Constructor( michael@0: "@mozilla.org/io/string-input-stream;1", michael@0: "nsIStringInputStream", michael@0: "setData"); michael@0: michael@0: const TEST_FILE_NAME_1 = "test-backgroundfilesaver-1.txt"; michael@0: michael@0: /** michael@0: * Returns a reference to a temporary file. If the file is then created, it michael@0: * will be removed when tests in this file finish. michael@0: */ michael@0: function getTempFile(aLeafName) { michael@0: let file = FileUtils.getFile("TmpD", [aLeafName]); michael@0: do_register_cleanup(function GTF_cleanup() { michael@0: if (file.exists()) { michael@0: file.remove(false); michael@0: } michael@0: }); michael@0: return file; michael@0: } michael@0: michael@0: /** michael@0: * Waits for the given saver object to complete. michael@0: * michael@0: * @param aSaver michael@0: * The saver, with the output stream or a stream listener implementation. michael@0: * @param aOnTargetChangeFn michael@0: * Optional callback invoked with the target file name when it changes. michael@0: * michael@0: * @return {Promise} michael@0: * @resolves When onSaveComplete is called with a success code. michael@0: * @rejects With an exception, if onSaveComplete is called with a failure code. michael@0: */ michael@0: function promiseSaverComplete(aSaver, aOnTargetChangeFn) { michael@0: let deferred = Promise.defer(); michael@0: aSaver.observer = { michael@0: onTargetChange: function BFSO_onSaveComplete(aSaver, aTarget) michael@0: { michael@0: if (aOnTargetChangeFn) { michael@0: aOnTargetChangeFn(aTarget); michael@0: } michael@0: }, michael@0: onSaveComplete: function BFSO_onSaveComplete(aSaver, aStatus) michael@0: { michael@0: if (Components.isSuccessCode(aStatus)) { michael@0: deferred.resolve(); michael@0: } else { michael@0: deferred.reject(new Components.Exception("Saver failed.", aStatus)); michael@0: } michael@0: }, michael@0: }; michael@0: return deferred.promise; michael@0: } michael@0: michael@0: /** michael@0: * Feeds a string to a BackgroundFileSaverOutputStream. michael@0: * michael@0: * @param aSourceString michael@0: * The source data to copy. michael@0: * @param aSaverOutputStream michael@0: * The BackgroundFileSaverOutputStream to feed. michael@0: * @param aCloseWhenDone michael@0: * If true, the output stream will be closed when the copy finishes. michael@0: * michael@0: * @return {Promise} michael@0: * @resolves When the copy completes with a success code. michael@0: * @rejects With an exception, if the copy fails. michael@0: */ michael@0: function promiseCopyToSaver(aSourceString, aSaverOutputStream, aCloseWhenDone) { michael@0: let deferred = Promise.defer(); michael@0: let inputStream = new StringInputStream(aSourceString, aSourceString.length); michael@0: let copier = Cc["@mozilla.org/network/async-stream-copier;1"] michael@0: .createInstance(Ci.nsIAsyncStreamCopier); michael@0: copier.init(inputStream, aSaverOutputStream, null, false, true, 0x8000, true, michael@0: aCloseWhenDone); michael@0: copier.asyncCopy({ michael@0: onStartRequest: function () { }, michael@0: onStopRequest: function (aRequest, aContext, aStatusCode) michael@0: { michael@0: if (Components.isSuccessCode(aStatusCode)) { michael@0: deferred.resolve(); michael@0: } else { michael@0: deferred.reject(new Components.Exception(aResult)); michael@0: } michael@0: }, michael@0: }, null); michael@0: return deferred.promise; michael@0: } michael@0: michael@0: let gStillRunning = true; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Tests michael@0: michael@0: function run_test() michael@0: { michael@0: run_next_test(); michael@0: } michael@0: michael@0: add_task(function test_setup() michael@0: { michael@0: // Wait 10 minutes, that is half of the external xpcshell timeout. michael@0: do_timeout(10 * 60 * 1000, function() { michael@0: if (gStillRunning) { michael@0: do_throw("Test timed out."); michael@0: } michael@0: }) michael@0: }); michael@0: michael@0: function readFileToString(aFilename) { michael@0: let f = do_get_file(aFilename); michael@0: let stream = Cc["@mozilla.org/network/file-input-stream;1"] michael@0: .createInstance(Ci.nsIFileInputStream); michael@0: stream.init(f, -1, 0, 0); michael@0: let buf = NetUtil.readInputStreamToString(stream, stream.available()); michael@0: return buf; michael@0: } michael@0: michael@0: add_task(function test_signature() michael@0: { michael@0: // Check that we get a signature if the saver is finished on Windows. michael@0: let destFile = getTempFile(TEST_FILE_NAME_1); michael@0: michael@0: let data = readFileToString("data/signed_win.exe"); michael@0: let saver = new BackgroundFileSaverOutputStream(); michael@0: let completionPromise = promiseSaverComplete(saver); michael@0: michael@0: try { michael@0: let signatureInfo = saver.signatureInfo; michael@0: do_throw("Can't get signature before saver is complete."); michael@0: } catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { } michael@0: michael@0: saver.enableSignatureInfo(); michael@0: saver.setTarget(destFile, false); michael@0: yield promiseCopyToSaver(data, saver, true); michael@0: michael@0: saver.finish(Cr.NS_OK); michael@0: yield completionPromise; michael@0: michael@0: // There's only one nsIX509CertList in the signature array. michael@0: do_check_eq(1, saver.signatureInfo.length); michael@0: let certLists = saver.signatureInfo.enumerate(); michael@0: do_check_true(certLists.hasMoreElements()); michael@0: let certList = certLists.getNext().QueryInterface(Ci.nsIX509CertList); michael@0: do_check_false(certLists.hasMoreElements()); michael@0: michael@0: // Check that it has 3 certs. michael@0: let certs = certList.getEnumerator(); michael@0: do_check_true(certs.hasMoreElements()); michael@0: let signer = certs.getNext().QueryInterface(Ci.nsIX509Cert); michael@0: do_check_true(certs.hasMoreElements()); michael@0: let issuer = certs.getNext().QueryInterface(Ci.nsIX509Cert); michael@0: do_check_true(certs.hasMoreElements()); michael@0: let root = certs.getNext().QueryInterface(Ci.nsIX509Cert); michael@0: do_check_false(certs.hasMoreElements()); michael@0: michael@0: // Check that the certs have expected strings attached. michael@0: let organization = "Microsoft Corporation"; michael@0: do_check_eq("Microsoft Corporation", signer.commonName); michael@0: do_check_eq(organization, signer.organization); michael@0: do_check_eq("Copyright (c) 2002 Microsoft Corp.", signer.organizationalUnit); michael@0: michael@0: do_check_eq("Microsoft Code Signing PCA", issuer.commonName); michael@0: do_check_eq(organization, issuer.organization); michael@0: do_check_eq("Copyright (c) 2000 Microsoft Corp.", issuer.organizationalUnit); michael@0: michael@0: do_check_eq("Microsoft Root Authority", root.commonName); michael@0: do_check_false(root.organization); michael@0: do_check_eq("Copyright (c) 1997 Microsoft Corp.", root.organizationalUnit); michael@0: michael@0: // Clean up. michael@0: destFile.remove(false); michael@0: }); michael@0: michael@0: add_task(function test_teardown() michael@0: { michael@0: gStillRunning = false; michael@0: });