michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: michael@0: // A very thin wrapper around nsICryptoHash. It's not strictly michael@0: // necessary, but makes the code a bit cleaner and gives us the michael@0: // opportunity to verify that our implementations give the results that michael@0: // we expect, for example if we have to interoperate with a server. michael@0: // michael@0: // The digest* methods reset the state of the hasher, so it's michael@0: // necessary to call init() explicitly after them. michael@0: // michael@0: // Works only in Firefox 1.5+. michael@0: // michael@0: // IMPORTANT NOTE: Due to https://bugzilla.mozilla.org/show_bug.cgi?id=321024 michael@0: // you cannot use the cryptohasher before app-startup. The symptom of doing michael@0: // so is a segfault in NSS. michael@0: michael@0: /** michael@0: * Instantiate a new hasher. You must explicitly call init() before use! michael@0: */ michael@0: function G_CryptoHasher() { michael@0: this.debugZone = "cryptohasher"; michael@0: this.hasher_ = null; michael@0: } michael@0: michael@0: G_CryptoHasher.algorithms = { michael@0: MD2: Ci.nsICryptoHash.MD2, michael@0: MD5: Ci.nsICryptoHash.MD5, michael@0: SHA1: Ci.nsICryptoHash.SHA1, michael@0: SHA256: Ci.nsICryptoHash.SHA256, michael@0: SHA384: Ci.nsICryptoHash.SHA384, michael@0: SHA512: Ci.nsICryptoHash.SHA512, michael@0: }; michael@0: michael@0: /** michael@0: * Initialize the hasher. This function must be called after every call michael@0: * to one of the digest* methods. michael@0: * michael@0: * @param algorithm Constant from G_CryptoHasher.algorithms specifying the michael@0: * algorithm this hasher will use michael@0: */ michael@0: G_CryptoHasher.prototype.init = function(algorithm) { michael@0: var validAlgorithm = false; michael@0: for (var alg in G_CryptoHasher.algorithms) michael@0: if (algorithm == G_CryptoHasher.algorithms[alg]) michael@0: validAlgorithm = true; michael@0: michael@0: if (!validAlgorithm) michael@0: throw new Error("Invalid algorithm: " + algorithm); michael@0: michael@0: this.hasher_ = Cc["@mozilla.org/security/hash;1"] michael@0: .createInstance(Ci.nsICryptoHash); michael@0: this.hasher_.init(algorithm); michael@0: } michael@0: michael@0: /** michael@0: * Update the hash's internal state with input given in a string. Can be michael@0: * called multiple times for incrementeal hash updates. michael@0: * michael@0: * @param input String containing data to hash. michael@0: */ michael@0: G_CryptoHasher.prototype.updateFromString = function(input) { michael@0: if (!this.hasher_) michael@0: throw new Error("You must initialize the hasher first!"); michael@0: michael@0: var stream = Cc['@mozilla.org/io/string-input-stream;1'] michael@0: .createInstance(Ci.nsIStringInputStream); michael@0: stream.setData(input, input.length); michael@0: this.updateFromStream(stream); michael@0: } michael@0: michael@0: /** michael@0: * Update the hash's internal state with input given in an array. Can be michael@0: * called multiple times for incremental hash updates. michael@0: * michael@0: * @param input Array containing data to hash. michael@0: */ michael@0: G_CryptoHasher.prototype.updateFromArray = function(input) { michael@0: if (!this.hasher_) michael@0: throw new Error("You must initialize the hasher first!"); michael@0: michael@0: this.hasher_.update(input, input.length); michael@0: } michael@0: michael@0: /** michael@0: * Update the hash's internal state with input given in a stream. Can be michael@0: * called multiple times from incremental hash updates. michael@0: */ michael@0: G_CryptoHasher.prototype.updateFromStream = function(stream) { michael@0: if (!this.hasher_) michael@0: throw new Error("You must initialize the hasher first!"); michael@0: michael@0: if (stream.available()) michael@0: this.hasher_.updateFromStream(stream, stream.available()); michael@0: } michael@0: michael@0: /** michael@0: * @returns The hash value as a string (sequence of 8-bit values) michael@0: */ michael@0: G_CryptoHasher.prototype.digestRaw = function() { michael@0: var digest = this.hasher_.finish(false /* not b64 encoded */); michael@0: this.hasher_ = null; michael@0: return digest; michael@0: } michael@0: michael@0: /** michael@0: * @returns The hash value as a base64-encoded string michael@0: */ michael@0: G_CryptoHasher.prototype.digestBase64 = function() { michael@0: var digest = this.hasher_.finish(true /* b64 encoded */); michael@0: this.hasher_ = null; michael@0: return digest; michael@0: } michael@0: michael@0: /** michael@0: * @returns The hash value as a hex-encoded string michael@0: */ michael@0: G_CryptoHasher.prototype.digestHex = function() { michael@0: var raw = this.digestRaw(); michael@0: return this.toHex_(raw); michael@0: } michael@0: michael@0: /** michael@0: * Converts a sequence of values to a hex-encoded string. The input is a michael@0: * a string, so you can stick 16-bit values in each character. michael@0: * michael@0: * @param str String to conver to hex. (Often this is just a sequence of michael@0: * 16-bit values) michael@0: * michael@0: * @returns String containing the hex representation of the input michael@0: */ michael@0: G_CryptoHasher.prototype.toHex_ = function(str) { michael@0: var hexchars = '0123456789ABCDEF'; michael@0: var hexrep = new Array(str.length * 2); michael@0: michael@0: for (var i = 0; i < str.length; ++i) { michael@0: hexrep[i * 2] = hexchars.charAt((str.charCodeAt(i) >> 4) & 15); michael@0: hexrep[i * 2 + 1] = hexchars.charAt(str.charCodeAt(i) & 15); michael@0: } michael@0: return hexrep.join(''); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: /** michael@0: * Lame unittest function michael@0: */ michael@0: function TEST_G_CryptoHasher() { michael@0: if (G_GDEBUG) { michael@0: var z = "cryptohasher UNITTEST"; michael@0: G_debugService.enableZone(z); michael@0: michael@0: G_Debug(z, "Starting"); michael@0: michael@0: var md5 = function(str) { michael@0: var hasher = new G_CryptoHasher(); michael@0: hasher.init(G_CryptoHasher.algorithms.MD5); michael@0: hasher.updateFromString(str); michael@0: return hasher.digestHex().toLowerCase(); michael@0: }; michael@0: michael@0: // test vectors from: http://www.faqs.org/rfcs/rfc1321.html michael@0: var vectors = {"": "d41d8cd98f00b204e9800998ecf8427e", michael@0: "a": "0cc175b9c0f1b6a831c399e269772661", michael@0: "abc": "900150983cd24fb0d6963f7d28e17f72", michael@0: "message digest": "f96b697d7cb7938d525a2f31aaf161d0", michael@0: "abcdefghijklmnopqrstuvwxyz": "c3fcd3d76192e4007dfb496cca67e13b", michael@0: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789": "d174ab98d277d9f5a5611c2c9f419d9f", michael@0: "12345678901234567890123456789012345678901234567890123456789012345678901234567890": "57edf4a22be3c955ac49da2e2107b67a"}; michael@0: michael@0: G_Debug(z, "PASSED"); michael@0: } michael@0: } michael@0: #endif