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 |