Added initial files.
Jeremy D

Jeremy D commited on 2021-10-22 16:24:31
Showing 8 changed files, with 669 additions and 0 deletions.

... ...
@@ -0,0 +1,11 @@
1
+# Force our line endings to be LF, even for Windows
2
+* text=auto
3
+
4
+# Set certain files to be binary
5
+*.png binary
6
+*.jpg binary
7
+*.gif binary
8
+*.tgz binary
9
+*.zip binary
10
+*.tar.gz binary
11
+*.ttf binary
... ...
@@ -0,0 +1,211 @@
1
+<?php
2
+/**
3
+ * The Main file for Hibp
4
+ * @package Hibp
5
+ * @author SleePy <sleepy @ simplemachines (dot) org>
6
+ * @copyright 2021
7
+ * @license 3-Clause BSD https://opensource.org/licenses/BSD-3-Clause
8
+ * @version 1.0
9
+ */
10
+
11
+/**
12
+ * Checks the password against the Have-I-Been-Pwned database.
13
+ *
14
+ * @param string $password The password to check
15
+ * @param bool $hashed If the password is hashed already or not.
16
+ * @return bool The result if found or not.  If null, the check failed.
17
+ */
18
+function hibp_password(string $password, bool $hashed = false): ?bool
19
+{
20
+	global $smcFunc;
21
+
22
+	if (!$hashed)
23
+		$password = sha1($password);
24
+	
25
+	$passhash_prefix = $smcFunc['substr']($password, 0, 5);
26
+	$passhash_suffix = $smcFunc['substr']($password, 5);
27
+
28
+	$call_url = 'https://api.pwnedpasswords.com/range/' . $passhash_prefix;
29
+
30
+	/*
31
+	* SMF's fetch_web_data doesn't support sending headers, so we are limited in our API options
32
+	* and could build our own to do this, but this works fine.
33
+	*/
34
+	$results = fetch_web_data($call_url);
35
+
36
+	// Invalid results, just pass them through.
37
+	if (empty($results))
38
+		return null;
39
+
40
+	// Sure we could make an array of the data, but we just want to see if its found.
41
+	$found = preg_match(
42
+		'~\s+' . preg_quote($passhash_suffix) . ':\d+~i',
43
+		$results
44
+		);
45
+
46
+	// We found a result, its found.
47
+	if ($found === 1)
48
+		return true;
49
+	// No result, return false.
50
+	elseif ($found === 0)
51
+		return false;
52
+
53
+	// $found returned something invalid, also fail.
54
+	return null;
55
+}
56
+
57
+/**
58
+ * Checks the password against the HiBP database.
59
+ *
60
+ * @calledby call_integration_hook('integrate_validatePassword', array($password, $username, $restrict_in, &$reg_error));
61
+ * @param string $password The password to check
62
+ * @param string $username Currently ignored by this hook.
63
+ * @param array $restrict_in Currently ignored by this hook.
64
+ * @param string $pass_error A password error if any.  If this is set, we won't process our hook.
65
+ * @return void
66
+ */
67
+function hibp_validatePassword(string $password, string $username, array $restrict_in, string &$pass_error): void
68
+{
69
+	global $modSettings;
70
+
71
+	// If another hook has set this, leave it alone.
72
+	if (!empty($pass_error) || empty($modSettings['enableHibP']))
73
+		return;
74
+
75
+	// Send it to the backend.
76
+	$res = hibp_password($password);
77
+
78
+	// If the result is true, we want to present a error to the prefix of $txt['profile_error_password_*']
79
+	if ($res === true)
80
+	{
81
+		loadLanguage('Hibp');
82
+		$pass_error = 'hibp';
83
+	}
84
+}
85
+
86
+/**
87
+ * When the password field is setup on the registration, send in some javascript.
88
+ *
89
+ * @calledby call_integration_hook('integrate_load_custom_profile_fields', array($memID, $area));
90
+ * @param array $fields User profile fields we are loading.
91
+ * @return void
92
+ */
93
+function hibp_load_custom_profile_fields(int $memID, string $area): void
94
+{
95
+	global $modSettings;
96
+
97
+	if ($area !== 'register' || empty($modSettings['enableHibPjs']))
98
+		return;
99
+
100
+	// <input type="password" name="passwrd1" id="smf_autov_pwmain" size="50" tabindex="3" class=" invalid_input">
101
+	hibp_build_javascript('#smf_autov_pwmain', '#smf_autov_pwmain_div');
102
+}
103
+	
104
+
105
+/**
106
+ * When the password field is setup on the profile pages, send in some javascript.
107
+ *
108
+ * @calledby call_integration_hook('integrate_setup_profile_context', array(&$fields));
109
+ * @param array $fields User profile fields we are loading.
110
+ * @return void
111
+ */
112
+function hibp_setup_profile_context(array $fields): void
113
+{
114
+	global $modSettings;
115
+
116
+	// If we are not loading the password field, don't bother.
117
+	if (!in_array('passwrd1', $fields) || empty($modSettings['enableHibPjs']))
118
+		return;
119
+
120
+	// <input type="password" name="passwrd1" id="passwrd1" size="20" value="">
121
+	hibp_build_javascript('#passwrd1', '#passwrd1');
122
+}
123
+
124
+function hibp_build_javascript(string $selector, string $errorSelector): void
125
+{
126
+	global $txt;
127
+
128
+	loadLanguage('Hibp');
129
+
130
+	// Add the JS.  From: https://github.com/emn178/js-sha1
131
+	loadJavaScriptFile('sha1.js');
132
+
133
+	/*
134
+	 *	When the password is ok we use: <span class="main_icons valid"></span>
135
+	 *	When the password is bad we use: <span class="main_icons error"></span>
136
+	*/
137
+	addInlineJavaScript('
138
+		$(document).ready(function () {
139
+			$(' . JavaScriptEscape($selector) . ').on("change", function (e){
140
+				var $hibp_attachSelector = ' . JavaScriptEscape($errorSelector) . '
141
+				var $passhash = sha1($(this).val());
142
+				var $passhash_prefix = $passhash.substring(0, 5);
143
+				var $passhash_suffix = $passhash.substring(5);
144
+				var $passhash_regx = new RegExp("\\\\s+" + $passhash_suffix + ":\\\\d+", "i");
145
+				 
146
+				$.ajax({
147
+					url: "https://api.pwnedpasswords.com/range/" + $passhash_prefix,
148
+					type: "GET",
149
+					dataType: "html",
150
+					success: function (data, textStatus, xhr) {
151
+						var $res = $passhash_regx.test(data);
152
+						
153
+						// Build the box.
154
+						if (typeof $hibpBox === "undefined")
155
+							$hibpBox = $($hibp_attachSelector).parent().append(' . JavaScriptEscape('<div class="errorbox pagesection" style="width: 78%;">' . $txt['profile_error_password_hibp']. '</div>') . ');
156
+						
157
+						// It was found.
158
+						if ($res === true)
159
+							$($hibpBox).find("div.errorbox").show();
160
+						else if ($res === false)
161
+							$($hibpBox).find("div.errorbox").hide();
162
+					}				
163
+				});
164
+			});
165
+		});
166
+	');
167
+	
168
+	/*
169
+	<div class="errorbox pagesection" style="width: 78%;">test</div>
170
+	*/
171
+}
172
+
173
+/**
174
+ * Adds Hibp options to the mangage registration.
175
+ * General registration settings and Coppa compliance settings.
176
+ * Accessed by ?action=admin;area=regcenter;sa=settings.
177
+ * Requires the admin_forum permission.
178
+ *
179
+ * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
180
+ * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
181
+ */
182
+function hibp_general_security_settings(array &$config_vars): void
183
+{
184
+	global $txt;
185
+
186
+	loadLanguage('Hibp');
187
+
188
+	// Find the last password setting.
189
+	foreach ($config_vars as $id => $val)
190
+		if (is_array($val) && $val[1] == 'enable_password_conversion' && is_string($config_vars[$id + 1]) && $config_vars[$id + 1] == '')
191
+			break;
192
+
193
+	$varsA = array_slice($config_vars, 0, $id + 1);
194
+	$varsB = array_slice($config_vars, $id + 1);
195
+
196
+	$new_vars = array(
197
+		'',
198
+		array('check', 'enableHibP'),
199
+		array('check', 'enableHibPjs'),
200
+	);
201
+
202
+	$config_vars = array_merge($varsA, $new_vars, $varsB);
203
+
204
+	// Saving?
205
+	if (isset($_GET['save']))
206
+	{
207
+		// Can't have one without the other.
208
+		if (!empty($_POST['enableHibPjs']) && empty($_POST['enableHibP']))
209
+			$_POST['enableHibP'] = $_POST['enableHibPjs'];
210
+	}
211
+}
0 212
\ No newline at end of file
... ...
@@ -0,0 +1,29 @@
1
+BSD 3-Clause License
2
+
3
+Copyright (c) 2021, SleePy
4
+All rights reserved.
5
+
6
+Redistribution and use in source and binary forms, with or without
7
+modification, are permitted provided that the following conditions are met:
8
+
9
+1. Redistributions of source code must retain the above copyright notice, this
10
+   list of conditions and the following disclaimer.
11
+
12
+2. Redistributions in binary form must reproduce the above copyright notice,
13
+   this list of conditions and the following disclaimer in the documentation
14
+   and/or other materials provided with the distribution.
15
+
16
+3. Neither the name of the copyright holder nor the names of its
17
+   contributors may be used to endorse or promote products derived from
18
+   this software without specific prior written permission.
19
+
20
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
... ...
@@ -0,0 +1,5 @@
1
+This enables checking passwords against the [Have-I-Been-Pwned](https://haveibeenpwned.com/Passwords) database.  Passwords are only checked on registration and when changed on the profile.
2
+
3
+Additionally this can attempt to check the password from the browser using the same API
4
+
5
+SMF 2.1.0 or higher only!
0 6
\ No newline at end of file
... ...
@@ -0,0 +1,6 @@
1
+<?php
2
+global $txt;
3
+
4
+$txt['profile_error_password_hibp'] = 'Your password has been found in <a href="https://haveibeenpwned.com/Passwords" target="_blank" rel="nofollow">the have i been pwned</a> database.';
5
+$txt['enableHibP'] = 'Enable Server based Have-I-Been-Pwned Checks';
6
+$txt['enableHibPjs'] = 'Enable Client based Have-I-Been-Pwned Checks';
... ...
@@ -0,0 +1,36 @@
1
+<?xml version="1.0"?>
2
+<!DOCTYPE package-info SYSTEM "http://www.simplemachines.org/xml/package-info">
3
+<package-info xmlns="http://www.simplemachines.org/xml/package-info" xmlns:smf="http://www.simplemachines.org/">
4
+	<id>SleePy:HibP</id>
5
+	<name>Have-I-Been-Pwned</name>
6
+	<version>1.0</version>
7
+	<type>modification</type>
8
+
9
+	<install for="2.1.*">
10
+		<require-file name="language/Hibp.english.php" destination="$themes_dir/default/languages" />
11
+		<require-file name="Hibp.php" destination="$sourcedir" />
12
+
13
+		<!-- All the hooks -->
14
+		<hook hook="integrate_validatePassword" function="hibp_validatePassword" file="$sourcedir/Hibp.php" />
15
+		<hook hook="integrate_setup_profile_context" function="hibp_setup_profile_context" file="$sourcedir/Hibp.php" />
16
+		<hook hook="integrate_load_custom_profile_fields" function="hibp_validatePassword" file="$sourcedir/Hibp.php" />
17
+		<hook hook="integrate_general_security_settings" function="hibp_general_security_settings" file="$sourcedir/Hibp.php" />
18
+
19
+		<redirect url="$scripturl?action=admin;area=serversettings;sa=security;$session_var=$session_id" />
20
+	</install>
21
+
22
+	<uninstall for="2.1.*">
23
+		<!-- All the hooks, removed -->
24
+		<hook reverse="true" hook="integrate_validatePassword" function="hibp_validatePassword" file="$sourcedir/Hibp.php" />
25
+		<hook reverse="true" hook="integrate_setup_profile_context" function="hibp_setup_profile_context" file="$sourcedir/Hibp.php" />
26
+		<hook reverse="true" hook="integrate_load_custom_profile_fields" function="hibp_validatePassword" file="$sourcedir/Hibp.php" />
27
+		<hook reverse="true" hook="integrate_general_security_settings" function="hibp_general_security_settings" file="$sourcedir/Hibp.php" />
28
+
29
+		<!-- language files, removed -->
30
+		<remove-file name="$themes_dir/default/languages/Hibp.english.php" />
31
+
32
+		<!-- source files, removed -->
33
+		<remove-file name="$sourcedir/Hibp.php" />
34
+	</uninstall>
35
+
36
+</package-info>
0 37
\ No newline at end of file
... ...
@@ -0,0 +1,371 @@
1
+/*
2
+ * [js-sha1]{@link https://github.com/emn178/js-sha1}
3
+ *
4
+ * @version 0.6.0
5
+ * @author Chen, Yi-Cyuan [emn178@gmail.com]
6
+ * @copyright Chen, Yi-Cyuan 2014-2017
7
+ * @license MIT
8
+ */
9
+/*jslint bitwise: true */
10
+(function() {
11
+  'use strict';
12
+
13
+  var root = typeof window === 'object' ? window : {};
14
+  var NODE_JS = !root.JS_SHA1_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
15
+  if (NODE_JS) {
16
+    root = global;
17
+  }
18
+  var COMMON_JS = !root.JS_SHA1_NO_COMMON_JS && typeof module === 'object' && module.exports;
19
+  var AMD = typeof define === 'function' && define.amd;
20
+  var HEX_CHARS = '0123456789abcdef'.split('');
21
+  var EXTRA = [-2147483648, 8388608, 32768, 128];
22
+  var SHIFT = [24, 16, 8, 0];
23
+  var OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer'];
24
+
25
+  var blocks = [];
26
+
27
+  var createOutputMethod = function (outputType) {
28
+    return function (message) {
29
+      return new Sha1(true).update(message)[outputType]();
30
+    };
31
+  };
32
+
33
+  var createMethod = function () {
34
+    var method = createOutputMethod('hex');
35
+    if (NODE_JS) {
36
+      method = nodeWrap(method);
37
+    }
38
+    method.create = function () {
39
+      return new Sha1();
40
+    };
41
+    method.update = function (message) {
42
+      return method.create().update(message);
43
+    };
44
+    for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
45
+      var type = OUTPUT_TYPES[i];
46
+      method[type] = createOutputMethod(type);
47
+    }
48
+    return method;
49
+  };
50
+
51
+  var nodeWrap = function (method) {
52
+    var crypto = eval("require('crypto')");
53
+    var Buffer = eval("require('buffer').Buffer");
54
+    var nodeMethod = function (message) {
55
+      if (typeof message === 'string') {
56
+        return crypto.createHash('sha1').update(message, 'utf8').digest('hex');
57
+      } else if (message.constructor === ArrayBuffer) {
58
+        message = new Uint8Array(message);
59
+      } else if (message.length === undefined) {
60
+        return method(message);
61
+      }
62
+      return crypto.createHash('sha1').update(new Buffer(message)).digest('hex');
63
+    };
64
+    return nodeMethod;
65
+  };
66
+
67
+  function Sha1(sharedMemory) {
68
+    if (sharedMemory) {
69
+      blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] =
70
+      blocks[4] = blocks[5] = blocks[6] = blocks[7] =
71
+      blocks[8] = blocks[9] = blocks[10] = blocks[11] =
72
+      blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
73
+      this.blocks = blocks;
74
+    } else {
75
+      this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
76
+    }
77
+
78
+    this.h0 = 0x67452301;
79
+    this.h1 = 0xEFCDAB89;
80
+    this.h2 = 0x98BADCFE;
81
+    this.h3 = 0x10325476;
82
+    this.h4 = 0xC3D2E1F0;
83
+
84
+    this.block = this.start = this.bytes = this.hBytes = 0;
85
+    this.finalized = this.hashed = false;
86
+    this.first = true;
87
+  }
88
+
89
+  Sha1.prototype.update = function (message) {
90
+    if (this.finalized) {
91
+      return;
92
+    }
93
+    var notString = typeof(message) !== 'string';
94
+    if (notString && message.constructor === root.ArrayBuffer) {
95
+      message = new Uint8Array(message);
96
+    }
97
+    var code, index = 0, i, length = message.length || 0, blocks = this.blocks;
98
+
99
+    while (index < length) {
100
+      if (this.hashed) {
101
+        this.hashed = false;
102
+        blocks[0] = this.block;
103
+        blocks[16] = blocks[1] = blocks[2] = blocks[3] =
104
+        blocks[4] = blocks[5] = blocks[6] = blocks[7] =
105
+        blocks[8] = blocks[9] = blocks[10] = blocks[11] =
106
+        blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
107
+      }
108
+
109
+      if(notString) {
110
+        for (i = this.start; index < length && i < 64; ++index) {
111
+          blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
112
+        }
113
+      } else {
114
+        for (i = this.start; index < length && i < 64; ++index) {
115
+          code = message.charCodeAt(index);
116
+          if (code < 0x80) {
117
+            blocks[i >> 2] |= code << SHIFT[i++ & 3];
118
+          } else if (code < 0x800) {
119
+            blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
120
+            blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
121
+          } else if (code < 0xd800 || code >= 0xe000) {
122
+            blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
123
+            blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
124
+            blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
125
+          } else {
126
+            code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
127
+            blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
128
+            blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
129
+            blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
130
+            blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
131
+          }
132
+        }
133
+      }
134
+
135
+      this.lastByteIndex = i;
136
+      this.bytes += i - this.start;
137
+      if (i >= 64) {
138
+        this.block = blocks[16];
139
+        this.start = i - 64;
140
+        this.hash();
141
+        this.hashed = true;
142
+      } else {
143
+        this.start = i;
144
+      }
145
+    }
146
+    if (this.bytes > 4294967295) {
147
+      this.hBytes += this.bytes / 4294967296 << 0;
148
+      this.bytes = this.bytes % 4294967296;
149
+    }
150
+    return this;
151
+  };
152
+
153
+  Sha1.prototype.finalize = function () {
154
+    if (this.finalized) {
155
+      return;
156
+    }
157
+    this.finalized = true;
158
+    var blocks = this.blocks, i = this.lastByteIndex;
159
+    blocks[16] = this.block;
160
+    blocks[i >> 2] |= EXTRA[i & 3];
161
+    this.block = blocks[16];
162
+    if (i >= 56) {
163
+      if (!this.hashed) {
164
+        this.hash();
165
+      }
166
+      blocks[0] = this.block;
167
+      blocks[16] = blocks[1] = blocks[2] = blocks[3] =
168
+      blocks[4] = blocks[5] = blocks[6] = blocks[7] =
169
+      blocks[8] = blocks[9] = blocks[10] = blocks[11] =
170
+      blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
171
+    }
172
+    blocks[14] = this.hBytes << 3 | this.bytes >>> 29;
173
+    blocks[15] = this.bytes << 3;
174
+    this.hash();
175
+  };
176
+
177
+  Sha1.prototype.hash = function () {
178
+    var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4;
179
+    var f, j, t, blocks = this.blocks;
180
+
181
+    for(j = 16; j < 80; ++j) {
182
+      t = blocks[j - 3] ^ blocks[j - 8] ^ blocks[j - 14] ^ blocks[j - 16];
183
+      blocks[j] =  (t << 1) | (t >>> 31);
184
+    }
185
+
186
+    for(j = 0; j < 20; j += 5) {
187
+      f = (b & c) | ((~b) & d);
188
+      t = (a << 5) | (a >>> 27);
189
+      e = t + f + e + 1518500249 + blocks[j] << 0;
190
+      b = (b << 30) | (b >>> 2);
191
+
192
+      f = (a & b) | ((~a) & c);
193
+      t = (e << 5) | (e >>> 27);
194
+      d = t + f + d + 1518500249 + blocks[j + 1] << 0;
195
+      a = (a << 30) | (a >>> 2);
196
+
197
+      f = (e & a) | ((~e) & b);
198
+      t = (d << 5) | (d >>> 27);
199
+      c = t + f + c + 1518500249 + blocks[j + 2] << 0;
200
+      e = (e << 30) | (e >>> 2);
201
+
202
+      f = (d & e) | ((~d) & a);
203
+      t = (c << 5) | (c >>> 27);
204
+      b = t + f + b + 1518500249 + blocks[j + 3] << 0;
205
+      d = (d << 30) | (d >>> 2);
206
+
207
+      f = (c & d) | ((~c) & e);
208
+      t = (b << 5) | (b >>> 27);
209
+      a = t + f + a + 1518500249 + blocks[j + 4] << 0;
210
+      c = (c << 30) | (c >>> 2);
211
+    }
212
+
213
+    for(; j < 40; j += 5) {
214
+      f = b ^ c ^ d;
215
+      t = (a << 5) | (a >>> 27);
216
+      e = t + f + e + 1859775393 + blocks[j] << 0;
217
+      b = (b << 30) | (b >>> 2);
218
+
219
+      f = a ^ b ^ c;
220
+      t = (e << 5) | (e >>> 27);
221
+      d = t + f + d + 1859775393 + blocks[j + 1] << 0;
222
+      a = (a << 30) | (a >>> 2);
223
+
224
+      f = e ^ a ^ b;
225
+      t = (d << 5) | (d >>> 27);
226
+      c = t + f + c + 1859775393 + blocks[j + 2] << 0;
227
+      e = (e << 30) | (e >>> 2);
228
+
229
+      f = d ^ e ^ a;
230
+      t = (c << 5) | (c >>> 27);
231
+      b = t + f + b + 1859775393 + blocks[j + 3] << 0;
232
+      d = (d << 30) | (d >>> 2);
233
+
234
+      f = c ^ d ^ e;
235
+      t = (b << 5) | (b >>> 27);
236
+      a = t + f + a + 1859775393 + blocks[j + 4] << 0;
237
+      c = (c << 30) | (c >>> 2);
238
+    }
239
+
240
+    for(; j < 60; j += 5) {
241
+      f = (b & c) | (b & d) | (c & d);
242
+      t = (a << 5) | (a >>> 27);
243
+      e = t + f + e - 1894007588 + blocks[j] << 0;
244
+      b = (b << 30) | (b >>> 2);
245
+
246
+      f = (a & b) | (a & c) | (b & c);
247
+      t = (e << 5) | (e >>> 27);
248
+      d = t + f + d - 1894007588 + blocks[j + 1] << 0;
249
+      a = (a << 30) | (a >>> 2);
250
+
251
+      f = (e & a) | (e & b) | (a & b);
252
+      t = (d << 5) | (d >>> 27);
253
+      c = t + f + c - 1894007588 + blocks[j + 2] << 0;
254
+      e = (e << 30) | (e >>> 2);
255
+
256
+      f = (d & e) | (d & a) | (e & a);
257
+      t = (c << 5) | (c >>> 27);
258
+      b = t + f + b - 1894007588 + blocks[j + 3] << 0;
259
+      d = (d << 30) | (d >>> 2);
260
+
261
+      f = (c & d) | (c & e) | (d & e);
262
+      t = (b << 5) | (b >>> 27);
263
+      a = t + f + a - 1894007588 + blocks[j + 4] << 0;
264
+      c = (c << 30) | (c >>> 2);
265
+    }
266
+
267
+    for(; j < 80; j += 5) {
268
+      f = b ^ c ^ d;
269
+      t = (a << 5) | (a >>> 27);
270
+      e = t + f + e - 899497514 + blocks[j] << 0;
271
+      b = (b << 30) | (b >>> 2);
272
+
273
+      f = a ^ b ^ c;
274
+      t = (e << 5) | (e >>> 27);
275
+      d = t + f + d - 899497514 + blocks[j + 1] << 0;
276
+      a = (a << 30) | (a >>> 2);
277
+
278
+      f = e ^ a ^ b;
279
+      t = (d << 5) | (d >>> 27);
280
+      c = t + f + c - 899497514 + blocks[j + 2] << 0;
281
+      e = (e << 30) | (e >>> 2);
282
+
283
+      f = d ^ e ^ a;
284
+      t = (c << 5) | (c >>> 27);
285
+      b = t + f + b - 899497514 + blocks[j + 3] << 0;
286
+      d = (d << 30) | (d >>> 2);
287
+
288
+      f = c ^ d ^ e;
289
+      t = (b << 5) | (b >>> 27);
290
+      a = t + f + a - 899497514 + blocks[j + 4] << 0;
291
+      c = (c << 30) | (c >>> 2);
292
+    }
293
+
294
+    this.h0 = this.h0 + a << 0;
295
+    this.h1 = this.h1 + b << 0;
296
+    this.h2 = this.h2 + c << 0;
297
+    this.h3 = this.h3 + d << 0;
298
+    this.h4 = this.h4 + e << 0;
299
+  };
300
+
301
+  Sha1.prototype.hex = function () {
302
+    this.finalize();
303
+
304
+    var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4;
305
+
306
+    return HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] +
307
+           HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] +
308
+           HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] +
309
+           HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] +
310
+           HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] +
311
+           HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] +
312
+           HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] +
313
+           HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] +
314
+           HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] +
315
+           HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] +
316
+           HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] +
317
+           HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] +
318
+           HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F] +
319
+           HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] +
320
+           HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] +
321
+           HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] +
322
+           HEX_CHARS[(h4 >> 28) & 0x0F] + HEX_CHARS[(h4 >> 24) & 0x0F] +
323
+           HEX_CHARS[(h4 >> 20) & 0x0F] + HEX_CHARS[(h4 >> 16) & 0x0F] +
324
+           HEX_CHARS[(h4 >> 12) & 0x0F] + HEX_CHARS[(h4 >> 8) & 0x0F] +
325
+           HEX_CHARS[(h4 >> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F];
326
+  };
327
+
328
+  Sha1.prototype.toString = Sha1.prototype.hex;
329
+
330
+  Sha1.prototype.digest = function () {
331
+    this.finalize();
332
+
333
+    var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4;
334
+
335
+    return [
336
+      (h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, h0 & 0xFF,
337
+      (h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, h1 & 0xFF,
338
+      (h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, h2 & 0xFF,
339
+      (h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, h3 & 0xFF,
340
+      (h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, h4 & 0xFF
341
+    ];
342
+  };
343
+
344
+  Sha1.prototype.array = Sha1.prototype.digest;
345
+
346
+  Sha1.prototype.arrayBuffer = function () {
347
+    this.finalize();
348
+
349
+    var buffer = new ArrayBuffer(20);
350
+    var dataView = new DataView(buffer);
351
+    dataView.setUint32(0, this.h0);
352
+    dataView.setUint32(4, this.h1);
353
+    dataView.setUint32(8, this.h2);
354
+    dataView.setUint32(12, this.h3);
355
+    dataView.setUint32(16, this.h4);
356
+    return buffer;
357
+  };
358
+
359
+  var exports = createMethod();
360
+
361
+  if (COMMON_JS) {
362
+    module.exports = exports;
363
+  } else {
364
+    root.sha1 = exports;
365
+    if (AMD) {
366
+      define(function () {
367
+        return exports;
368
+      });
369
+    }
370
+  }
371
+})();
0 372
\ No newline at end of file
1 373