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