test_signature_extraction.js (5959B)
1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* Any copyright is dedicated to the Public Domain. 4 * http://creativecommons.org/publicdomain/zero/1.0/ */ 5 6 /** 7 * This file tests signature extraction using Windows Authenticode APIs of 8 * downloaded files. 9 */ 10 11 //////////////////////////////////////////////////////////////////////////////// 12 //// Globals 13 "use strict"; 14 15 ChromeUtils.defineESModuleGetters(this, { 16 FileTestUtils: "resource://testing-common/FileTestUtils.sys.mjs", 17 }); 18 19 const BackgroundFileSaverOutputStream = Components.Constructor( 20 "@mozilla.org/network/background-file-saver;1?mode=outputstream", 21 "nsIBackgroundFileSaver" 22 ); 23 24 const StringInputStream = Components.Constructor( 25 "@mozilla.org/io/string-input-stream;1", 26 "nsIStringInputStream", 27 "setByteStringData" 28 ); 29 30 const TEST_FILE_NAME_1 = "test-backgroundfilesaver-1.txt"; 31 32 /** 33 * Returns a reference to a temporary file that is guaranteed not to exist and 34 * is cleaned up later. See FileTestUtils.getTempFile for details. 35 */ 36 function getTempFile(leafName) { 37 return FileTestUtils.getTempFile(leafName); 38 } 39 40 /** 41 * Waits for the given saver object to complete. 42 * 43 * @param aSaver 44 * The saver, with the output stream or a stream listener implementation. 45 * @param aOnTargetChangeFn 46 * Optional callback invoked with the target file name when it changes. 47 * 48 * @returns {Promise<void>} 49 * Resolves when onSaveComplete is called with a success code. 50 * @rejects With an exception, if onSaveComplete is called with a failure code. 51 */ 52 function promiseSaverComplete(aSaver, aOnTargetChangeFn) { 53 return new Promise((resolve, reject) => { 54 aSaver.observer = { 55 onTargetChange: function BFSO_onSaveComplete(saver, aTarget) { 56 if (aOnTargetChangeFn) { 57 aOnTargetChangeFn(aTarget); 58 } 59 }, 60 onSaveComplete: function BFSO_onSaveComplete(saver, aStatus) { 61 if (Components.isSuccessCode(aStatus)) { 62 resolve(); 63 } else { 64 reject(new Components.Exception("Saver failed.", aStatus)); 65 } 66 }, 67 }; 68 }); 69 } 70 71 /** 72 * Feeds a string to a BackgroundFileSaverOutputStream. 73 * 74 * @param aSourceString 75 * The source data to copy. 76 * @param aSaverOutputStream 77 * The BackgroundFileSaverOutputStream to feed. 78 * @param aCloseWhenDone 79 * If true, the output stream will be closed when the copy finishes. 80 * 81 * @returns {Promise<void>} 82 * Resolves when the copy completes with a success code. 83 * @rejects With an exception, if the copy fails. 84 */ 85 function promiseCopyToSaver(aSourceString, aSaverOutputStream, aCloseWhenDone) { 86 return new Promise((resolve, reject) => { 87 let inputStream = new StringInputStream(aSourceString); 88 let copier = Cc[ 89 "@mozilla.org/network/async-stream-copier;1" 90 ].createInstance(Ci.nsIAsyncStreamCopier); 91 copier.init( 92 inputStream, 93 aSaverOutputStream, 94 null, 95 false, 96 true, 97 0x8000, 98 true, 99 aCloseWhenDone 100 ); 101 copier.asyncCopy( 102 { 103 onStartRequest() {}, 104 onStopRequest(aRequest, aContext, aStatusCode) { 105 if (Components.isSuccessCode(aStatusCode)) { 106 resolve(); 107 } else { 108 reject(new Components.Exception(aStatusCode)); 109 } 110 }, 111 }, 112 null 113 ); 114 }); 115 } 116 117 var gStillRunning = true; 118 119 //////////////////////////////////////////////////////////////////////////////// 120 //// Tests 121 122 add_task(function test_setup() { 123 // Wait 10 minutes, that is half of the external xpcshell timeout. 124 do_timeout(10 * 60 * 1000, function () { 125 if (gStillRunning) { 126 do_throw("Test timed out."); 127 } 128 }); 129 }); 130 131 function readFileToString(aFilename) { 132 let f = do_get_file(aFilename); 133 let stream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance( 134 Ci.nsIFileInputStream 135 ); 136 stream.init(f, -1, 0, 0); 137 let buf = NetUtil.readInputStreamToString(stream, stream.available()); 138 return buf; 139 } 140 141 add_task(async function test_signature() { 142 // Check that we get a signature if the saver is finished on Windows. 143 let destFile = getTempFile(TEST_FILE_NAME_1); 144 145 let data = readFileToString("data/signed_win.exe"); 146 let saver = new BackgroundFileSaverOutputStream(); 147 let completionPromise = promiseSaverComplete(saver); 148 149 try { 150 saver.signatureInfo; 151 do_throw("Can't get signature before saver is complete."); 152 } catch (ex) { 153 if (ex.result != Cr.NS_ERROR_NOT_AVAILABLE) { 154 throw ex; 155 } 156 } 157 158 saver.enableSignatureInfo(); 159 saver.setTarget(destFile, false); 160 await promiseCopyToSaver(data, saver, true); 161 162 saver.finish(Cr.NS_OK); 163 await completionPromise; 164 165 // There's only one Array of certs(raw bytes) in the signature array. 166 Assert.equal(1, saver.signatureInfo.length); 167 let certLists = saver.signatureInfo; 168 Assert.strictEqual(certLists.length, 1); 169 170 // Check that it has 3 certs(raw bytes). 171 let certs = certLists[0]; 172 Assert.strictEqual(certs.length, 3); 173 174 const certDB = Cc["@mozilla.org/security/x509certdb;1"].getService( 175 Ci.nsIX509CertDB 176 ); 177 let signer = certDB.constructX509(certs[0]); 178 let issuer = certDB.constructX509(certs[1]); 179 let root = certDB.constructX509(certs[2]); 180 181 let organization = "Microsoft Corporation"; 182 Assert.equal("Microsoft Corporation", signer.commonName); 183 Assert.equal(organization, signer.organization); 184 Assert.equal("Copyright (c) 2002 Microsoft Corp.", signer.organizationalUnit); 185 186 Assert.equal("Microsoft Code Signing PCA", issuer.commonName); 187 Assert.equal(organization, issuer.organization); 188 Assert.equal("Copyright (c) 2000 Microsoft Corp.", issuer.organizationalUnit); 189 190 Assert.equal("Microsoft Root Authority", root.commonName); 191 Assert.ok(!root.organization); 192 Assert.equal("Copyright (c) 1997 Microsoft Corp.", root.organizationalUnit); 193 194 // Clean up. 195 destFile.remove(false); 196 }); 197 198 add_task(function test_teardown() { 199 gStillRunning = false; 200 });