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 part of dart.io; | |
6 | |
7 class _CryptoUtils { | |
8 static const int PAD = 61; // '=' | |
9 static const int CR = 13; // '\r' | |
10 static const int LF = 10; // '\n' | |
11 static const int LINE_LENGTH = 76; | |
12 | |
13 static const String _encodeTable = | |
14 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
15 | |
16 static const String _encodeTableUrlSafe = | |
17 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; | |
18 | |
19 // Lookup table used for finding Base 64 alphabet index of a given byte. | |
20 // -2 : Outside Base 64 alphabet. | |
21 // -1 : '\r' or '\n' | |
22 // 0 : = (Padding character). | |
23 // >0 : Base 64 alphabet index of given byte. | |
24 static const List<int> _decodeTable = | |
25 const [ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -1, -2, -2, | |
26 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
27 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, 62, -2, 63, | |
28 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, 0, -2, -2, | |
29 -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | |
30 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, 63, | |
31 -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, | |
32 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2, | |
33 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
34 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
35 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
36 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
37 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
38 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
39 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, | |
40 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 ]; | |
41 | |
42 static String bytesToHex(List<int> bytes) { | |
43 var result = new StringBuffer(); | |
44 for (var part in bytes) { | |
45 result.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}'); | |
46 } | |
47 return result.toString(); | |
48 } | |
49 | |
50 static String bytesToBase64(List<int> bytes, | |
51 [bool urlSafe = false, | |
52 bool addLineSeparator = false]) { | |
53 int len = bytes.length; | |
54 if (len == 0) { | |
55 return ""; | |
56 } | |
57 final String lookup = urlSafe ? _encodeTableUrlSafe : _encodeTable; | |
58 // Size of 24 bit chunks. | |
59 final int remainderLength = len.remainder(3); | |
60 final int chunkLength = len - remainderLength; | |
61 // Size of base output. | |
62 int outputLen = ((len ~/ 3) * 4) + ((remainderLength > 0) ? 4 : 0); | |
63 // Add extra for line separators. | |
64 if (addLineSeparator) { | |
65 outputLen += ((outputLen - 1) ~/ LINE_LENGTH) << 1; | |
66 } | |
67 List<int> out = new List<int>(outputLen); | |
68 | |
69 // Encode 24 bit chunks. | |
70 int j = 0, i = 0, c = 0; | |
71 while (i < chunkLength) { | |
72 int x = ((bytes[i++] << 16) & 0xFFFFFF) | | |
73 ((bytes[i++] << 8) & 0xFFFFFF) | | |
74 bytes[i++]; | |
75 out[j++] = lookup.codeUnitAt(x >> 18); | |
76 out[j++] = lookup.codeUnitAt((x >> 12) & 0x3F); | |
77 out[j++] = lookup.codeUnitAt((x >> 6) & 0x3F); | |
78 out[j++] = lookup.codeUnitAt(x & 0x3f); | |
79 // Add optional line separator for each 76 char output. | |
80 if (addLineSeparator && ++c == 19 && j < outputLen - 2) { | |
81 out[j++] = CR; | |
82 out[j++] = LF; | |
83 c = 0; | |
84 } | |
85 } | |
86 | |
87 // If input length if not a multiple of 3, encode remaining bytes and | |
88 // add padding. | |
89 if (remainderLength == 1) { | |
90 int x = bytes[i]; | |
91 out[j++] = lookup.codeUnitAt(x >> 2); | |
92 out[j++] = lookup.codeUnitAt((x << 4) & 0x3F); | |
93 out[j++] = PAD; | |
94 out[j++] = PAD; | |
95 } else if (remainderLength == 2) { | |
96 int x = bytes[i]; | |
97 int y = bytes[i + 1]; | |
98 out[j++] = lookup.codeUnitAt(x >> 2); | |
99 out[j++] = lookup.codeUnitAt(((x << 4) | (y >> 4)) & 0x3F); | |
100 out[j++] = lookup.codeUnitAt((y << 2) & 0x3F); | |
101 out[j++] = PAD; | |
102 } | |
103 | |
104 return new String.fromCharCodes(out); | |
105 } | |
106 | |
107 static List<int> base64StringToBytes(String input, | |
108 [bool ignoreInvalidCharacters = true]) { | |
109 int len = input.length; | |
110 if (len == 0) { | |
111 return new List<int>(0); | |
112 } | |
113 | |
114 // Count '\r', '\n' and illegal characters, For illegal characters, | |
115 // if [ignoreInvalidCharacters] is false, throw an exception. | |
116 int extrasLen = 0; | |
117 for (int i = 0; i < len; i++) { | |
118 int c = _decodeTable[input.codeUnitAt(i)]; | |
119 if (c < 0) { | |
120 extrasLen++; | |
121 if(c == -2 && !ignoreInvalidCharacters) { | |
122 throw new FormatException('Invalid character: ${input[i]}'); | |
123 } | |
124 } | |
125 } | |
126 | |
127 if ((len - extrasLen) % 4 != 0) { | |
128 throw new FormatException('''Size of Base 64 characters in Input | |
129 must be a multiple of 4. Input: $input'''); | |
130 } | |
131 | |
132 // Count pad characters, ignore illegal characters at the end. | |
133 int padLength = 0; | |
134 for (int i = len - 1; i >= 0; i--) { | |
135 int currentCodeUnit = input.codeUnitAt(i); | |
136 if (_decodeTable[currentCodeUnit] > 0) break; | |
137 if (currentCodeUnit == PAD) padLength++; | |
138 } | |
139 int outputLen = (((len - extrasLen) * 6) >> 3) - padLength; | |
140 List<int> out = new List<int>(outputLen); | |
141 | |
142 for (int i = 0, o = 0; o < outputLen;) { | |
143 // Accumulate 4 valid 6 bit Base 64 characters into an int. | |
144 int x = 0; | |
145 for (int j = 4; j > 0;) { | |
146 int c = _decodeTable[input.codeUnitAt(i++)]; | |
147 if (c >= 0) { | |
148 x = ((x << 6) & 0xFFFFFF) | c; | |
149 j--; | |
150 } | |
151 } | |
152 out[o++] = x >> 16; | |
153 if (o < outputLen) { | |
154 out[o++] = (x >> 8) & 0xFF; | |
155 if (o < outputLen) out[o++] = x & 0xFF; | |
156 } | |
157 } | |
158 return out; | |
159 } | |
160 | |
161 } | |
162 | |
163 // Constants. | |
164 const _MASK_8 = 0xff; | |
165 const _MASK_32 = 0xffffffff; | |
166 const _BITS_PER_BYTE = 8; | |
167 const _BYTES_PER_WORD = 4; | |
168 | |
169 // Base class encapsulating common behavior for cryptographic hash | |
170 // functions. | |
171 abstract class _HashBase { | |
172 // Hasher state. | |
173 final int _chunkSizeInWords; | |
174 final int _digestSizeInWords; | |
175 final bool _bigEndianWords; | |
176 int _lengthInBytes = 0; | |
177 List<int> _pendingData; | |
178 List<int> _currentChunk; | |
179 List<int> _h; | |
180 bool _digestCalled = false; | |
181 | |
182 _HashBase(this._chunkSizeInWords, | |
183 this._digestSizeInWords, | |
184 this._bigEndianWords) | |
185 : _pendingData = [] { | |
186 _currentChunk = new List(_chunkSizeInWords); | |
187 _h = new List(_digestSizeInWords); | |
188 } | |
189 | |
190 // Update the hasher with more data. | |
191 add(List<int> data) { | |
192 if (_digestCalled) { | |
193 throw new StateError( | |
194 'Hash update method called after digest was retrieved'); | |
195 } | |
196 _lengthInBytes += data.length; | |
197 _pendingData.addAll(data); | |
198 _iterate(); | |
199 } | |
200 | |
201 // Finish the hash computation and return the digest string. | |
202 List<int> close() { | |
203 if (_digestCalled) { | |
204 return _resultAsBytes(); | |
205 } | |
206 _digestCalled = true; | |
207 _finalizeData(); | |
208 _iterate(); | |
209 assert(_pendingData.length == 0); | |
210 return _resultAsBytes(); | |
211 } | |
212 | |
213 // Returns the block size of the hash in bytes. | |
214 int get blockSize { | |
215 return _chunkSizeInWords * _BYTES_PER_WORD; | |
216 } | |
217 | |
218 // Create a fresh instance of this Hash. | |
219 newInstance(); | |
220 | |
221 // One round of the hash computation. | |
222 _updateHash(List<int> m); | |
223 | |
224 // Helper methods. | |
225 _add32(x, y) => (x + y) & _MASK_32; | |
226 _roundUp(val, n) => (val + n - 1) & -n; | |
227 | |
228 // Rotate left limiting to unsigned 32-bit values. | |
229 int _rotl32(int val, int shift) { | |
230 var mod_shift = shift & 31; | |
231 return ((val << mod_shift) & _MASK_32) | | |
232 ((val & _MASK_32) >> (32 - mod_shift)); | |
233 } | |
234 | |
235 | |
236 // Compute the final result as a list of bytes from the hash words. | |
237 _resultAsBytes() { | |
238 var result = []; | |
239 for (var i = 0; i < _h.length; i++) { | |
240 result.addAll(_wordToBytes(_h[i])); | |
241 } | |
242 return result; | |
243 } | |
244 | |
245 // Converts a list of bytes to a chunk of 32-bit words. | |
246 _bytesToChunk(List<int> data, int dataIndex) { | |
247 assert((data.length - dataIndex) >= (_chunkSizeInWords * _BYTES_PER_WORD)); | |
248 | |
249 for (var wordIndex = 0; wordIndex < _chunkSizeInWords; wordIndex++) { | |
250 var w3 = _bigEndianWords ? data[dataIndex] : data[dataIndex + 3]; | |
251 var w2 = _bigEndianWords ? data[dataIndex + 1] : data[dataIndex + 2]; | |
252 var w1 = _bigEndianWords ? data[dataIndex + 2] : data[dataIndex + 1]; | |
253 var w0 = _bigEndianWords ? data[dataIndex + 3] : data[dataIndex]; | |
254 dataIndex += 4; | |
255 var word = (w3 & 0xff) << 24; | |
256 word |= (w2 & _MASK_8) << 16; | |
257 word |= (w1 & _MASK_8) << 8; | |
258 word |= (w0 & _MASK_8); | |
259 _currentChunk[wordIndex] = word; | |
260 } | |
261 } | |
262 | |
263 // Convert a 32-bit word to four bytes. | |
264 _wordToBytes(int word) { | |
265 List<int> bytes = new List(_BYTES_PER_WORD); | |
266 bytes[0] = (word >> (_bigEndianWords ? 24 : 0)) & _MASK_8; | |
267 bytes[1] = (word >> (_bigEndianWords ? 16 : 8)) & _MASK_8; | |
268 bytes[2] = (word >> (_bigEndianWords ? 8 : 16)) & _MASK_8; | |
269 bytes[3] = (word >> (_bigEndianWords ? 0 : 24)) & _MASK_8; | |
270 return bytes; | |
271 } | |
272 | |
273 // Iterate through data updating the hash computation for each | |
274 // chunk. | |
275 _iterate() { | |
276 var len = _pendingData.length; | |
277 var chunkSizeInBytes = _chunkSizeInWords * _BYTES_PER_WORD; | |
278 if (len >= chunkSizeInBytes) { | |
279 var index = 0; | |
280 for (; (len - index) >= chunkSizeInBytes; index += chunkSizeInBytes) { | |
281 _bytesToChunk(_pendingData, index); | |
282 _updateHash(_currentChunk); | |
283 } | |
284 _pendingData = _pendingData.sublist(index, len); | |
285 } | |
286 } | |
287 | |
288 // Finalize the data. Add a 1 bit to the end of the message. Expand with | |
289 // 0 bits and add the length of the message. | |
290 _finalizeData() { | |
291 _pendingData.add(0x80); | |
292 var contentsLength = _lengthInBytes + 9; | |
293 var chunkSizeInBytes = _chunkSizeInWords * _BYTES_PER_WORD; | |
294 var finalizedLength = _roundUp(contentsLength, chunkSizeInBytes); | |
295 var zeroPadding = finalizedLength - contentsLength; | |
296 for (var i = 0; i < zeroPadding; i++) { | |
297 _pendingData.add(0); | |
298 } | |
299 var lengthInBits = _lengthInBytes * _BITS_PER_BYTE; | |
300 assert(lengthInBits < pow(2, 32)); | |
301 if (_bigEndianWords) { | |
302 _pendingData.addAll(_wordToBytes(0)); | |
303 _pendingData.addAll(_wordToBytes(lengthInBits & _MASK_32)); | |
304 } else { | |
305 _pendingData.addAll(_wordToBytes(lengthInBits & _MASK_32)); | |
306 _pendingData.addAll(_wordToBytes(0)); | |
307 } | |
308 } | |
309 } | |
310 | |
311 // The MD5 hasher is used to compute an MD5 message digest. | |
312 class _MD5 extends _HashBase { | |
313 _MD5() : super(16, 4, false) { | |
314 _h[0] = 0x67452301; | |
315 _h[1] = 0xefcdab89; | |
316 _h[2] = 0x98badcfe; | |
317 _h[3] = 0x10325476; | |
318 } | |
319 | |
320 // Returns a new instance of this Hash. | |
321 _MD5 newInstance() { | |
322 return new _MD5(); | |
323 } | |
324 | |
325 static const _k = const [ | |
326 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, | |
327 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, | |
328 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, | |
329 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, | |
330 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, | |
331 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, | |
332 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, | |
333 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, | |
334 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, | |
335 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, | |
336 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 ]; | |
337 | |
338 static const _r = const [ | |
339 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, | |
340 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, | |
341 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, | |
342 10, 15, 21, 6, 10, 15, 21 ]; | |
343 | |
344 // Compute one iteration of the MD5 algorithm with a chunk of | |
345 // 16 32-bit pieces. | |
346 void _updateHash(List<int> m) { | |
347 assert(m.length == 16); | |
348 | |
349 var a = _h[0]; | |
350 var b = _h[1]; | |
351 var c = _h[2]; | |
352 var d = _h[3]; | |
353 | |
354 var t0; | |
355 var t1; | |
356 | |
357 for (var i = 0; i < 64; i++) { | |
358 if (i < 16) { | |
359 t0 = (b & c) | ((~b & _MASK_32) & d); | |
360 t1 = i; | |
361 } else if (i < 32) { | |
362 t0 = (d & b) | ((~d & _MASK_32) & c); | |
363 t1 = ((5 * i) + 1) % 16; | |
364 } else if (i < 48) { | |
365 t0 = b ^ c ^ d; | |
366 t1 = ((3 * i) + 5) % 16; | |
367 } else { | |
368 t0 = c ^ (b | (~d & _MASK_32)); | |
369 t1 = (7 * i) % 16; | |
370 } | |
371 | |
372 var temp = d; | |
373 d = c; | |
374 c = b; | |
375 b = _add32(b, _rotl32(_add32(_add32(a, t0), | |
376 _add32(_k[i], m[t1])), | |
377 _r[i])); | |
378 a = temp; | |
379 } | |
380 | |
381 _h[0] = _add32(a, _h[0]); | |
382 _h[1] = _add32(b, _h[1]); | |
383 _h[2] = _add32(c, _h[2]); | |
384 _h[3] = _add32(d, _h[3]); | |
385 } | |
386 } | |
387 | |
388 // The SHA1 hasher is used to compute an SHA1 message digest. | |
389 class _SHA1 extends _HashBase { | |
390 // Construct a SHA1 hasher object. | |
391 _SHA1() : _w = new List(80), super(16, 5, true) { | |
392 _h[0] = 0x67452301; | |
393 _h[1] = 0xEFCDAB89; | |
394 _h[2] = 0x98BADCFE; | |
395 _h[3] = 0x10325476; | |
396 _h[4] = 0xC3D2E1F0; | |
397 } | |
398 | |
399 // Returns a new instance of this Hash. | |
400 _SHA1 newInstance() { | |
401 return new _SHA1(); | |
402 } | |
403 | |
404 // Compute one iteration of the SHA1 algorithm with a chunk of | |
405 // 16 32-bit pieces. | |
406 void _updateHash(List<int> m) { | |
407 assert(m.length == 16); | |
408 | |
409 var a = _h[0]; | |
410 var b = _h[1]; | |
411 var c = _h[2]; | |
412 var d = _h[3]; | |
413 var e = _h[4]; | |
414 | |
415 for (var i = 0; i < 80; i++) { | |
416 if (i < 16) { | |
417 _w[i] = m[i]; | |
418 } else { | |
419 var n = _w[i - 3] ^ _w[i - 8] ^ _w[i - 14] ^ _w[i - 16]; | |
420 _w[i] = _rotl32(n, 1); | |
421 } | |
422 var t = _add32(_add32(_rotl32(a, 5), e), _w[i]); | |
423 if (i < 20) { | |
424 t = _add32(_add32(t, (b & c) | (~b & d)), 0x5A827999); | |
425 } else if (i < 40) { | |
426 t = _add32(_add32(t, (b ^ c ^ d)), 0x6ED9EBA1); | |
427 } else if (i < 60) { | |
428 t = _add32(_add32(t, (b & c) | (b & d) | (c & d)), 0x8F1BBCDC); | |
429 } else { | |
430 t = _add32(_add32(t, b ^ c ^ d), 0xCA62C1D6); | |
431 } | |
432 | |
433 e = d; | |
434 d = c; | |
435 c = _rotl32(b, 30); | |
436 b = a; | |
437 a = t & _MASK_32; | |
438 } | |
439 | |
440 _h[0] = _add32(a, _h[0]); | |
441 _h[1] = _add32(b, _h[1]); | |
442 _h[2] = _add32(c, _h[2]); | |
443 _h[3] = _add32(d, _h[3]); | |
444 _h[4] = _add32(e, _h[4]); | |
445 } | |
446 | |
447 List<int> _w; | |
448 } | |
OLD | NEW |