From cccc0ff868ebbd1e0c2833a953c21f41f9c06ef5 Mon Sep 17 00:00:00 2001 From: "darin%meer.net" Date: Wed, 8 Jun 2005 22:15:17 +0000 Subject: [PATCH] Verify downloads leveraging nsICryptoHash. --- .../mozapps/update/src/nsUpdateService.js.in | 87 +++++++++++++++---- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/toolkit/mozapps/update/src/nsUpdateService.js.in b/toolkit/mozapps/update/src/nsUpdateService.js.in index 82ededc43d5..8bf26b54285 100644 --- a/toolkit/mozapps/update/src/nsUpdateService.js.in +++ b/toolkit/mozapps/update/src/nsUpdateService.js.in @@ -85,6 +85,9 @@ const nsIUpdateService = Components.interfaces.nsIUpdateService; const nsIUpdateItem = Components.interfaces.nsIUpdateItem; const nsIPrefLocalizedString = Components.interfaces.nsIPrefLocalizedString; const nsIIncrementalDownload = Components.interfaces.nsIIncrementalDownload; +const nsIFileInputStream = Components.interfaces.nsIFileInputStream; +const nsIFileOutputStream = Components.interfaces.nsIFileOutputStream; +const nsICryptoHash = Components.interfaces.nsICryptoHash; const Node = Components.interfaces.nsIDOMNode; @@ -103,6 +106,17 @@ function LOG(string) { gConsole.logStringMessage(string); } +/** + * Convert a string containing binary values to hex. + */ +function binaryToHex(input) { + var result = ""; + for (var i = 0; i < input.length; ++i) { + result += input.charCodeAt(i).toString(16); + } + return result; +} + /** * Gets a File URL spec for a nsIFile * @param file @@ -239,7 +253,7 @@ function stripPrefix(string, prefix) { function writeStringToFile(file, text) { var fos = Components.classes["@mozilla.org/network/safe-file-output-stream;1"]. - createInstance(Components.interfaces.nsIFileOutputStream); + createInstance(nsIFileOutputStream); var modeFlags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE; if (!file.exists()) file.create(nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE); @@ -259,7 +273,7 @@ function writeStringToFile(file, text) { function readStringFromFile(file) { var fis = Components.classes["@mozilla.org/network/file-input-stream;1"]. - createInstance(Components.interfaces.nsIFileInputStream); + createInstance(nsIFileInputStream); var modeFlags = MODE_RDONLY; if (!file.exists()) return null; @@ -780,7 +794,8 @@ Checker.prototype = { function Downloader() { } Downloader.prototype = { - _request: null, + _patch: null, // UpdatePatch + _request: null, // nsIIncrementalDownload /** * @@ -825,6 +840,42 @@ Downloader.prototype = { return readStringFromFile(statusFile); }, + /** + * Verify the downloaded file. We assume that the download is complete at + * this point. + */ + _verifyDownload: function() { + var fileStream = Components.classes["@mozilla.org/network/file-input-stream;1"]. + createInstance(nsIFileInputStream); + fileStream.init(this._request.destination, MODE_RDONLY, PERMS_FILE, 0); + + try { + var hash = Components.classes["@mozilla.org/security/hash;1"]. + createInstance(nsICryptoHash); + const map = { + "MD2" : nsICryptoHash.MD2, + "MD5" : nsICryptoHash.MD5, + "SHA1" : nsICryptoHash.SHA1, + "SHA256" : nsICryptoHash.SHA256, + "SHA384" : nsICryptoHash.SHA384, + "SHA512" : nsICryptoHash.SHA512 + }; + var hashfunction = this._patch.hashfunction.toUpperCase(); + if (!(hashfunction in map)) + return false; + hash.init(map[hashfunction]); + hash.updateFromStream(fileStream, -1); + digest = binaryToHex(hash.finish(false)); + } catch (e) { + LOG("failed to compute hash of downloaded update archive"); + digest = ""; + } + + fileStream.close(); + + return digest == this._patch.hashvalue; + }, + /** * Select the patch to use given the current state of updateDir and the given * set of update patches. @@ -963,8 +1014,8 @@ Downloader.prototype = { // This function may return null, which indicates that there are no patches // to download. - var patch = this._selectPatch(update, updateDir); - if (!patch) { + this._patch = this._selectPatch(update, updateDir); + if (!this._patch) { LOG("no patch to download"); return; } @@ -974,7 +1025,7 @@ Downloader.prototype = { var ios = Components.classes["@mozilla.org/network/io-service;1"]. getService(Components.interfaces.nsIIOService); - var uri = ios.newURI(patch.url, null, null); + var uri = ios.newURI(this._patch.url, null, null); this._request = Components.classes["@mozilla.org/network/incremental-download;1"]. @@ -1048,16 +1099,24 @@ Downloader.prototype = { request.QueryInterface(nsIIncrementalDownload); LOG("Downloader.onStopRequest: " + request.URI.spec + ", status = " + status); + if (Components.isSuccessCode(status)) { + var state; + if (this._verifyDownload()) { + state = STATE_PENDING; + } else { + LOG("download verification failed"); + state = STATE_FAILED; + // TODO: use more informative error code here + status = Components.results.NS_ERROR_UNEXPECTED; + } + this._writeStatusFile(this._getUpdatesDir(), state); + } + var listenerCount = this._listeners.length; for (var i = 0; i < listenerCount; ++i) this._listeners[i].onStopRequest(request, context, status); this._request = null; - - if (Components.isSuccessCode(status)) { - // TODO: Verify download - this._writeStatusFile(this._getUpdatesDir(), STATE_PENDING); - } }, /** @@ -1072,12 +1131,6 @@ Downloader.prototype = { } }; -function Verifier() { -} -Verifier.prototype = { - -}; - function TimerManager() { } TimerManager.prototype = {