toolkit/components/url-classifier/content/moz/cryptohasher.js

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5
michael@0 6 // A very thin wrapper around nsICryptoHash. It's not strictly
michael@0 7 // necessary, but makes the code a bit cleaner and gives us the
michael@0 8 // opportunity to verify that our implementations give the results that
michael@0 9 // we expect, for example if we have to interoperate with a server.
michael@0 10 //
michael@0 11 // The digest* methods reset the state of the hasher, so it's
michael@0 12 // necessary to call init() explicitly after them.
michael@0 13 //
michael@0 14 // Works only in Firefox 1.5+.
michael@0 15 //
michael@0 16 // IMPORTANT NOTE: Due to https://bugzilla.mozilla.org/show_bug.cgi?id=321024
michael@0 17 // you cannot use the cryptohasher before app-startup. The symptom of doing
michael@0 18 // so is a segfault in NSS.
michael@0 19
michael@0 20 /**
michael@0 21 * Instantiate a new hasher. You must explicitly call init() before use!
michael@0 22 */
michael@0 23 function G_CryptoHasher() {
michael@0 24 this.debugZone = "cryptohasher";
michael@0 25 this.hasher_ = null;
michael@0 26 }
michael@0 27
michael@0 28 G_CryptoHasher.algorithms = {
michael@0 29 MD2: Ci.nsICryptoHash.MD2,
michael@0 30 MD5: Ci.nsICryptoHash.MD5,
michael@0 31 SHA1: Ci.nsICryptoHash.SHA1,
michael@0 32 SHA256: Ci.nsICryptoHash.SHA256,
michael@0 33 SHA384: Ci.nsICryptoHash.SHA384,
michael@0 34 SHA512: Ci.nsICryptoHash.SHA512,
michael@0 35 };
michael@0 36
michael@0 37 /**
michael@0 38 * Initialize the hasher. This function must be called after every call
michael@0 39 * to one of the digest* methods.
michael@0 40 *
michael@0 41 * @param algorithm Constant from G_CryptoHasher.algorithms specifying the
michael@0 42 * algorithm this hasher will use
michael@0 43 */
michael@0 44 G_CryptoHasher.prototype.init = function(algorithm) {
michael@0 45 var validAlgorithm = false;
michael@0 46 for (var alg in G_CryptoHasher.algorithms)
michael@0 47 if (algorithm == G_CryptoHasher.algorithms[alg])
michael@0 48 validAlgorithm = true;
michael@0 49
michael@0 50 if (!validAlgorithm)
michael@0 51 throw new Error("Invalid algorithm: " + algorithm);
michael@0 52
michael@0 53 this.hasher_ = Cc["@mozilla.org/security/hash;1"]
michael@0 54 .createInstance(Ci.nsICryptoHash);
michael@0 55 this.hasher_.init(algorithm);
michael@0 56 }
michael@0 57
michael@0 58 /**
michael@0 59 * Update the hash's internal state with input given in a string. Can be
michael@0 60 * called multiple times for incrementeal hash updates.
michael@0 61 *
michael@0 62 * @param input String containing data to hash.
michael@0 63 */
michael@0 64 G_CryptoHasher.prototype.updateFromString = function(input) {
michael@0 65 if (!this.hasher_)
michael@0 66 throw new Error("You must initialize the hasher first!");
michael@0 67
michael@0 68 var stream = Cc['@mozilla.org/io/string-input-stream;1']
michael@0 69 .createInstance(Ci.nsIStringInputStream);
michael@0 70 stream.setData(input, input.length);
michael@0 71 this.updateFromStream(stream);
michael@0 72 }
michael@0 73
michael@0 74 /**
michael@0 75 * Update the hash's internal state with input given in an array. Can be
michael@0 76 * called multiple times for incremental hash updates.
michael@0 77 *
michael@0 78 * @param input Array containing data to hash.
michael@0 79 */
michael@0 80 G_CryptoHasher.prototype.updateFromArray = function(input) {
michael@0 81 if (!this.hasher_)
michael@0 82 throw new Error("You must initialize the hasher first!");
michael@0 83
michael@0 84 this.hasher_.update(input, input.length);
michael@0 85 }
michael@0 86
michael@0 87 /**
michael@0 88 * Update the hash's internal state with input given in a stream. Can be
michael@0 89 * called multiple times from incremental hash updates.
michael@0 90 */
michael@0 91 G_CryptoHasher.prototype.updateFromStream = function(stream) {
michael@0 92 if (!this.hasher_)
michael@0 93 throw new Error("You must initialize the hasher first!");
michael@0 94
michael@0 95 if (stream.available())
michael@0 96 this.hasher_.updateFromStream(stream, stream.available());
michael@0 97 }
michael@0 98
michael@0 99 /**
michael@0 100 * @returns The hash value as a string (sequence of 8-bit values)
michael@0 101 */
michael@0 102 G_CryptoHasher.prototype.digestRaw = function() {
michael@0 103 var digest = this.hasher_.finish(false /* not b64 encoded */);
michael@0 104 this.hasher_ = null;
michael@0 105 return digest;
michael@0 106 }
michael@0 107
michael@0 108 /**
michael@0 109 * @returns The hash value as a base64-encoded string
michael@0 110 */
michael@0 111 G_CryptoHasher.prototype.digestBase64 = function() {
michael@0 112 var digest = this.hasher_.finish(true /* b64 encoded */);
michael@0 113 this.hasher_ = null;
michael@0 114 return digest;
michael@0 115 }
michael@0 116
michael@0 117 /**
michael@0 118 * @returns The hash value as a hex-encoded string
michael@0 119 */
michael@0 120 G_CryptoHasher.prototype.digestHex = function() {
michael@0 121 var raw = this.digestRaw();
michael@0 122 return this.toHex_(raw);
michael@0 123 }
michael@0 124
michael@0 125 /**
michael@0 126 * Converts a sequence of values to a hex-encoded string. The input is a
michael@0 127 * a string, so you can stick 16-bit values in each character.
michael@0 128 *
michael@0 129 * @param str String to conver to hex. (Often this is just a sequence of
michael@0 130 * 16-bit values)
michael@0 131 *
michael@0 132 * @returns String containing the hex representation of the input
michael@0 133 */
michael@0 134 G_CryptoHasher.prototype.toHex_ = function(str) {
michael@0 135 var hexchars = '0123456789ABCDEF';
michael@0 136 var hexrep = new Array(str.length * 2);
michael@0 137
michael@0 138 for (var i = 0; i < str.length; ++i) {
michael@0 139 hexrep[i * 2] = hexchars.charAt((str.charCodeAt(i) >> 4) & 15);
michael@0 140 hexrep[i * 2 + 1] = hexchars.charAt(str.charCodeAt(i) & 15);
michael@0 141 }
michael@0 142 return hexrep.join('');
michael@0 143 }
michael@0 144
michael@0 145 #ifdef DEBUG
michael@0 146 /**
michael@0 147 * Lame unittest function
michael@0 148 */
michael@0 149 function TEST_G_CryptoHasher() {
michael@0 150 if (G_GDEBUG) {
michael@0 151 var z = "cryptohasher UNITTEST";
michael@0 152 G_debugService.enableZone(z);
michael@0 153
michael@0 154 G_Debug(z, "Starting");
michael@0 155
michael@0 156 var md5 = function(str) {
michael@0 157 var hasher = new G_CryptoHasher();
michael@0 158 hasher.init(G_CryptoHasher.algorithms.MD5);
michael@0 159 hasher.updateFromString(str);
michael@0 160 return hasher.digestHex().toLowerCase();
michael@0 161 };
michael@0 162
michael@0 163 // test vectors from: http://www.faqs.org/rfcs/rfc1321.html
michael@0 164 var vectors = {"": "d41d8cd98f00b204e9800998ecf8427e",
michael@0 165 "a": "0cc175b9c0f1b6a831c399e269772661",
michael@0 166 "abc": "900150983cd24fb0d6963f7d28e17f72",
michael@0 167 "message digest": "f96b697d7cb7938d525a2f31aaf161d0",
michael@0 168 "abcdefghijklmnopqrstuvwxyz": "c3fcd3d76192e4007dfb496cca67e13b",
michael@0 169 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789": "d174ab98d277d9f5a5611c2c9f419d9f",
michael@0 170 "12345678901234567890123456789012345678901234567890123456789012345678901234567890": "57edf4a22be3c955ac49da2e2107b67a"};
michael@0 171
michael@0 172 G_Debug(z, "PASSED");
michael@0 173 }
michael@0 174 }
michael@0 175 #endif

mercurial