OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /** | |
6 * SHA-1. | |
7 * Ripped from package:crypto. | |
8 */ | |
9 library sha1; | |
10 | |
11 import 'dart:math' show pow; | |
12 import 'dart:convert'; | |
13 | |
14 /// Returns the base64-encoded SHA-1 hash of the utf-8 bytes of [input]. | |
15 String hashOfString(String input) { | |
16 Hash hasher = new SHA1(); | |
17 hasher.add(const Utf8Encoder().convert(input)); | |
18 return _bytesToBase64(hasher.close()); | |
19 } | |
20 | |
21 /** | |
22 * Converts a list of bytes into a Base 64 encoded string. | |
23 * | |
24 * The list can be any list of integers in the range 0..255, | |
25 * for example a message digest. | |
26 * | |
27 * If [addLineSeparator] is true, the resulting string will be | |
28 * broken into lines of 76 characters, separated by "\r\n". | |
29 * | |
30 * If [urlSafe] is true, the result is URL and filename safe. | |
31 * | |
32 * Based on [RFC 4648](http://tools.ietf.org/html/rfc4648) | |
33 * | |
34 */ | |
35 String _bytesToBase64(List<int> bytes, | |
36 {bool urlSafe : false, | |
37 bool addLineSeparator : false}) { | |
38 return _CryptoUtils.bytesToBase64(bytes, | |
39 urlSafe, | |
40 addLineSeparator); | |
41 } | |
42 | |
43 | |
44 // Constants. | |
45 const _MASK_8 = 0xff; | |
46 const _MASK_32 = 0xffffffff; | |
47 const _BITS_PER_BYTE = 8; | |
48 const _BYTES_PER_WORD = 4; | |
49 | |
50 // Helper functions used by more than one hasher. | |
51 | |
52 // Rotate left limiting to unsigned 32-bit values. | |
53 int _rotl32(int val, int shift) { | |
54 var mod_shift = shift & 31; | |
55 return ((val << mod_shift) & _MASK_32) | | |
56 ((val & _MASK_32) >> (32 - mod_shift)); | |
57 } | |
58 | |
59 // Base class encapsulating common behavior for cryptographic hash | |
60 // functions. | |
61 abstract class _HashBase implements Hash { | |
62 final int _chunkSizeInWords; | |
63 final int _digestSizeInWords; | |
64 final bool _bigEndianWords; | |
65 final List<int> _currentChunk; | |
66 final List<int> _h; | |
67 int _lengthInBytes = 0; | |
68 List<int> _pendingData; | |
69 bool _digestCalled = false; | |
70 | |
71 _HashBase(int chunkSizeInWords, | |
72 int digestSizeInWords, | |
73 bool this._bigEndianWords) | |
74 : _pendingData = [], | |
75 _currentChunk = new List(chunkSizeInWords), | |
76 _h = new List(digestSizeInWords), | |
77 _chunkSizeInWords = chunkSizeInWords, | |
78 _digestSizeInWords = digestSizeInWords; | |
79 | |
80 // Update the hasher with more data. | |
81 void add(List<int> data) { | |
82 if (_digestCalled) { | |
83 throw new StateError( | |
84 'Hash update method called after digest was retrieved'); | |
85 } | |
86 _lengthInBytes += data.length; | |
87 _pendingData.addAll(data); | |
88 _iterate(); | |
89 } | |
90 | |
91 // Finish the hash computation and return the digest string. | |
92 List<int> close() { | |
93 if (_digestCalled) { | |
94 return _resultAsBytes(); | |
95 } | |
96 _digestCalled = true; | |
97 _finalizeData(); | |
98 _iterate(); | |
99 assert(_pendingData.length == 0); | |
100 return _resultAsBytes(); | |
101 } | |
102 | |
103 // Returns the block size of the hash in bytes. | |
104 int get blockSize { | |
105 return _chunkSizeInWords * _BYTES_PER_WORD; | |
106 } | |
107 | |
108 // One round of the hash computation. | |
109 void _updateHash(List<int> m); | |
110 | |
111 // Helper methods. | |
112 int _add32(x, y) => (x + y) & _MASK_32; | |
113 int _roundUp(val, n) => (val + n - 1) & -n; | |
114 | |
115 // Compute the final result as a list of bytes from the hash words. | |
116 List<int> _resultAsBytes() { | |
117 var result = []; | |
118 for (var i = 0; i < _h.length; i++) { | |
119 result.addAll(_wordToBytes(_h[i])); | |
120 } | |
121 return result; | |
122 } | |
123 | |
124 // Converts a list of bytes to a chunk of 32-bit words. | |
125 void _bytesToChunk(List<int> data, int dataIndex) { | |
126 assert((data.length - dataIndex) >= (_chunkSizeInWords * _BYTES_PER_WORD)); | |
127 | |
128 for (var wordIndex = 0; wordIndex < _chunkSizeInWords; wordIndex++) { | |
129 var w3 = _bigEndianWords ? data[dataIndex] : data[dataIndex + 3]; | |
130 var w2 = _bigEndianWords ? data[dataIndex + 1] : data[dataIndex + 2]; | |
131 var w1 = _bigEndianWords ? data[dataIndex + 2] : data[dataIndex + 1]; | |
132 var w0 = _bigEndianWords ? data[dataIndex + 3] : data[dataIndex]; | |
133 dataIndex += 4; | |
134 var word = (w3 & 0xff) << 24; | |
135 word |= (w2 & _MASK_8) << 16; | |
136 word |= (w1 & _MASK_8) << 8; | |
137 word |= (w0 & _MASK_8); | |
138 _currentChunk[wordIndex] = word; | |
139 } | |
140 } | |
141 | |
142 // Convert a 32-bit word to four bytes. | |
143 List<int> _wordToBytes(int word) { | |
144 List<int> bytes = new List(_BYTES_PER_WORD); | |
145 bytes[0] = (word >> (_bigEndianWords ? 24 : 0)) & _MASK_8; | |
146 bytes[1] = (word >> (_bigEndianWords ? 16 : 8)) & _MASK_8; | |
147 bytes[2] = (word >> (_bigEndianWords ? 8 : 16)) & _MASK_8; | |
148 bytes[3] = (word >> (_bigEndianWords ? 0 : 24)) & _MASK_8; | |
149 return bytes; | |
150 } | |
151 | |
152 // Iterate through data updating the hash computation for each | |
153 // chunk. | |
154 void _iterate() { | |
155 var len = _pendingData.length; | |
156 var chunkSizeInBytes = _chunkSizeInWords * _BYTES_PER_WORD; | |
157 if (len >= chunkSizeInBytes) { | |
158 var index = 0; | |
159 for (; (len - index) >= chunkSizeInBytes; index += chunkSizeInBytes) { | |
160 _bytesToChunk(_pendingData, index); | |
161 _updateHash(_currentChunk); | |
162 } | |
163 _pendingData = _pendingData.sublist(index, len); | |
164 } | |
165 } | |
166 | |
167 // Finalize the data. Add a 1 bit to the end of the message. Expand with | |
168 // 0 bits and add the length of the message. | |
169 void _finalizeData() { | |
170 _pendingData.add(0x80); | |
171 var contentsLength = _lengthInBytes + 9; | |
172 var chunkSizeInBytes = _chunkSizeInWords * _BYTES_PER_WORD; | |
173 var finalizedLength = _roundUp(contentsLength, chunkSizeInBytes); | |
174 var zeroPadding = finalizedLength - contentsLength; | |
175 for (var i = 0; i < zeroPadding; i++) { | |
176 _pendingData.add(0); | |
177 } | |
178 var lengthInBits = _lengthInBytes * _BITS_PER_BYTE; | |
179 assert(lengthInBits < pow(2, 32)); | |
180 if (_bigEndianWords) { | |
181 _pendingData.addAll(_wordToBytes(0)); | |
182 _pendingData.addAll(_wordToBytes(lengthInBits & _MASK_32)); | |
183 } else { | |
184 _pendingData.addAll(_wordToBytes(lengthInBits & _MASK_32)); | |
185 _pendingData.addAll(_wordToBytes(0)); | |
186 } | |
187 } | |
188 } | |
189 | |
190 | |
191 /** | |
192 * Interface for cryptographic hash functions. | |
193 * | |
194 * The [add] method is used to add data to the hash. The [close] method | |
195 * is used to extract the message digest. | |
196 * | |
197 * Once the [close] method has been called no more data can be added using the | |
198 * [add] method. If [add] is called after the first call to [close] a | |
199 * HashException is thrown. | |
200 * | |
201 * If multiple instances of a given Hash is needed the [newInstance] | |
202 * method can provide a new instance. | |
203 */ | |
204 // TODO(floitsch): make Hash implement Sink, EventSink or similar. | |
205 abstract class Hash { | |
206 /** | |
207 * Add a list of bytes to the hash computation. | |
208 */ | |
209 void add(List<int> data); | |
210 | |
211 /** | |
212 * Finish the hash computation and extract the message digest as | |
213 * a list of bytes. | |
214 */ | |
215 List<int> close(); | |
216 | |
217 /** | |
218 * Internal block size of the hash in bytes. | |
219 * | |
220 * This is exposed for use by the HMAC class which needs to know the | |
221 * block size for the [Hash] it is using. | |
222 */ | |
223 int get blockSize; | |
224 } | |
225 | |
226 /** | |
227 * SHA1 hash function implementation. | |
228 */ | |
229 class SHA1 extends _HashBase { | |
230 final List<int> _w; | |
231 | |
232 // Construct a SHA1 hasher object. | |
233 SHA1() : _w = new List(80), super(16, 5, true) { | |
234 _h[0] = 0x67452301; | |
235 _h[1] = 0xEFCDAB89; | |
236 _h[2] = 0x98BADCFE; | |
237 _h[3] = 0x10325476; | |
238 _h[4] = 0xC3D2E1F0; | |
239 } | |
240 | |
241 // Compute one iteration of the SHA1 algorithm with a chunk of | |
242 // 16 32-bit pieces. | |
243 void _updateHash(List<int> m) { | |
244 assert(m.length == 16); | |
245 | |
246 var a = _h[0]; | |
247 var b = _h[1]; | |
248 var c = _h[2]; | |
249 var d = _h[3]; | |
250 var e = _h[4]; | |
251 | |
252 for (var i = 0; i < 80; i++) { | |
253 if (i < 16) { | |
254 _w[i] = m[i]; | |
255 } else { | |
256 var n = _w[i - 3] ^ _w[i - 8] ^ _w[i - 14] ^ _w[i - 16]; | |
257 _w[i] = _rotl32(n, 1); | |
258 } | |
259 var t = _add32(_add32(_rotl32(a, 5), e), _w[i]); | |
260 if (i < 20) { | |
261 t = _add32(_add32(t, (b & c) | (~b & d)), 0x5A827999); | |
262 } else if (i < 40) { | |
263 t = _add32(_add32(t, (b ^ c ^ d)), 0x6ED9EBA1); | |
264 } else if (i < 60) { | |
265 t = _add32(_add32(t, (b & c) | (b & d) | (c & d)), 0x8F1BBCDC); | |
266 } else { | |
267 t = _add32(_add32(t, b ^ c ^ d), 0xCA62C1D6); | |
268 } | |
269 | |
270 e = d; | |
271 d = c; | |
272 c = _rotl32(b, 30); | |
273 b = a; | |
274 a = t & _MASK_32; | |
275 } | |
276 | |
277 _h[0] = _add32(a, _h[0]); | |
278 _h[1] = _add32(b, _h[1]); | |
279 _h[2] = _add32(c, _h[2]); | |
280 _h[3] = _add32(d, _h[3]); | |
281 _h[4] = _add32(e, _h[4]); | |
282 } | |
283 } | |
284 | |
285 abstract class _CryptoUtils { | |
286 | |
287 static const int PAD = 61; // '=' | |
288 static const int CR = 13; // '\r' | |
289 static const int LF = 10; // '\n' | |
290 static const int LINE_LENGTH = 76; | |
291 | |
292 static const String _encodeTable = | |
293 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
294 | |
295 static const String _encodeTableUrlSafe = | |
296 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; | |
297 | |
298 // Lookup table used for finding Base 64 alphabet index of a given byte. | |
299 // -2 : Outside Base 64 alphabet. | |
300 // -1 : '\r' or '\n' | |
301 // 0 : = (Padding character). | |
302 // >0 : Base 64 alphabet index of given byte. | |
303 static const List<int> _decodeTable = | |
304 const [ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -1, -2, -2, | |
305 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
306 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, 62, -2, 63, | |
307 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, 0, -2, -2, | |
308 -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | |
309 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, 63, | |
310 -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, | |
311 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2, | |
312 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
313 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
314 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
315 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
316 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
317 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
318 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
319 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 ]; | |
320 | |
321 static String bytesToBase64(List<int> bytes, | |
322 [bool urlSafe = false, | |
323 bool addLineSeparator = false]) { | |
324 int len = bytes.length; | |
325 if (len == 0) { | |
326 return ""; | |
327 } | |
328 final String lookup = urlSafe ? _encodeTableUrlSafe : _encodeTable; | |
329 // Size of 24 bit chunks. | |
330 final int remainderLength = len.remainder(3); | |
331 final int chunkLength = len - remainderLength; | |
332 // Size of base output. | |
333 int outputLen = ((len ~/ 3) * 4) + ((remainderLength > 0) ? 4 : 0); | |
334 // Add extra for line separators. | |
335 if (addLineSeparator) { | |
336 outputLen += ((outputLen - 1) ~/ LINE_LENGTH) << 1; | |
337 } | |
338 List<int> out = new List<int>(outputLen); | |
339 | |
340 // Encode 24 bit chunks. | |
341 int j = 0, i = 0, c = 0; | |
342 while (i < chunkLength) { | |
343 int x = ((bytes[i++] << 16) & 0xFFFFFF) | | |
344 ((bytes[i++] << 8) & 0xFFFFFF) | | |
345 bytes[i++]; | |
346 out[j++] = lookup.codeUnitAt(x >> 18); | |
347 out[j++] = lookup.codeUnitAt((x >> 12) & 0x3F); | |
348 out[j++] = lookup.codeUnitAt((x >> 6) & 0x3F); | |
349 out[j++] = lookup.codeUnitAt(x & 0x3f); | |
350 // Add optional line separator for each 76 char output. | |
351 if (addLineSeparator && ++c == 19 && j < outputLen - 2) { | |
352 out[j++] = CR; | |
353 out[j++] = LF; | |
354 c = 0; | |
355 } | |
356 } | |
357 | |
358 // If input length if not a multiple of 3, encode remaining bytes and | |
359 // add padding. | |
360 if (remainderLength == 1) { | |
361 int x = bytes[i]; | |
362 out[j++] = lookup.codeUnitAt(x >> 2); | |
363 out[j++] = lookup.codeUnitAt((x << 4) & 0x3F); | |
364 out[j++] = PAD; | |
365 out[j++] = PAD; | |
366 } else if (remainderLength == 2) { | |
367 int x = bytes[i]; | |
368 int y = bytes[i + 1]; | |
369 out[j++] = lookup.codeUnitAt(x >> 2); | |
370 out[j++] = lookup.codeUnitAt(((x << 4) | (y >> 4)) & 0x3F); | |
371 out[j++] = lookup.codeUnitAt((y << 2) & 0x3F); | |
372 out[j++] = PAD; | |
373 } | |
374 | |
375 return new String.fromCharCodes(out); | |
376 } | |
377 } | |
OLD | NEW |