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