| 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 |