Jeremy D commited on 2021-10-22 16:24:31
Showing 8 changed files, with 669 additions and 0 deletions.
... | ... |
@@ -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 |