├── README.md
├── bench
└── products
│ ├── bench1.png
│ └── bench2.png
├── doc
├── api.md
├── client-hash-via-insecure-network
│ ├── README.md
│ ├── hijack.png
│ ├── mode1.png
│ ├── mode2.png
│ └── sniffer.png
├── clienthash1.png
├── clienthash2.png
├── security-plugin-protect-input
│ ├── 1.png
│ ├── 2.png
│ └── README.md
└── why-not-argon2
│ └── README.md
├── example
├── basic
│ ├── README.md
│ ├── index.html
│ ├── main.css
│ └── main.js
└── login
│ ├── README.md
│ ├── db.json
│ ├── index.html
│ ├── login.php
│ ├── main.css
│ └── main.js
├── release
├── asset
│ ├── asmjs.js
│ └── flash.swf
├── scrypt.js
└── sourcemap
│ ├── asmjs.js.map
│ └── scrypt.js.map
└── src
├── c
├── pbkdf2.c
├── pbkdf2.h
├── smix.c
├── smix.h
└── sysendian.h
├── main
├── .vscode
│ └── tasks.json
├── Makefile
├── debug
│ ├── scrypt.js
│ └── scrypt.js.map
├── release
│ ├── scrypt.js
│ └── scrypt.js.map
├── src
│ ├── Const.ts
│ ├── IMod.d.ts
│ ├── ModAsmJs.ts
│ ├── ModFlash.ts
│ ├── Scrypt.ts
│ └── Util.ts
└── tsconfig.json
├── mod_asmjs
├── .vscode
│ └── tasks.json
├── Makefile
├── c-bind
│ ├── emout
│ │ ├── pbkdf2.js
│ │ └── smix.js
│ └── reduce.js
├── debug
│ ├── asmjs.js
│ └── asmjs.js.map
├── lib
│ ├── pbkdf2.js
│ └── smix.js
├── release
│ ├── asmjs.js
│ └── asmjs.js.map
├── src
│ ├── WorkerChild.ts
│ └── WorkerMain.ts
└── tsconfig.json
└── mod_flash
├── .actionScriptProperties
├── .project
├── .settings
└── org.eclipse.core.resources.prefs
├── Makefile
├── README.md
├── bin-debug
└── Scrypt.swf
├── bin-release
└── Scrypt.swf
├── c-bind
├── exports.txt
├── flascc.c
├── pthread-exports.txt
└── swf2lzma.py
├── lib
├── WebWorker.swc
└── scrypt.swc
└── src
├── Child.as
├── Main.as
└── Scrypt.as
/README.md:
--------------------------------------------------------------------------------
1 | # WebScrypt
2 |
3 | 一个浏览器版的 scrypt 算法,性能高、体积小。
4 |
5 |
6 | ## scrypt 简介
7 |
8 | [scrypt](https://en.wikipedia.org/wiki/Scrypt) 是一种密码学 Hash 函数,专门用于 Hash 口令。
9 |
10 | 不同于 PBKDF2、bcrypt 只有`时间成本`,scrypt 还可设定`空间成本`,该特征能使 GPU 等硬件设备破解 Hash 时瓶颈出现在内存上,从而降低硬件的计算优势。
11 |
12 | 另外 scrypt 支持`并发维度`,可充分利用多线程提高工作量,使破解时间成倍增加。[详细讲解](https://www.cnblogs.com/index-html/p/hardware-resistant-hash-algorithm.html)
13 |
14 |
15 | ## 前端计算
16 |
17 | 口令 Hash 函数的计算成本,决定了暴力破解的难度。但过高的成本,也会给服务器带来压力。因此通常只能在性能和安全之间折中。
18 |
19 | 事实上,口令 Hash 完全可在前端计算 —— 账号注册时,提交口令的 Hash 值(通常称之 DK);登录时,如果提交的 DK 相同,即可证明口令是相同的。
20 |
21 | ```javascript
22 | // REG or LOGIN
23 | dk = scrypt(password, username, cost ...)
24 |
25 | submit(username, dk, ...)
26 | ```
27 |
28 | 前端高成本 Hash 计算,不仅分担了后端压力,还能让原始口令数据更早消失,从而减少泄露环节,例如网络被窃听、服务端恶意程序等。
29 |
30 | 这就是本项目的初衷:在不增加网站基础设施的前提下,大幅提升账号口令安全。
31 |
32 |
33 |
34 | ## API
35 |
36 | * [使用文档](doc/api.md)
37 |
38 |
39 | ## 演示
40 |
41 | * [基本功能](https://etherdream.github.io/WebScrypt/example/basic/)
42 |
43 | * [登录演示](example/login/)
44 |
45 |
46 | ## 如何用到我的项目中?
47 |
48 | 更新中...
49 |
50 |
51 | ## 各种浏览器的性能对比
52 |
53 | 更新中...
54 |
55 |
56 | ## 其他的前端 scrypt 库
57 |
58 | | project | ver | asm.js | flash | purejs | thread | progress | size (gzip -6) |
59 | |:------------------------------------------------------------:|:------:|:------:|:-----:|:------:|:------:|:--------:|------------------:|
60 | | **WebScrypt** | latest | ✔ | ✔ | ✘ | ✔ | ✔️ | 2KB + 10KB / 54KB |
61 | | [js-scrypt](https://github.com/tonyg/js-scrypt) | 1.2.0 | ✔ | ✘ | ✘ | ✘ | ✘ | 384KB |
62 | | [scrypt-async-js](https://github.com/dchest/scrypt-async-js) | 1.3.0 | ✘ | ✘ | ✔ | ✘ | ✔ | 3KB |
63 |
64 | > 备注:54KB 的是 `flash.swf` 文件,只有低版本浏览器才会使用
65 |
66 |
67 | ### 单线程性能
68 |
69 |
70 |
71 | ### 多线程性能
72 |
73 |
74 |
75 | > 测试环境:Chrome56、OSX 10.11.6、MacBookPro 2013(2 GHz Intel Core i7,8 GB 1600 MHz DDR3)
76 |
77 |
78 | ## 探讨
79 |
80 | 探讨一些前端技术、隐私安全相关的话题。
81 |
82 | * [为何不选择 argon2 算法](doc/why-not-argon2/README.md)
83 |
84 | * [前端 Hash 能否对抗不安全的通信](doc/client-hash-via-insecure-network/README.md)
85 |
86 | * [「安全输入框插件」能否有效地保护输入数据](doc/security-plugin-protect-input/README.md)
87 |
88 | * 更多内容,敬请关注 ...
89 |
90 |
91 | ## License
92 |
93 | [MIT](https://opensource.org/licenses/MIT)
94 |
--------------------------------------------------------------------------------
/bench/products/bench1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherDream/WebScrypt/0620377a9d40372222d9268203c1fcd4354cb3ff/bench/products/bench1.png
--------------------------------------------------------------------------------
/bench/products/bench2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherDream/WebScrypt/0620377a9d40372222d9268203c1fcd4354cb3ff/bench/products/bench2.png
--------------------------------------------------------------------------------
/doc/api.md:
--------------------------------------------------------------------------------
1 | # WebScrypt API
2 |
3 | ## 使用
4 |
5 | ```html
6 |
7 | ```
8 |
9 | ## 方法
10 |
11 | ### getAvailableMod
12 |
13 | 获得可用的模块列表
14 |
15 | 参数:无
16 |
17 | 返回:string[]
18 |
19 | 案例:
20 |
21 | ```javascript
22 | scrypt.getAvailableMod();
23 | // ["asmjs", "flash"]
24 | ```
25 |
26 | 备注:scrypt.js 本身只是个加载器,实际的计算程序则位于外部模块中。这样能根据不同的浏览器,选择最合适的计算方案。
27 |
28 |
29 | ### setResPath
30 |
31 | 设置模块资源的路径
32 |
33 | 参数:
34 |
35 | * path: string
36 |
37 | 返回:无
38 |
39 | 案例:
40 |
41 | ```html
42 |
43 |
44 |
47 | ```
48 |
49 | 备注:该路径即 `release/asset` 目录最终所在的 URL。
50 |
51 |
52 | ### load
53 |
54 | 加载使用的模块。
55 |
56 | 参数:
57 |
58 | * mod: string (可选)
59 |
60 | | 模块 | 要求 | 相应资源
61 | |:-----:|:-------------------|---------------------|
62 | | asmjs | 支持 asm.js 的浏览器 | RES_PATH/asmjs.js |
63 | | flash | Flash 版本大于 18 | RES_PATH/flash.swf |
64 |
65 |
66 | 如果参数为空,系统将自动选择最合适的模块。(推荐)
67 |
68 |
69 | 案例:
70 |
71 | ```javascript
72 | scrypt.load();
73 | ```
74 |
75 | 备注:加载完成后会触发 `onload` 回调。
76 |
77 |
78 | ### config
79 |
80 | 配置算法所需的参数
81 |
82 | 参数:
83 |
84 | * param: object
85 |
86 | * opt: object(可选)
87 |
88 | * test: boolean(可选,默认 false)
89 |
90 |
91 | #### param
92 |
93 | * N: number
94 |
95 | * r: number
96 |
97 | * P: number
98 |
99 |
100 | ##### opt
101 |
102 | * maxPassLen: number(可选,默认 64)
103 |
104 | * maxSaltLen: number(可选,默认 64)
105 |
106 | * maxDkLen: number(可选,默认 64)
107 |
108 | * maxThread: number(可选,默认 1)
109 |
110 |
111 | ##### test
112 |
113 | 设置为 true 时,仅测试参数是否合法,不会真正分配资源。
114 |
115 |
116 | 案例:
117 |
118 | ```javascript
119 | var param = {
120 | N: 16384, // 时空成本
121 | r: 8, // 块大小
122 | P: 4 // 并发维度
123 | };
124 |
125 | var opt = {
126 | maxPassLen: 32, // 缓冲区大小分配
127 | maxSaltLen: 32,
128 | maxDkLen: 32,
129 | maxThread: 2 // 最多使用的线程数
130 | };
131 |
132 | try {
133 | scrypt.config(param, opt, true);
134 | } catch (err) {
135 | console.warn('config err: ', err);
136 | }
137 | ```
138 |
139 | 备注:该方法会创建相应数量的 Worker 并进行内存分配,做 `hash()` 的前期准备工作。配置完成后会触发 `onready` 回调。
140 |
141 | 注意,实际创建的 Worker 数量未必就是 maxThread 个,系统会根据参数 P 选择最合理的数量。例如 P = 8,maxThread = 6 时,开启 4 个 Worker 就够了。
142 |
143 |
144 | ### strToBin
145 |
146 | 辅助功能。字符串 → 二进制数组。
147 |
148 | 参数:
149 |
150 | * str: string
151 |
152 | 返回:Bytes (高版本浏览器使用 `Uint8Array`,低版本使用 `Array`)
153 |
154 | 案例:
155 |
156 | ```javascript
157 | scrypt.strToBin('hello');
158 | // [104, 101, 108, 108, 111]
159 |
160 | scrypt.strToBin('你好');
161 | // [228, 189, 160, 229, 165, 189]
162 |
163 | scrypt.strToBin('😀');
164 | // [240, 159, 152, 128]
165 | ```
166 |
167 |
168 | ### binToStr
169 |
170 | 辅助功能。二进制数组 → 字符串。
171 |
172 | 参数:
173 |
174 | * bin: Bytes
175 |
176 | 返回:string
177 |
178 | 案例:
179 |
180 | ```javascript
181 | scrypt.binToStr([104, 101, 108, 108, 111]);
182 | // "hello"
183 |
184 | scrypt.binToStr([228, 189, 160, 229, 165, 189]);
185 | // "你好"
186 |
187 | scrypt.binToStr([240, 159, 152, 128]);
188 | // "😀"
189 | ```
190 |
191 | 备注:解码失败会抛出异常。
192 |
193 |
194 | ### hexToBin
195 |
196 | 辅助功能。16 进制字符串 → 二进制数组
197 |
198 | 参数:
199 |
200 | * hex: string
201 |
202 | 返回:Bytes
203 |
204 | 案例:
205 |
206 | ```javascript
207 | scrypt.hexToBin('0001020340ff');
208 | // [0, 1, 2, 3, 64, 255]
209 | ```
210 |
211 |
212 | ### binToHex
213 |
214 | 辅助功能。二进制数组 → 16 进制字符串
215 |
216 | 参数:
217 |
218 | * bin: Bytes
219 |
220 | 返回:string
221 |
222 | 案例:
223 |
224 | ```javascript
225 | scrypt.binToHex([0, 1, 2, 3, 64, 255]);
226 | // "0001020340ff"
227 | ```
228 |
229 |
230 | ### hash
231 |
232 | 计算口令和盐的 hash 值。
233 |
234 | 参数:
235 |
236 | * pass: Bytes
237 |
238 | * salt: Bytes
239 |
240 | * dkLen: number
241 |
242 | 返回:无
243 |
244 | 案例:
245 |
246 | ```javascript
247 | var pass = scrypt.strToBin('iloveyou');
248 | var salt = scrypt.hexToBin('bc9064f2e2f978ed');
249 |
250 | scrypt.hash(pass, salt, 32);
251 | ```
252 |
253 | 备注:计算进度通过 `onprogress` 更新;计算完成会触发 `oncomplete` 回调。
254 |
255 | 注意,`pass`、`salt` 以及 `dkLen` 不能超过 `config()` 方法中对应的最大长度,否则会抛出错误。
256 |
257 |
258 | ### stop
259 |
260 | 停止当前的计算
261 |
262 | 参数:无
263 |
264 | 返回:无
265 |
266 | 案例:
267 |
268 | ```javascript
269 | txtPassword.onchange = function() {
270 | scrypt.stop();
271 | scrypt.hash(this.value, salt);
272 | };
273 | ```
274 |
275 | 备注:该方法不会强行关闭 Worker,而是在计算间歇中令其停止。
276 |
277 |
278 | ### unload
279 |
280 | 清理 Worker 并释放内存。
281 |
282 | 参数:无
283 |
284 | 返回:无
285 |
286 | 案例:
287 |
288 | ```javascript
289 | function onLoginSuccess() {
290 | scrypt.unload();
291 | }
292 | ```
293 |
294 | 备注:为了提高重复运行的效率,hash() 执行后不会自动清理资源,而是通过该方法手动清理。
295 |
296 | 由于网页关闭后浏览器会自动清理资源,因此通常不必调用该方法。但对于长时间运行的页面,例如 Single-Page App,及时清理还是有必要的。
297 |
298 | 清理后若要继续使用,则需重新 load()、config()。
299 |
300 |
301 |
302 | ## 回调
303 |
304 | ### onload
305 |
306 | `load` 方法加载完成时触发。
307 |
308 | 参数:无
309 |
310 | 案例:
311 |
312 | ```javascript
313 | scrypt.onload = function() {
314 | console.log('loaded');
315 | };
316 | ```
317 |
318 |
319 | ### onready
320 |
321 | `config` 方法完成时触发。
322 |
323 | 参数:无
324 |
325 | 案例:
326 |
327 | ```javascript
328 | scrypt.onready = function() {
329 | console.log('ready');
330 | scrypt.hash(P, S);
331 | };
332 | ```
333 |
334 | 备注:此时已准备就绪,可调用 `hash()`。
335 |
336 |
337 | ### onerror
338 |
339 | 资源加载失败、内存申请失败等错误发生时触发。
340 |
341 | 参数:
342 |
343 | * err: string
344 |
345 | 案例:
346 |
347 | ```javascript
348 | scrypt.onerror = function(err) {
349 | console.warn('scrypt err:', err);
350 | };
351 | ```
352 |
353 |
354 | ### onprogress
355 |
356 | 计算进度更新时触发。
357 |
358 | 参数:
359 |
360 | * percent: number
361 |
362 | 案例:
363 |
364 | ```javascript
365 | scrypt.onprogress = function(percent) {
366 | console.log('prog', (percent * 100) + '%');
367 | };
368 | ```
369 |
370 |
371 | ### oncomplete
372 |
373 | 计算完成时触发。
374 |
375 | 参数:
376 |
377 | * dk: Bytes
378 |
379 | 案例:
380 |
381 | ```javascript
382 | scrypt.oncomplete = function(dk) {
383 | console.log('done', scrypt.binToHex(dk));
384 | };
385 | ```
386 |
--------------------------------------------------------------------------------
/doc/client-hash-via-insecure-network/README.md:
--------------------------------------------------------------------------------
1 | # 前端 Hash 能否对抗不安全的通信
2 |
3 | 本项目的简介里提到,前端使用 WebScrypt 脚本 Hash 口令,可对「隐私嗅探」起到一定的防护。
4 |
5 | 当然现在不少网站部署了 HTTPS 协议,因此无需再考虑通信风险。但对于仍在使用 HTTP 的网站,前端 Hash 又能起到多大的效果?
6 |
7 |
8 | ## 攻击类型
9 |
10 | 通信上的攻击,通常可分「嗅探」和「劫持」。
11 |
12 | ### 嗅探
13 |
14 | 嗅探,即攻击者窃听通信流量。
15 |
16 | 
17 |
18 | 假如我们的登录过程被嗅探,那么前端提交的 dk 就会泄露。而 dk 是身份凭据,泄露即意味着 **攻击者用它也能登上该账号**。所以账号被盗用,显然是无法避免的。
19 |
20 | 不过,相比传统提交,风险其实已降低了不少 —— 传统提交,口令大多是毫不避讳,直接明文发送的。(当然有些开发者会用公钥加密口令,这确实能有效对抗嗅探攻击)
21 |
22 | 
23 |
24 | 而现在,攻击者嗅探到却是的 dk,这是口令经过 **高成本 Hash** 的计算结果。攻击者若想通过 dk 还原口令,得花费巨大的算力。
25 |
26 | 因此最终:账号被盗,口令拿不到。那些使用类似口令的其他账号,就幸免于难了。
27 |
28 | > 注意,这里的「被盗」是指能被攻击者使用,但未必就能改掉口令。后续文章会讨论这个问题。
29 |
30 |
31 | ### 劫持
32 |
33 | 嗅探是静默的,通常不篡改或注入流量。因此流量只是失去了隐蔽性,并没有破坏完整性。
34 |
35 | 然而现实中,攻击者大多有主动出击的能力。例如中间人攻击,或是数据包注入,能对流量实施篡改。
36 |
37 | 
38 |
39 | 既然能修改流量内容,攻击者即可往登录页面中植入一段 JS 恶意代码,这样就能从应用层发起攻击,直接读取表单元素的口令值。
40 |
41 | 
42 |
43 | 事实上,用户在页面中的一举一动,都能被恶意代码所监控,甚至在提交之前,输入的内容就已被悄悄发送到后台了。
44 |
45 | 因此对于流量劫持,前端 Hash 是无法防止口令泄露的。
46 |
47 |
48 | ## 结论
49 |
50 | 在不安全的通信下,使用前端 Hash 的效果:
51 |
52 | * 对于嗅探,虽不能防止账号被盗用,但可有效防止明文口令泄露。
53 |
54 | * 对于劫持,明文口令仍能轻易窃取。
55 |
56 | 所以前端 Hash 只能起到部分效果,无法代替 HTTPS 的功能。
57 |
--------------------------------------------------------------------------------
/doc/client-hash-via-insecure-network/hijack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherDream/WebScrypt/0620377a9d40372222d9268203c1fcd4354cb3ff/doc/client-hash-via-insecure-network/hijack.png
--------------------------------------------------------------------------------
/doc/client-hash-via-insecure-network/mode1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherDream/WebScrypt/0620377a9d40372222d9268203c1fcd4354cb3ff/doc/client-hash-via-insecure-network/mode1.png
--------------------------------------------------------------------------------
/doc/client-hash-via-insecure-network/mode2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherDream/WebScrypt/0620377a9d40372222d9268203c1fcd4354cb3ff/doc/client-hash-via-insecure-network/mode2.png
--------------------------------------------------------------------------------
/doc/client-hash-via-insecure-network/sniffer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherDream/WebScrypt/0620377a9d40372222d9268203c1fcd4354cb3ff/doc/client-hash-via-insecure-network/sniffer.png
--------------------------------------------------------------------------------
/doc/clienthash1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherDream/WebScrypt/0620377a9d40372222d9268203c1fcd4354cb3ff/doc/clienthash1.png
--------------------------------------------------------------------------------
/doc/clienthash2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherDream/WebScrypt/0620377a9d40372222d9268203c1fcd4354cb3ff/doc/clienthash2.png
--------------------------------------------------------------------------------
/doc/security-plugin-protect-input/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherDream/WebScrypt/0620377a9d40372222d9268203c1fcd4354cb3ff/doc/security-plugin-protect-input/1.png
--------------------------------------------------------------------------------
/doc/security-plugin-protect-input/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherDream/WebScrypt/0620377a9d40372222d9268203c1fcd4354cb3ff/doc/security-plugin-protect-input/2.png
--------------------------------------------------------------------------------
/doc/security-plugin-protect-input/README.md:
--------------------------------------------------------------------------------
1 | # 「安全输入框插件」能否有效地保护输入数据
2 |
3 | 不少安全性较高的网站,会提供一个安全输入框插件,用户必须下载安装后,才能输入账号口令等数据。例如各大网银的登录页面,相信大家都见过。
4 |
5 | 下面我们来探讨,这类安全插件对用户输入的数据,究竟能起到多大的保护效果。
6 |
7 | ## 对抗
8 |
9 | 稍了解编程的朋友都知道,用户在安全框内输入时,其他程序是无法通过简单的技术手段,来截获按键消息的。因为插件会从最底层的键盘驱动中取走消息,保障按键过程无法被轻易窃听。
10 |
11 | 同时,也无法简单地通过内存扫描等手段,获取控件内部的数据。所以从系统攻防上,这类插件还是有一定保护效果的。
12 |
13 | 但是,攻击者非得从系统攻防上进行对抗吗?
14 |
15 |
16 | ## 绕过
17 |
18 | 我们先来聊一个其他话题。大家都知道 HTTPS 是难以破解的,但这并不意味它就是无懈可击的。例如某些场合下,使用类似 SSLStrip 的工具,就能让用户进不了 HTTPS —— 我们虽然不能解决问题,但能回避问题。
19 |
20 | 类似的,对于安全输入框,我们也可设法回避它。例如,支付宝曾经使用的登录插件(几年前的截图):
21 |
22 | 
23 |
24 | 从系统攻防的角度来看,它是管用的。恶意程序想通过按键窃听口令,并不十分容易。至于想靠页面中的 JS 来捕获键盘事件,那更是天方夜谭。
25 |
26 | 然而事实上,用 JS 攻击才是最容易、最可行的。并不是因为这个插件存在什么接口、暴露了什么隐私,而是,这里根本就用不着插件!
27 |
28 | 如果能运行 JS,我们只需简单地删除插件 DOM,然后**通过 HTML 画个相似的输入框**,就能钓到用户口令了:)
29 |
30 | ```javascript
31 | $('.alieditContainer').html(
32 | ''
33 | );
34 | ```
35 |
36 | 至于用户无法正常登录,我们并不用关心,反正我们已经拿到明文口令了 —— 虽然未必正确,但正确的可能性是很大的。另外,也不必每次都劫持,让用户仍能正常登陆。
37 |
38 | 
39 |
40 | 更进一步,恶意代码甚至可通过 API Hook 或是 `MutationObserver` 拦截安全控件的 DOM 创建。这样,防护程序连运行的机会都没有,更不用说风险扫描了。
41 |
42 | 由此看来,程序的安全等级,并不能只看权限的高低。启动的先后顺序,更是直接决定了生死。
43 |
44 | 如果某个恶意程序在系统攻防上并不突出,却有注入 JS 到浏览器页面的能力,同样能绕过这类插件,直接获取用户的输入信息。
45 |
46 |
47 | ## 结尾
48 |
49 | 所以光靠独立的插件,并不能十分有效地保护输入信息。还需结合优先级更高的系统服务共同防护,才能起到更好的效果。
50 |
51 | 当然,安全插件并不仅仅是为了提供输入环境,还有读取 USBkey、采集系统信息等更多操作。这些功能浏览器大多不支持,所以只能通过插件来实现 —— 既然有了插件,于是就干脆再加上输入防护的功能,有防护总比没有好吧。
52 |
--------------------------------------------------------------------------------
/doc/why-not-argon2/README.md:
--------------------------------------------------------------------------------
1 |
2 | # 为何不用 argon2
3 |
4 | 2015 年 P-H-C 胜出者 [argon2](https://github.com/P-H-C/phc-winner-argon2),是目前最新的口令 Hash 函数。OWASP 在 [Password Storage Cheat Sheet](https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet) 中,也推荐开发者首选该算法。
5 |
6 | 既然 argon2 比 scrypt 更先进,为什么本项目不使用?事实上,之前已有人尝试将 [argon2 移植到浏览器](https://github.com/antelle/argon2-browser),但遇到一个棘手的问题:argon2 大量使用了 64 位整数计算,而 JavaScript 并没有原生的 64 位整数,只能通过模拟实现,因此效率非常低。(asm.js 作为 JS 的子集自然也不支持。另外 Flash 虚拟机 AVM2 同样不支持 64 位整数)
7 |
8 | 所以算法未必越新越好,还得看实际运行的环境,能否提供充足的支持。
9 |
--------------------------------------------------------------------------------
/example/basic/README.md:
--------------------------------------------------------------------------------
1 |
2 | Online Demo: https://etherdream.github.io/webscrypt/example/basic/
3 |
--------------------------------------------------------------------------------
/example/basic/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | webscrypt
5 |
6 |
7 |
14 |
15 |
16 | Scrypt Hash Function
17 |
75 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/example/basic/main.css:
--------------------------------------------------------------------------------
1 | body, textarea, input {
2 | font-family: monospace;
3 | font-size: 13px;
4 | }
5 | input {
6 | border: #ddd 1px solid;
7 | }
8 | fieldset {
9 | padding: 10px;
10 | margin-bottom: 20px;
11 | }
12 |
13 | .column {
14 | border: #ddd 1px solid;
15 | background-color: #f8f8f8;
16 | padding: 6px 10px;
17 | margin-bottom: 10px;
18 | }
19 | .column div {
20 | padding: 1px 0;
21 | }
22 |
23 | ul {
24 | padding-left: 18px;
25 | margin-left: 0;
26 | }
27 |
28 | #txtPass, #txtSalt {
29 | width: 300px;
30 | }
31 |
32 | #txtLog {
33 | width: 100%;
34 | height: 250px;
35 | font-size: 12px;
36 | margin-bottom: 10px;
37 | overflow-y: scroll;
38 | }
39 |
40 | #progBox {
41 | width: 400px;
42 | height: 8px;
43 | overflow: hidden;
44 | margin-top: 10px;
45 | background-color: #eee;
46 | border: #000 1px solid;
47 | }
48 |
49 | #progBar {
50 | height: 100%;
51 | width: 0;
52 | background-color: #333;
53 | }
54 |
--------------------------------------------------------------------------------
/example/basic/main.js:
--------------------------------------------------------------------------------
1 | var tick;
2 | var progBarStyle = $('#progBar')[0].style;
3 |
4 | scrypt.onprogress = function(percent) {
5 | progBarStyle.width = (percent * 100) + '%';
6 | };
7 |
8 | scrypt.oncomplete = function(dk) {
9 | var t = +new Date() - tick;
10 | print('done! time: ' + t + 'ms');
11 |
12 | print('dk: ' + scrypt.binToHex(dk));
13 | updateButton(1, 0);
14 | };
15 |
16 | scrypt.onready = function() {
17 | var pass = getPassBytes();
18 | var salt = getSaltBytes();
19 | var dkLen = getTextVal('#txtDkLen');
20 |
21 | tick = +new Date();
22 |
23 | try {
24 | scrypt.hash(pass, salt, dkLen);
25 | } catch (err) {
26 | print('scrypt.hash err: ' + err.message);
27 | updateButton(1, 0);
28 | }
29 | };
30 |
31 | scrypt.onload = function() {
32 | print('loaded');
33 | updateButton(1, 0);
34 | updateCost();
35 | };
36 |
37 | scrypt.onerror = function(err) {
38 | print('scrypt.onerror: ' + err);
39 | };
40 |
41 | /*------------------------------*/
42 | var param = {};
43 | var opt = {
44 | maxPassLen: 1024,
45 | maxSaltLen: 1024,
46 | maxDkLen: 1024,
47 | maxThread: 1
48 | };
49 |
50 | $('#btnHash').click(function() {
51 | updateParam();
52 | try {
53 | scrypt.config(param, opt);
54 | } catch (err) {
55 | print('scrypt.config err: ' + err.message);
56 | return;
57 | }
58 |
59 | var pass = $('#txtPass').val();
60 | var salt = $('#txtSalt').val();
61 |
62 | pass = formatVal(pass, $('#chkHexPass').prop('checked'));
63 | salt = formatVal(salt, $('#chkHexSalt').prop('checked'));
64 |
65 | print(
66 | 'scrypt(' +
67 | 'P=' + pass + ', ' +
68 | 'S=' + salt + ', ' +
69 | 'N=' + param.N + ', ' +
70 | 'r=' + param.r + ', ' +
71 | 'P=' + param.P + ', ' +
72 | 'dkLen=' + $('#txtDkLen').val() + ')'
73 | );
74 | updateButton(0, 1);
75 | });
76 |
77 | $('#btnStop').click(function() {
78 | scrypt.stop();
79 |
80 | print('canceled');
81 | updateButton(1, 0);
82 | });
83 |
84 | $('#btnCls').click(function() {
85 | $('#txtLog').html('');
86 | });
87 |
88 | $('#optMod').change(function() {
89 | var mod = this.options[this.selectedIndex].value;
90 | loadMod(mod);
91 | });
92 |
93 | $('#chkHexPass').click(function() {
94 | updateHex($('#txtPass'), this.checked);
95 | });
96 |
97 | $('#chkHexSalt').click(function() {
98 | updateHex($('#txtSalt'), this.checked);
99 | });
100 |
101 | $('.column.params input').change(updateCost);
102 |
103 | /*------------------------------*/
104 | var curMod;
105 |
106 | function loadMod(mod) {
107 | if (curMod == mod) {
108 | return;
109 | }
110 | curMod = mod;
111 |
112 | print('loading module ' + mod);
113 | updateButton(0, 0);
114 |
115 | scrypt.unload();
116 |
117 | if (mod == 'auto') {
118 | scrypt.load();
119 | } else {
120 | scrypt.load(mod);
121 | }
122 | }
123 |
124 | function getPassBytes() {
125 | var v = $('#txtPass').val();
126 |
127 | return $('#chkHexPass').prop('checked') ?
128 | scrypt.hexToBin(v) :
129 | scrypt.strToBin(v) ;
130 | }
131 |
132 | function getSaltBytes() {
133 | var v = $('#txtSalt').val();
134 |
135 | return $('#chkHexSalt').prop('checked') ?
136 | scrypt.hexToBin(v) :
137 | scrypt.strToBin(v) ;
138 | }
139 |
140 | function formatVal(val, isHex) {
141 | return isHex ?
142 | ('0x' + val) :
143 | ('"' + val + '"') ;
144 | }
145 |
146 | function updateHex(textbox, showHex) {
147 | if (showHex) {
148 | var str = textbox.val();
149 | var bin = scrypt.strToBin(str);
150 | var hex = scrypt.binToHex(bin);
151 | textbox.val(hex);
152 | } else {
153 | var hex = textbox.val();
154 | var bin = scrypt.hexToBin(hex);
155 | var str = scrypt.binToStr(bin);
156 | textbox.val(str);
157 | }
158 | }
159 |
160 | function updateButton(b1, b2) {
161 | $('#btnHash').prop('disabled', !b1);
162 | $('#btnStop').prop('disabled', !b2);
163 | }
164 |
165 | function formatByte(v) {
166 | var unit = ['B', 'KB', 'MB', 'GB'];
167 | var i = 0;
168 | while (v >= 1024 && i < unit.length - 1) {
169 | v /= 1024;
170 | i++;
171 | }
172 | return Math.round(v * 100) / 100 + unit[i];
173 | }
174 |
175 | function getTextVal(s) {
176 | return parseInt($(s).val(), 10);
177 | }
178 |
179 | function updateParam() {
180 | param.N = Math.pow(2, getTextVal('#txtLog2N'));
181 | param.r = getTextVal('#txtArgR');
182 | param.P = getTextVal('#txtArgP');
183 | opt.maxThread = getTextVal('#txtThread');
184 | }
185 |
186 | function updateCost() {
187 | updateParam();
188 | try {
189 | // check param
190 | scrypt.config(param, opt, true);
191 | } catch (err) {
192 | $('#txtMemCost').text('INVALID');
193 | return;
194 | }
195 |
196 | var maxPerThread = Math.ceil(param.P / opt.maxThread);
197 | var threadNeed = Math.ceil(param.P / maxPerThread);
198 |
199 | var mem = param.N * 128 * param.r * threadNeed;
200 | $('#txtMemCost').text(
201 | param.N + ' * ' + param.r + ' * 128 * ' + threadNeed +
202 | ' = ' + formatByte(mem)
203 | );
204 | }
205 |
206 | function print(s) {
207 | $('#txtLog')
208 | .append(s + '
')
209 | .scrollTop(1e6);
210 | }
211 |
212 | function initModOptBox() {
213 | var list = scrypt.getAvailableMod();
214 | if (list.length == 0) {
215 | print('HTML5 or Flash Player 18+');
216 | updateButton(0, 0);
217 | return;
218 | }
219 |
220 | var set = {auto: true};
221 |
222 | $.each(list, function(i, name) {
223 | set[name] = true;
224 | });
225 |
226 | var optBox = $('#optMod');
227 | $.each(optBox[0], function(i, item) {
228 | item.disabled = !(item.value in set);
229 | });
230 | optBox.change();
231 | }
232 |
233 | function main() {
234 | scrypt.setResPath('../../release/asset');
235 |
236 | var sysThread = navigator.hardwareConcurrency;
237 | if (sysThread != null) {
238 | if (sysThread > 4) {
239 | txtThread.value = sysThread / 2;
240 | } else {
241 | txtThread.value = sysThread;
242 | }
243 | }
244 |
245 | $('#txtLog').val('');
246 | print('UserAgent: ' + navigator.userAgent);
247 | print('SysThread: ' + sysThread);
248 | print('--------------------');
249 |
250 | initModOptBox();
251 | }
252 |
253 | $(main);
254 |
--------------------------------------------------------------------------------
/example/login/README.md:
--------------------------------------------------------------------------------
1 | ## 简介
2 |
3 | 演示 scrypt 在前端登录的应用。
4 |
5 | 为简单起见,本案例 scrypt 参数是固定的 —— 选取用户名作为盐,时间成本、空间成本、并行维度都是固定的。
6 |
7 | 另外后端也没有使用数据库,而是直接记录在 `db.json` 文件里。
8 |
9 | 登录成功后,你可以看到用户对应的一些隐私信息,里面有红包哦:)
10 |
11 |
12 | ## 演示
13 |
14 | 在线演示:http://www.etherdream.com/webscrypt/example/login/
15 |
16 | 如果无法访问,可下载到本地运行:
17 |
18 | ```
19 | git clone https://github.com/EtherDream/webscrypt.git
20 | cd webscrypt
21 | php -S 127.0.0.1:8080
22 | ```
23 |
24 | 访问 http://127.0.0.1:8080/example/login/ 即可本地测试。
25 |
26 | 由于程序和数据都是公开的(相当于已被拖库),因此不推荐在线撞库破解。如果非要撞库,请在本地进行。
27 |
--------------------------------------------------------------------------------
/example/login/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "test": {
3 | "s_salt": "049655f8",
4 | "s_hash": "6089ddb4374d9a0850c8680f2cbca69107fd40ee488b22eba89ad6b9fe6e54fc",
5 | "pub_info": "登录成功会在此显示一些隐私信息。刷新页面继续挑战吧",
6 | "sec_info": ""
7 | },
8 | "alice": {
9 | "s_salt": "6ea1361a",
10 | "s_hash": "5c4e522168f2c33c2d0f62c7f307c5822dc7a768948767cf34f47a550b32cb03",
11 | "pub_info": "这个密码简单吧~ 赏你一点微小的奖励(支付宝口令红包 %s)",
12 | "sec_info": "94ba2b35a4167781"
13 | },
14 | "bob": {
15 | "s_salt": "6097685b",
16 | "s_hash": "cfae279c825b3ad428fb64bae0c6d83c7708e4afafc423cef527e739c8ac6788",
17 | "pub_info": "不错,这都被你试出来啦:) 来领取红包 %s",
18 | "sec_info": "d0147aac2cd75256"
19 | },
20 | "root": {
21 | "s_salt": "3e8f4a52",
22 | "s_hash": "f63a289813c0d2953f913bfe52d12e87a2db81564be4879ae12e40ca0679b41d",
23 | "pub_info": "太厉害了!奖励你个大红包 %s",
24 | "sec_info": "d1ec66e43a0a2a49"
25 | }
26 | }
--------------------------------------------------------------------------------
/example/login/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 弱口令保护 Demo
5 |
6 |
7 |
8 |
9 |
10 |
27 |
28 | 挑战账号:
29 | alice(密码是 2 位数字)
30 | bob (密码是 4 位数字)
31 | root (密码是 6 位数字)
32 |
33 | 挑战奖励:
34 | 登录成功可获得红包,难度越大奖励越高。
35 | 红包数量有限,过期时间为一天。(最后更新:2017-2-27 22:30:00)
36 |
37 | 测试账号:
38 | test/test
39 |
40 | 后端程序:
41 | 后端的程序和数据都是公开的,点此查看。本站带宽很小,请勿在线撞库。
42 |
43 |
44 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/example/login/login.php:
--------------------------------------------------------------------------------
1 | >>0>>0){k=C+-64|0;U=o+k|0;L=A+-1|0;I=q&1073741792;x=N+k|0;if(!I){b=c;while(1){f(o,N,e);f(N,o,e);b=b+2|0;if(b>>>0>=s>>>0)break A}}else g=c;do{_=M+((t(n[U>>2]&L,q)|0)<<2)|0;v=0;do{H=o+(v<<2)|0;n[H>>2]=n[H>>2]^n[_+(v<<2)>>2];v=v+1|0}while((v|0)!=(I|0));f(o,N,e);j=M+((t(n[x>>2]&L,q)|0)<<2)|0;d=0;do{G=N+(d<<2)|0;n[G>>2]=n[G>>2]^n[j+(d<<2)>>2];d=d+1|0}while((d|0)!=(I|0));f(N,o,e);g=g+2|0}while(g>>>0>>0)}while(0);if((s|0)==(A|0)&(q|0)!=0)m=0;else return;do{P=r+(m<<2)|0;S=n[o+(m<<2)>>2]|0;i[P>>0]=S;i[P+1>>0]=S>>>8;i[P+2>>0]=S>>>16;i[P+3>>0]=S>>>24;m=m+1|0}while((m|0)!=(q|0));return}if((c|0)==0&(q|0)!=0){p=0;do{E=r+(p<<2)|0;n[o+(p<<2)>>2]=(a[E+1>>0]|0)<<8|(a[E>>0]|0)|(a[E+2>>0]|0)<<16|(a[E+3>>0]|0)<<24;p=p+1|0}while((p|0)!=(q|0))}D=t(q,c)|0;K=t(q,s)|0;if(D>>>0>=K>>>0)return;R=q&1073741792;if(!R){w=D;do{f(o,N,e);w=w+q+q|0;f(N,o,e)}while(w>>>0>>0);return}else l=D;do{B=M+(l<<2)|0;h=0;do{n[B+(h<<2)>>2]=n[o+(h<<2)>>2];h=h+1|0}while((h|0)!=(R|0));F=l+q|0;f(o,N,e);O=M+(F<<2)|0;y=0;do{n[O+(y<<2)>>2]=n[N+(y<<2)>>2];y=y+1|0}while((y|0)!=(R|0));l=F+q|0;f(N,o,e)}while(l>>>0>>0);return}function f(A,e,r){A=A|0;e=e|0;r=r|0;var t=0,i=0,a=0,o=0,f=0,u=0,c=0,s=0,l=0,w=0,h=0,v=0,d=0,y=0,p=0,g=0,b=0,m=0,M=0,k=0,U=0,L=0,I=0,x=0,E=0,D=0,K=0,R=0,B=0,F=0,O=0,_=0,C=0,H=0,j=0,q=0,G=0,N=0,P=0,S=0,V=0,Z=0,Q=0,T=0,J=0,Y=0,W=0,z=0,X=0,$=0,AA=0,eA=0,rA=0,nA=0,tA=0,iA=0,aA=0,oA=0,fA=0,uA=0,cA=0,sA=0,lA=0,wA=0,hA=0,vA=0,dA=0,yA=0,pA=0,gA=0,bA=0,mA=0,MA=0,kA=0,UA=0,LA=0,IA=0,xA=0,EA=0,DA=0,KA=0,RA=0,BA=0,FA=0,OA=0,_A=0,CA=0,HA=0,jA=0,qA=0,GA=0,NA=0,PA=0,SA=0,VA=0,ZA=0,QA=0,TA=0,JA=0,YA=0,WA=0,zA=0,XA=0,$A=0,Ae=0,ee=0,re=0,ne=0,te=0,ie=0,ae=0,oe=0,fe=0,ue=0,ce=0,se=0,le=0,we=0,he=0,ve=0,de=0,ye=0,pe=0,ge=0,be=0,me=0,Me=0,ke=0,Ue=0,Le=0,Ie=0,xe=0,Ee=0,De=0,Ke=0,Re=0,Be=0,Fe=0,Oe=0,_e=0,Ce=0,He=0,je=0,qe=0,Ge=0,Ne=0,Pe=0,Se=0,Ve=0,Ze=0,Qe=0,Te=0,Je=0,Ye=0,We=0,ze=0,Xe=0,$e=0,Ar=0,er=0,rr=0,nr=0,tr=0,ir=0,ar=0,or=0,fr=0,ur=0,cr=0,sr=0,lr=0,wr=0,hr=0,vr=0,dr=0,yr=0,pr=0,gr=0,br=0,mr=0,Mr=0,kr=0,Ur=0,Lr=0,Ir=0,xr=0,Er=0,Dr=0,Kr=0,Rr=0,Br=0,Fr=0,Or=0,_r=0,Cr=0,Hr=0,jr=0,qr=0,Gr=0,Nr=0,Pr=0,Sr=0,Vr=0,Zr=0,Qr=0,Tr=0,Jr=0,Yr=0,Wr=0,zr=0,Xr=0,$r=0,An=0,en=0,rn=0,nn=0,tn=0,an=0,on=0,fn=0,un=0,cn=0,sn=0,ln=0,wn=0,hn=0,vn=0,dn=0,yn=0,pn=0,gn=0,bn=0,mn=0,Mn=0,kn=0,Un=0,Ln=0,In=0,xn=0,En=0,Dn=0,Kn=0,Rn=0,Bn=0,Fn=0,On=0,_n=0,Cn=0,Hn=0,jn=0,qn=0,Gn=0,Nn=0,Pn=0,Sn=0,Vn=0,Zn=0,Qn=0,Tn=0,Jn=0,Yn=0,Wn=0,zn=0,Xn=0,$n=0,At=0,et=0,rt=0,nt=0,tt=0,it=0,at=0,ot=0,ft=0,ut=0,ct=0,st=0,lt=0,wt=0,ht=0,vt=0,dt=0,yt=0,pt=0,gt=0,bt=0,mt=0,Mt=0,kt=0,Ut=0,Lt=0,It=0,xt=0,Et=0,Dt=0,Kt=0,Rt=0,Bt=0,Ft=0,Ot=0,_t=0,Ct=0,Ht=0,jt=0,qt=0,Gt=0,Nt=0,Pt=0,St=0,Vt=0,Zt=0,Qt=0,Tt=0,Jt=0,Yt=0,Wt=0,zt=0,Xt=0,$t=0,Ai=0,ei=0,ri=0,ni=0,ti=0,ii=0,ai=0,oi=0,fi=0,ui=0,ci=0,si=0,li=0,wi=0,hi=0,vi=0,di=0,yi=0,pi=0,gi=0,bi=0,mi=0,Mi=0,ki=0,Ui=0,Li=0,Ii=0,xi=0,Ei=0,Di=0,Ki=0,Ri=0,Bi=0,Fi=0,Oi=0,_i=0,Ci=0,Hi=0,ji=0,qi=0,Gi=0,Ni=0,Pi=0,Si=0,Vi=0,Zi=0,Qi=0,Ti=0,Ji=0,Yi=0,Wi=0,zi=0,Xi=0,$i=0,Aa=0,ea=0,ra=0,na=0,ta=0,ia=0,aa=0,oa=0,fa=0,ua=0,ca=0,sa=0,la=0,wa=0,ha=0,va=0,da=0,ya=0,pa=0,ga=0,ba=0,ma=0,Ma=0,ka=0,Ua=0,La=0,Ia=0,xa=0,Ea=0,Da=0,Ka=0,Ra=0,Ba=0,Fa=0,Oa=0,_a=0,Ca=0,Ha=0,ja=0,qa=0,Ga=0,Na=0,Pa=0,Sa=0,Va=0,Za=0,Qa=0,Ta=0,Ja=0,Ya=0,Wa=0,za=0,Xa=0,$a=0,Ao=0,eo=0,ro=0,no=0,to=0,io=0,ao=0,oo=0,fo=0,uo=0,co=0,so=0,lo=0,wo=0,ho=0,vo=0,yo=0,po=0,go=0,bo=0,mo=0,Mo=0,ko=0,Uo=0,Lo=0,Io=0,xo=0,Eo=0,Do=0,Ko=0,Ro=0,Bo=0,Fo=0,Oo=0,_o=0,Co=0,Ho=0,jo=0,qo=0,Go=0,No=0,Po=0,So=0,Vo=0,Zo=0,Qo=0,To=0,Jo=0,Yo=0,Wo=0,zo=0,Xo=0,$o=0,Af=0,ef=0,rf=0,nf=0,tf=0,af=0,of=0,ff=0,uf=0,cf=0,sf=0,lf=0,wf=0,hf=0,vf=0,df=0,yf=0,pf=0,gf=0,bf=0,mf=0,Mf=0,kf=0,Uf=0,Lf=0,If=0,xf=0,Ef=0,Df=0,Kf=0,Rf=0,Bf=0,Ff=0,Of=0,_f=0,Cf=0,Hf=0,jf=0,qf=0,Gf=0,Nf=0,Pf=0,Sf=0,Vf=0,Zf=0,Qf=0,Tf=0,Jf=0,Yf=0,Wf=0,zf=0,Xf=0,$f=0,Au=0,eu=0,ru=0,nu=0,tu=0,iu=0,au=0,ou=0,fu=0,uu=0,cu=0,su=0,lu=0,wu=0,hu=0,vu=0,du=0,yu=0,pu=0,gu=0,bu=0,mu=0,Mu=0,ku=0,Uu=0,Lu=0,Iu=0,xu=0,Eu=0,Du=0,Ku=0,Ru=0,Bu=0,Fu=0,Ou=0,_u=0,Cu=0,Hu=0,ju=0,qu=0,Gu=0,Nu=0,Pu=0,Su=0,Vu=0;ct=r<<1;Ba=A+((r<<5)+-16<<2)|0;if(!ct)return;Tt=r<<4;t=n[Ba>>2]|0;i=n[Ba+4>>2]|0;a=n[Ba+8>>2]|0;o=n[Ba+12>>2]|0;f=n[Ba+16>>2]|0;u=n[Ba+20>>2]|0;c=n[Ba+24>>2]|0;s=n[Ba+28>>2]|0;l=n[Ba+32>>2]|0;w=n[Ba+36>>2]|0;h=n[Ba+40>>2]|0;v=n[Ba+44>>2]|0;d=n[Ba+48>>2]|0;y=n[Ba+52>>2]|0;p=n[Ba+56>>2]|0;g=n[Ba+60>>2]|0;b=0;do{Xt=b<<4;ni=A+(Xt<<2)|0;li=n[ni>>2]^t;Li=n[ni+4>>2]^i;ji=n[ni+8>>2]^a;zi=n[ni+12>>2]^o;ca=n[ni+16>>2]^f;ka=n[ni+20>>2]^u;Ha=n[ni+24>>2]^c;Wa=n[ni+28>>2]^s;fo=n[ni+32>>2]^l;mo=n[ni+36>>2]^w;Bo=n[ni+40>>2]^h;Wo=n[ni+44>>2]^v;ff=n[ni+48>>2]^d;gf=n[ni+52>>2]^y;Bf=n[ni+56>>2]^p;Zf=n[ni+60>>2]^g;Wf=ff+li|0;wu=(Wf<<7|Wf>>>25)^ca;pu=wu+li|0;_u=(pu<<9|pu>>>23)^fo;Gu=_u+wu|0;F=(Gu<<13|Gu>>>19)^ff;j=F+_u|0;eA=(j<<18|j>>>14)^li;aA=ka+Li|0;kA=mo^(aA<<7|aA>>>25);EA=kA+ka|0;ZA=gf^(EA<<9|EA>>>23);WA=ZA+kA|0;we=(WA<<13|WA>>>19)^Li;pe=we+ZA|0;_e=(pe<<18|pe>>>14)^ka;Ge=Bo+Ha|0;nr=Bf^(Ge<<7|Ge>>>25);fr=nr+Bo|0;Lr=(fr<<9|fr>>>23)^ji;Kr=Lr+nr|0;Zr=(Kr<<13|Kr>>>19)^Ha;Yr=Zr+Lr|0;en=(Yr<<18|Yr>>>14)^Bo;rn=Zf+Wo|0;nn=(rn<<7|rn>>>25)^zi;tn=nn+Zf|0;an=(tn<<9|tn>>>23)^Wa;on=an+nn|0;fn=(on<<13|on>>>19)^Wo;un=fn+an|0;cn=(un<<18|un>>>14)^Zf;sn=eA+nn|0;ln=(sn<<7|sn>>>25)^we;wn=ln+eA|0;hn=(wn<<9|wn>>>23)^Lr;vn=hn+ln|0;dn=(vn<<13|vn>>>19)^nn;yn=dn+hn|0;pn=(yn<<18|yn>>>14)^eA;gn=_e+wu|0;bn=(gn<<7|gn>>>25)^Zr;mn=bn+_e|0;Mn=(mn<<9|mn>>>23)^an;kn=Mn+bn|0;Un=(kn<<13|kn>>>19)^wu;Ln=Un+Mn|0;In=(Ln<<18|Ln>>>14)^_e;xn=en+kA|0;En=(xn<<7|xn>>>25)^fn;Dn=En+en|0;Kn=(Dn<<9|Dn>>>23)^_u;Rn=Kn+En|0;Bn=(Rn<<13|Rn>>>19)^kA;Fn=Bn+Kn|0;On=(Fn<<18|Fn>>>14)^en;_n=cn+nr|0;Cn=(_n<<7|_n>>>25)^F;Hn=Cn+cn|0;jn=(Hn<<9|Hn>>>23)^ZA;qn=jn+Cn|0;Gn=(qn<<13|qn>>>19)^nr;Nn=Gn+jn|0;Pn=(Nn<<18|Nn>>>14)^cn;Sn=pn+Cn|0;Vn=(Sn<<7|Sn>>>25)^Un;Zn=Vn+pn|0;Qn=(Zn<<9|Zn>>>23)^Kn;Tn=Qn+Vn|0;Jn=(Tn<<13|Tn>>>19)^Cn;Yn=Jn+Qn|0;Wn=(Yn<<18|Yn>>>14)^pn;zn=In+ln|0;Xn=(zn<<7|zn>>>25)^Bn;$n=Xn+In|0;At=($n<<9|$n>>>23)^jn;et=At+Xn|0;rt=(et<<13|et>>>19)^ln;nt=rt+At|0;tt=(nt<<18|nt>>>14)^In;it=On+bn|0;at=(it<<7|it>>>25)^Gn;ot=at+On|0;ft=(ot<<9|ot>>>23)^hn;ut=ft+at|0;st=(ut<<13|ut>>>19)^bn;lt=st+ft|0;wt=(lt<<18|lt>>>14)^On;ht=Pn+En|0;vt=(ht<<7|ht>>>25)^dn;dt=vt+Pn|0;yt=(dt<<9|dt>>>23)^Mn;pt=yt+vt|0;gt=(pt<<13|pt>>>19)^En;bt=gt+yt|0;mt=(bt<<18|bt>>>14)^Pn;Mt=Wn+vt|0;kt=(Mt<<7|Mt>>>25)^rt;Ut=kt+Wn|0;Lt=(Ut<<9|Ut>>>23)^ft;It=Lt+kt|0;xt=(It<<13|It>>>19)^vt;Et=xt+Lt|0;Dt=(Et<<18|Et>>>14)^Wn;Kt=tt+Vn|0;Rt=(Kt<<7|Kt>>>25)^st;Bt=Rt+tt|0;Ft=(Bt<<9|Bt>>>23)^yt;Ot=Ft+Rt|0;_t=(Ot<<13|Ot>>>19)^Vn;Ct=_t+Ft|0;Ht=(Ct<<18|Ct>>>14)^tt;jt=wt+Xn|0;qt=(jt<<7|jt>>>25)^gt;Gt=qt+wt|0;Nt=(Gt<<9|Gt>>>23)^Qn;Pt=Nt+qt|0;St=(Pt<<13|Pt>>>19)^Xn;Vt=St+Nt|0;Zt=(Vt<<18|Vt>>>14)^wt;Qt=mt+at|0;Jt=(Qt<<7|Qt>>>25)^Jn;Yt=Jt+mt|0;Wt=(Yt<<9|Yt>>>23)^At;zt=Wt+Jt|0;$t=(zt<<13|zt>>>19)^at;Ai=$t+Wt|0;ei=(Ai<<18|Ai>>>14)^mt;ri=Dt+Jt|0;ti=(ri<<7|ri>>>25)^_t;ii=ti+Dt|0;ai=(ii<<9|ii>>>23)^Nt;oi=ai+ti|0;fi=(oi<<13|oi>>>19)^Jt;ui=fi+ai|0;ci=(ui<<18|ui>>>14)^Dt;si=Ht+kt|0;wi=(si<<7|si>>>25)^St;hi=wi+Ht|0;vi=(hi<<9|hi>>>23)^Wt;di=vi+wi|0;yi=(di<<13|di>>>19)^kt;pi=yi+vi|0;gi=(pi<<18|pi>>>14)^Ht;bi=Zt+Rt|0;mi=(bi<<7|bi>>>25)^$t;Mi=mi+Zt|0;ki=(Mi<<9|Mi>>>23)^Lt;Ui=ki+mi|0;Ii=(Ui<<13|Ui>>>19)^Rt;xi=Ii+ki|0;Ei=(xi<<18|xi>>>14)^Zt;Di=ei+qt|0;Ki=(Di<<7|Di>>>25)^xt;Ri=Ki+ei|0;Bi=(Ri<<9|Ri>>>23)^Ft;Fi=Bi+Ki|0;Oi=(Fi<<13|Fi>>>19)^qt;_i=Oi+Bi|0;Ci=(_i<<18|_i>>>14)^ei;Hi=ci+Ki|0;qi=(Hi<<7|Hi>>>25)^yi;Gi=qi+ci|0;Ni=(Gi<<9|Gi>>>23)^ki;Pi=Ni+qi|0;Si=(Pi<<13|Pi>>>19)^Ki;Vi=Si+Ni|0;Zi=(Vi<<18|Vi>>>14)^ci;Qi=gi+ti|0;Ti=(Qi<<7|Qi>>>25)^Ii;Ji=Ti+gi|0;Yi=(Ji<<9|Ji>>>23)^Bi;Wi=Yi+Ti|0;Xi=(Wi<<13|Wi>>>19)^ti;$i=Xi+Yi|0;Aa=($i<<18|$i>>>14)^gi;ea=Ei+wi|0;ra=(ea<<7|ea>>>25)^Oi;na=ra+Ei|0;ta=(na<<9|na>>>23)^ai;ia=ta+ra|0;aa=(ia<<13|ia>>>19)^wi;oa=aa+ta|0;fa=(oa<<18|oa>>>14)^Ei;ua=Ci+mi|0;sa=(ua<<7|ua>>>25)^fi;la=sa+Ci|0;wa=(la<<9|la>>>23)^vi;ha=wa+sa|0;va=(ha<<13|ha>>>19)^mi;da=va+wa|0;ya=(da<<18|da>>>14)^Ci;pa=Zi+sa|0;ga=(pa<<7|pa>>>25)^Xi;ba=ga+Zi|0;ma=(ba<<9|ba>>>23)^ta;Ma=ma+ga|0;Ua=(Ma<<13|Ma>>>19)^sa;La=Ua+ma|0;Ia=(La<<18|La>>>14)^Zi;xa=Aa+qi|0;Ea=(xa<<7|xa>>>25)^aa;Da=Ea+Aa|0;Ka=(Da<<9|Da>>>23)^wa;Ra=Ka+Ea|0;Fa=(Ra<<13|Ra>>>19)^qi;Oa=Fa+Ka|0;_a=(Oa<<18|Oa>>>14)^Aa;Ca=fa+Ti|0;ja=(Ca<<7|Ca>>>25)^va;qa=ja+fa|0;Ga=(qa<<9|qa>>>23)^Ni;Na=Ga+ja|0;Pa=(Na<<13|Na>>>19)^Ti;Sa=Pa+Ga|0;Va=(Sa<<18|Sa>>>14)^fa;Za=ya+ra|0;Qa=(Za<<7|Za>>>25)^Si;Ta=Qa+ya|0;Ja=(Ta<<9|Ta>>>23)^Yi;Ya=Ja+Qa|0;za=(Ya<<13|Ya>>>19)^ra;Xa=za+Ja|0;$a=(Xa<<18|Xa>>>14)^ya;Ao=Ia+Qa|0;eo=(Ao<<7|Ao>>>25)^Fa;ro=eo+Ia|0;no=(ro<<9|ro>>>23)^Ga;to=no+eo|0;io=(to<<13|to>>>19)^Qa;ao=io+no|0;oo=_a+ga|0;uo=(oo<<7|oo>>>25)^Pa;co=uo+_a|0;so=(co<<9|co>>>23)^Ja;lo=so+uo|0;wo=(lo<<13|lo>>>19)^ga;ho=wo+so|0;vo=Va+Ea|0;yo=(vo<<7|vo>>>25)^za;po=yo+Va|0;go=(po<<9|po>>>23)^ma;bo=go+yo|0;Mo=(bo<<13|bo>>>19)^Ea;ko=Mo+go|0;Uo=$a+ja|0;Lo=(Uo<<7|Uo>>>25)^Ua;Io=Lo+$a|0;xo=(Io<<9|Io>>>23)^Ka;Eo=xo+Lo|0;Do=(Eo<<13|Eo>>>19)^ja;Ko=Do+xo|0;Ro=((ao<<18|ao>>>14)^Ia)+li|0;Fo=eo+Li|0;Oo=no+ji|0;_o=io+zi|0;Co=wo+ca|0;Ho=((ho<<18|ho>>>14)^_a)+ka|0;jo=uo+Ha|0;qo=so+Wa|0;Go=go+fo|0;No=Mo+mo|0;Po=((ko<<18|ko>>>14)^Va)+Bo|0;So=yo+Wo|0;Vo=Lo+ff|0;Zo=xo+gf|0;Qo=Do+Bf|0;To=((Ko<<18|Ko>>>14)^$a)+Zf|0;Jo=b<<3;Yo=e+(Jo<<2)|0;n[Yo>>2]=Ro;n[Yo+4>>2]=Fo;n[Yo+8>>2]=Oo;n[Yo+12>>2]=_o;n[Yo+16>>2]=Co;n[Yo+20>>2]=Ho;n[Yo+24>>2]=jo;n[Yo+28>>2]=qo;n[Yo+32>>2]=Go;n[Yo+36>>2]=No;n[Yo+40>>2]=Po;n[Yo+44>>2]=So;n[Yo+48>>2]=Vo;n[Yo+52>>2]=Zo;n[Yo+56>>2]=Qo;n[Yo+60>>2]=To;zo=A+((Xt|16)<<2)|0;Xo=Ro^n[zo>>2];$o=Fo^n[zo+4>>2];Af=Oo^n[zo+8>>2];ef=_o^n[zo+12>>2];rf=Co^n[zo+16>>2];nf=Ho^n[zo+20>>2];tf=jo^n[zo+24>>2];af=qo^n[zo+28>>2];of=Go^n[zo+32>>2];uf=No^n[zo+36>>2];cf=Po^n[zo+40>>2];sf=So^n[zo+44>>2];lf=Vo^n[zo+48>>2];wf=Zo^n[zo+52>>2];hf=Qo^n[zo+56>>2];vf=To^n[zo+60>>2];df=Xo+lf|0;yf=(df<<7|df>>>25)^rf;pf=yf+Xo|0;bf=(pf<<9|pf>>>23)^of;mf=bf+yf|0;Mf=(mf<<13|mf>>>19)^lf;kf=Mf+bf|0;Uf=(kf<<18|kf>>>14)^Xo;Lf=nf+$o|0;If=(Lf<<7|Lf>>>25)^uf;xf=If+nf|0;Ef=(xf<<9|xf>>>23)^wf;Df=Ef+If|0;Kf=(Df<<13|Df>>>19)^$o;Rf=Kf+Ef|0;Ff=(Rf<<18|Rf>>>14)^nf;Of=cf+tf|0;_f=(Of<<7|Of>>>25)^hf;Cf=_f+cf|0;Hf=(Cf<<9|Cf>>>23)^Af;jf=Hf+_f|0;qf=(jf<<13|jf>>>19)^tf;Gf=qf+Hf|0;Nf=(Gf<<18|Gf>>>14)^cf;Pf=vf+sf|0;Sf=(Pf<<7|Pf>>>25)^ef;Vf=Sf+vf|0;Qf=(Vf<<9|Vf>>>23)^af;Tf=Qf+Sf|0;Jf=(Tf<<13|Tf>>>19)^sf;Yf=Jf+Qf|0;zf=(Yf<<18|Yf>>>14)^vf;Xf=Uf+Sf|0;$f=(Xf<<7|Xf>>>25)^Kf;Au=$f+Uf|0;eu=(Au<<9|Au>>>23)^Hf;ru=eu+$f|0;nu=(ru<<13|ru>>>19)^Sf;tu=nu+eu|0;iu=(tu<<18|tu>>>14)^Uf;au=Ff+yf|0;ou=(au<<7|au>>>25)^qf;fu=ou+Ff|0;uu=(fu<<9|fu>>>23)^Qf;cu=uu+ou|0;su=(cu<<13|cu>>>19)^yf;lu=su+uu|0;hu=(lu<<18|lu>>>14)^Ff;vu=Nf+If|0;du=(vu<<7|vu>>>25)^Jf;yu=du+Nf|0;gu=(yu<<9|yu>>>23)^bf;bu=gu+du|0;mu=(bu<<13|bu>>>19)^If;Mu=mu+gu|0;ku=(Mu<<18|Mu>>>14)^Nf;Uu=zf+_f|0;Lu=(Uu<<7|Uu>>>25)^Mf;Iu=Lu+zf|0;xu=(Iu<<9|Iu>>>23)^Ef;Eu=xu+Lu|0;Du=(Eu<<13|Eu>>>19)^_f;Ku=Du+xu|0;Ru=(Ku<<18|Ku>>>14)^zf;Bu=iu+Lu|0;Fu=(Bu<<7|Bu>>>25)^su;Ou=Fu+iu|0;Cu=(Ou<<9|Ou>>>23)^gu;Hu=Cu+Fu|0;ju=(Hu<<13|Hu>>>19)^Lu;qu=ju+Cu|0;Nu=(qu<<18|qu>>>14)^iu;Pu=hu+$f|0;Su=(Pu<<7|Pu>>>25)^mu;Vu=Su+hu|0;m=(Vu<<9|Vu>>>23)^xu;M=m+Su|0;k=(M<<13|M>>>19)^$f;U=k+m|0;L=(U<<18|U>>>14)^hu;I=ku+ou|0;x=(I<<7|I>>>25)^Du;E=x+ku|0;D=(E<<9|E>>>23)^eu;K=D+x|0;R=(K<<13|K>>>19)^ou;B=R+D|0;O=(B<<18|B>>>14)^ku;_=Ru+du|0;C=(_<<7|_>>>25)^nu;H=C+Ru|0;q=(H<<9|H>>>23)^uu;G=q+C|0;N=(G<<13|G>>>19)^du;P=N+q|0;S=(P<<18|P>>>14)^Ru;V=Nu+C|0;Z=(V<<7|V>>>25)^k;Q=Z+Nu|0;T=(Q<<9|Q>>>23)^D;J=T+Z|0;Y=(J<<13|J>>>19)^C;W=Y+T|0;z=(W<<18|W>>>14)^Nu;X=L+Fu|0;$=(X<<7|X>>>25)^R;AA=$+L|0;rA=(AA<<9|AA>>>23)^q;nA=rA+$|0;tA=(nA<<13|nA>>>19)^Fu;iA=tA+rA|0;oA=(iA<<18|iA>>>14)^L;fA=O+Su|0;uA=(fA<<7|fA>>>25)^N;cA=uA+O|0;sA=(cA<<9|cA>>>23)^Cu;lA=sA+uA|0;wA=(lA<<13|lA>>>19)^Su;hA=wA+sA|0;vA=(hA<<18|hA>>>14)^O;dA=S+x|0;yA=(dA<<7|dA>>>25)^ju;pA=yA+S|0;gA=(pA<<9|pA>>>23)^m;bA=gA+yA|0;mA=(bA<<13|bA>>>19)^x;MA=mA+gA|0;UA=(MA<<18|MA>>>14)^S;LA=z+yA|0;IA=(LA<<7|LA>>>25)^tA;xA=IA+z|0;DA=(xA<<9|xA>>>23)^sA;KA=DA+IA|0;RA=(KA<<13|KA>>>19)^yA;BA=RA+DA|0;FA=(BA<<18|BA>>>14)^z;OA=oA+Z|0;_A=(OA<<7|OA>>>25)^wA;CA=_A+oA|0;HA=(CA<<9|CA>>>23)^gA;jA=HA+_A|0;qA=(jA<<13|jA>>>19)^Z;GA=qA+HA|0;NA=(GA<<18|GA>>>14)^oA;PA=vA+$|0;SA=(PA<<7|PA>>>25)^mA;VA=SA+vA|0;QA=(VA<<9|VA>>>23)^T;TA=QA+SA|0;JA=(TA<<13|TA>>>19)^$;YA=JA+QA|0;zA=(YA<<18|YA>>>14)^vA;XA=UA+uA|0;$A=(XA<<7|XA>>>25)^Y;Ae=$A+UA|0;ee=(Ae<<9|Ae>>>23)^rA;re=ee+$A|0;ne=(re<<13|re>>>19)^uA;te=ne+ee|0;ie=(te<<18|te>>>14)^UA;ae=FA+$A|0;oe=(ae<<7|ae>>>25)^qA;fe=oe+FA|0;ue=(fe<<9|fe>>>23)^QA;ce=ue+oe|0;se=(ce<<13|ce>>>19)^$A;le=se+ue|0;he=(le<<18|le>>>14)^FA;ve=NA+IA|0;de=(ve<<7|ve>>>25)^JA;ye=de+NA|0;ge=(ye<<9|ye>>>23)^ee;be=ge+de|0;me=(be<<13|be>>>19)^IA;Me=me+ge|0;ke=(Me<<18|Me>>>14)^NA;Ue=zA+_A|0;Le=(Ue<<7|Ue>>>25)^ne;Ie=Le+zA|0;xe=(Ie<<9|Ie>>>23)^DA;Ee=xe+Le|0;De=(Ee<<13|Ee>>>19)^_A;Ke=De+xe|0;Re=(Ke<<18|Ke>>>14)^zA;Be=ie+SA|0;Fe=(Be<<7|Be>>>25)^RA;Oe=Fe+ie|0;Ce=(Oe<<9|Oe>>>23)^HA;He=Ce+Fe|0;je=(He<<13|He>>>19)^SA;qe=je+Ce|0;Ne=(qe<<18|qe>>>14)^ie;Pe=he+Fe|0;Se=(Pe<<7|Pe>>>25)^me;Ve=Se+he|0;Ze=(Ve<<9|Ve>>>23)^xe;Qe=Ze+Se|0;Te=(Qe<<13|Qe>>>19)^Fe;Je=Te+Ze|0;Ye=(Je<<18|Je>>>14)^he;We=ke+oe|0;ze=(We<<7|We>>>25)^De;Xe=ze+ke|0;$e=(Xe<<9|Xe>>>23)^Ce;Ar=$e+ze|0;er=(Ar<<13|Ar>>>19)^oe;rr=er+$e|0;tr=(rr<<18|rr>>>14)^ke;ir=Re+de|0;ar=(ir<<7|ir>>>25)^je;or=ar+Re|0;ur=(or<<9|or>>>23)^ue;cr=ur+ar|0;sr=(cr<<13|cr>>>19)^de;lr=sr+ur|0;wr=(lr<<18|lr>>>14)^Re;hr=Ne+Le|0;vr=(hr<<7|hr>>>25)^se;dr=vr+Ne|0;yr=(dr<<9|dr>>>23)^ge;pr=yr+vr|0;gr=(pr<<13|pr>>>19)^Le;br=gr+yr|0;mr=(br<<18|br>>>14)^Ne;Mr=Ye+vr|0;kr=(Mr<<7|Mr>>>25)^er;Ur=kr+Ye|0;Ir=(Ur<<9|Ur>>>23)^ur;xr=Ir+kr|0;Er=(xr<<13|xr>>>19)^vr;Dr=Er+Ir|0;Rr=tr+Se|0;Br=(Rr<<7|Rr>>>25)^sr;Fr=Br+tr|0;Or=(Fr<<9|Fr>>>23)^yr;_r=Or+Br|0;Cr=(_r<<13|_r>>>19)^Se;Hr=Cr+Or|0;jr=wr+ze|0;qr=(jr<<7|jr>>>25)^gr;Gr=qr+wr|0;Nr=(Gr<<9|Gr>>>23)^Ze;Pr=Nr+qr|0;Sr=(Pr<<13|Pr>>>19)^ze;Vr=Sr+Nr|0;Qr=mr+ar|0;Tr=(Qr<<7|Qr>>>25)^Te;Jr=Tr+mr|0;Wr=(Jr<<9|Jr>>>23)^$e;zr=Wr+Tr|0;Xr=(zr<<13|zr>>>19)^ar;$r=Xr+Wr|0;t=((Dr<<18|Dr>>>14)^Ye)+Xo|0;i=kr+$o|0;a=Ir+Af|0;o=Er+ef|0;f=Cr+rf|0;u=((Hr<<18|Hr>>>14)^tr)+nf|0;c=Br+tf|0;s=Or+af|0;l=Nr+of|0;w=Sr+uf|0;h=((Vr<<18|Vr>>>14)^wr)+cf|0;v=qr+sf|0;d=Tr+lf|0;y=Wr+wf|0;p=Xr+hf|0;g=(($r<<18|$r>>>14)^mr)+vf|0;An=e+(Jo+Tt<<2)|0;n[An>>2]=t;n[An+4>>2]=i;n[An+8>>2]=a;n[An+12>>2]=o;n[An+16>>2]=f;n[An+20>>2]=u;n[An+24>>2]=c;n[An+28>>2]=s;n[An+32>>2]=l;n[An+36>>2]=w;n[An+40>>2]=h;n[An+44>>2]=v;n[An+48>>2]=d;n[An+52>>2]=y;n[An+56>>2]=p;n[An+60>>2]=g;b=b+2|0}while(b>>>0>>0);return}return{_SMix:o}};return{create:A,getHeap:e}}function e(){"use strict";function A(A){for(var e=new Uint8Array(A),n=atob("mC+KQpFEN3HP+8C1pdu16VvCVjnxEfFZpII/ktVeHKuYqgfYAVuDEr6FMSTDfQxVdF2+cv6x3oCnBtybdPGbwcFpm+SGR77vxp3BD8yhDCRvLOktqoR0StypsFzaiPl2UlE+mG3GMajIJwOwx39Zv/ML4MZHkafVUWPKBmcpKRSFCrcnOCEbLvxtLE0TDThTVHMKZbsKanYuycKBhSxykqHov6JLZhqocItLwqNRbMcZ6JLRJAaZ1oU1DvRwoGoQFsGkGQhsNx5Md0gntbywNLMMHDlKqthOT8qcW/NvLmjugo90b2OleBR4yIQIAseM+v++kOtsUKT3o/m+8nhxxgUAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAYAcAAAAEAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAr/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAQAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="),t=8,i=0;i>>0>64){n[191]=0;n[183]=1779033703;n[184]=-1150833019;n[185]=1013904242;n[186]=-1521486534;n[187]=1359893119;n[188]=-1694144372;n[189]=528734635;n[190]=1541459225;o(732,A,e);c(1816,732);l=1816;w=32}else{l=A;w=e}n[191]=0;n[183]=1779033703;n[184]=-1150833019;n[185]=1013904242;n[186]=-1521486534;n[187]=1359893119;n[188]=-1694144372;n[189]=528734635;n[190]=1541459225;g=1752;m=g+64|0;do{t[g>>0]=54;g=g+1|0}while((g|0)<(m|0));if(!w)h=1;else{t[1752]=t[l>>0]^54;if((w|0)==1)h=0;else{t[1753]=t[l+1>>0]^54;if((w|0)==2)h=0;else{t[1754]=t[l+2>>0]^54;if((w|0)==3)h=0;else{v=3;do{t[1752+v>>0]=t[l+v>>0]^t[1752+v>>0];v=v+1|0}while((v|0)!=(w|0));h=0}}}}o(732,1752,64);n[216]=0;n[208]=1779033703;n[209]=-1150833019;n[210]=1013904242;n[211]=-1521486534;n[212]=1359893119;n[213]=-1694144372;n[214]=528734635;n[215]=1541459225;g=1752;m=g+64|0;do{t[g>>0]=92;g=g+1|0}while((g|0)<(m|0));if(!h){t[1752]=t[l>>0]^92;if((w|0)!=1){t[1753]=t[l+1>>0]^92;if((w|0)!=2){t[1754]=t[l+2>>0]^92;if((w|0)!=3){d=3;do{t[1752+d>>0]=t[l+d>>0]^t[1752+d>>0];d=d+1|0}while((d|0)!=(w|0))}}}}o(832,1752,64);o(732,r,i);if(u|0){s=0;p=0;do{s=s+1|0;t[1687]=s;t[1686]=s>>>8;t[1685]=s>>>16;t[1684]=s>>>24;f(932,732,200)|0;o(932,1684,4);c(1848,932);o(1032,1848,32);c(1688,1032);g=1720;b=1688;m=g+32|0;do{t[g>>0]=t[b>>0]|0;g=g+1|0;b=b+1|0}while((g|0)<(m|0));y=u-p|0;f(a+p|0,1720,(y>>>0>32?32:y)|0)|0;p=s<<5}while(p>>>0>>0)}return}function o(A,e,r){A=A|0;e=e|0;r=r|0;var t=0,i=0,a=0,o=0,c=0,s=0,l=0,w=0,h=0,v=0,d=0,y=0,p=0,g=0,b=0;do if(r|0){p=A+32|0;g=n[p>>2]|0;b=g>>>3&63;n[p>>2]=g+(r<<3);c=64-b|0;s=A+36+b|0;if(c>>>0>r>>>0){f(s|0,e|0,r|0)|0;break}f(s|0,e|0,c|0)|0;l=A+36|0;u(A,l);w=e+c|0;h=r-c|0;if(h>>>0>63){v=b+r+-128|0;d=v&-64;y=d+128-b|0;a=h;o=w;while(1){u(A,o);a=a+-64|0;if(a>>>0<=63)break;else o=o+64|0}t=e+y|0;i=v-d|0}else{t=w;i=h}f(l|0,t|0,i|0)|0}while(0);return}function f(A,e,r){A=A|0;e=e|0;r=r|0;var i=0;i=A|0;if((A&3)==(e&3)){while(A&3){if(!r)return i|0;t[A>>0]=t[e>>0]|0;A=A+1|0;e=e+1|0;r=r-1|0}while((r|0)>=4){n[A>>2]=n[e>>2];A=A+4|0;e=e+4|0;r=r-4|0}}while((r|0)>0){t[A>>0]=t[e>>0]|0;A=A+1|0;e=e+1|0;r=r-1|0}return i|0}function u(A,e){A=A|0;e=e|0;var r=0,t=0,a=0,o=0,f=0,u=0,c=0,s=0,l=0,w=0,h=0,v=0,d=0,y=0,p=0,g=0,b=0;r=0;do{w=e+(r<<2)|0;n[444+(r<<2)>>2]=(i[w+2>>0]|0)<<8|(i[w+3>>0]|0)|(i[w+1>>0]|0)<<16|(i[w>>0]|0)<<24;r=r+1|0}while((r|0)!=16);t=16;h=n[111]|0;do{l=n[444+(t+-2<<2)>>2]|0;b=h;h=n[444+(t+-15<<2)>>2]|0;n[444+(t<<2)>>2]=b+(n[444+(t+-7<<2)>>2]|0)+((l>>>19|l<<13)^l>>>10^(l>>>17|l<<15))+((h>>>18|h<<14)^h>>>3^(h>>>7|h<<25));t=t+1|0}while((t|0)!=64);n[175]=n[A>>2];n[176]=n[A+4>>2];n[177]=n[A+8>>2];n[178]=n[A+12>>2];n[179]=n[A+16>>2];n[180]=n[A+20>>2];n[181]=n[A+24>>2];n[182]=n[A+28>>2];a=0;do{v=700+(((71-a|0)%8|0)<<2)|0;d=n[700+(((68-a|0)%8|0)<<2)>>2]|0;y=n[700+(((70-a|0)%8|0)<<2)>>2]|0;p=(n[444+(a<<2)>>2]|0)+(n[v>>2]|0)+((d>>>6|d<<26)^(d>>>11|d<<21)^(d>>>25|d<<7))+(n[8+(a<<2)>>2]|0)+((y^n[700+(((69-a|0)%8|0)<<2)>>2])&d^y)|0;g=n[700+(((64-a|0)%8|0)<<2)>>2]|0;f=n[700+(((65-a|0)%8|0)<<2)>>2]|0;u=n[700+(((66-a|0)%8|0)<<2)>>2]|0;c=700+(((67-a|0)%8|0)<<2)|0;n[c>>2]=(n[c>>2]|0)+p;n[v>>2]=((g>>>2|g<<30)^(g>>>13|g<<19)^(g>>>22|g<<10))+p+((u|f)&g|u&f);a=a+1|0}while((a|0)!=64);o=0;do{s=A+(o<<2)|0;n[s>>2]=(n[s>>2]|0)+(n[700+(o<<2)>>2]|0);o=o+1|0}while((o|0)!=8);return}function c(A,e){A=A|0;e=e|0;var r=0,i=0,a=0,f=0,u=0;u=n[e+32>>2]|0;t[1139]=u;t[1138]=u>>>8;t[1137]=u>>>16;t[1136]=u>>>24;i=u>>>3&63;n[283]=0;o(e,380,(i>>>0<56?56:120)-i|0);o(e,1132,8);r=0;do{a=A+(r<<2)|0;f=n[e+(r<<2)>>2]|0;t[a+3>>0]=f;t[a+2>>0]=f>>>8;t[a+1>>0]=f>>>16;t[a>>0]=f>>>24;r=r+1|0}while((r|0)!=8);return}return{_PBKDF2_OneIter:a}};return{create:A,getHeap:e}}var r,n=function(){"use strict";function e(){}function r(A,r){u=A,c=r,f=128*r,o=d+f;var n=d+f*(3+A),t=16777216*Math.ceil(n/16777216);if(!s||s.byteLength>1<<1}function i(){var A,e=h,r=w,n=r+e,i=0;switch(n>=u&&(n=u,e=n-r,i=v?1:2),2!=i&&g(e),0==i&&(A=b()),p._SMix(u,c,d,o,v?0:1,r,n),i){case 0:A=b()-A,w=n,t(100/A);break;case 1:w=0,v=!1,t(.7);break;case 2:var a=s.slice(d,d+f);g({state:"done",step:e,output:a},[a])}}function a(A){var e=A.data;if(e===!0)return void i();switch(e.cmd){case"task":n(e.input),i();break;case"config":var t=r(e.N,e.r);g({state:t?"ready":"fail"});break;case"free":p=l=s=null}}var o,f,u,c,s,l,w,h,v,d=64,y=A(),p=null,g=self.postMessage,b=Date.now;addEventListener("message",a)};!function(r){function t(A,e,r,n,t,i,a){L=128*e,h=r,E=r*A*2,I=n,K=0;var o=O.getHeap();p=o,o+=t,g=o,o+=i,b=o,o+=a,m=o,o+=L*r,o=65536*Math.ceil(o/65536),(!d||d.byteLength32?U:32);var A=new Uint8Array(d,b,U);x=!1,w("oncomplete",A)}function s(A){var e=this,r=A.data;if("number"==typeof r){if(!x)return;e.postMessage(!0),D+=r;var n=Date.now();return n-F>50&&w("onprogress",D/E),void(F=n)}switch(r.state){case"done":if(!x)return;var t=new Uint8Array(r.output),i=e.tag;y.set(t,m+L*i),D+=r.step,++B==h?c():R=18}function e(n){t.__flash_cb=u;var o=n+"flash.swf";m=l(o),r.hideDom(m)}function a(n,o,t){var e=r.bytesToHex(n),a=r.bytesToHex(o);m.hash(e,a,t)}function i(r,n,o,t,e,a,i){m.config.apply(m,arguments)}function c(){m.cancel()}function f(){m.free(),m=null}function s(){document.body.removeChild(m),m=null}function u(o,t){"oncomplete"==o&&(t=r.hexToBytes(t));try{n[o](t)}catch(e){throw e}}function l(r){var n=document.createElement("div"),o="_"+(1e6*Math.random()|0);r=encodeURI(r),n.innerHTML=p?"":"";var t=n.firstChild;return document.body.appendChild(t),t}function h(){var r=navigator.plugins;if(r){var n=r["Shockwave Flash"];if(n){var o=n.description;if(o)return+o.match(w)}}}function d(){var r=window.ActiveXObject;if(r){var n="";try{n=new r("ShockwaveFlash.ShockwaveFlash").GetVariable("$version").replace(",",".")}catch(o){return}return+n.match(w)}}function v(){var r=h();return r>0?r:(r=d(),r>0?(p=!0,r):0)}var m,p;n.check=o,n.load=e,n.hash=a,n.config=i,n.stop=c,n.free=f,n.unload=s;var w=/\d+\.\d+/}(o||(o={}));var t;!function(t){function e(){for(var r=f(),n={},o=0;oj)throw Error("pass.length > maxPassLen");if(n.length>A)throw Error("salt.length > maxSaltLen");if(o>B)throw Error("dkLen > maxDkLen");x.hash(r,n,o)}function f(){if(!E){E=[];for(var r in b)b[r].check()&&E.push(r)}return E}function s(r){if(!(T>=1)){if(!r&&(r=e(),!r))throw Error("no available mod");if(x=b[r],!x)throw Error("unsupported mod: "+r);x.onload=function(){i(),a(t.onload)},x.onerror=function(r){h(),a(t.onerror,r)},x.onready=function(){T=4,a(t.onready)},x.onprogress=function(r){a(t.onprogress,r)},x.oncomplete=function(r){T=4,a(t.onprogress,1),a(t.oncomplete,r)},i(),k=setTimeout(function(){h(),a(t.onerror,"load timeout")},L),T=1,x.load(S)}}function u(){x.stop(),T=4}function l(){4==T&&(x.free(),T=2)}function h(){0!=T&&(x.unload(),T=0),i()}function d(r,n,o){if(!r)throw Error("config() takes at least 1 argument");var t=0|r.N;if(!(11073741824)throw Error("memory limit exceeded (N * r * 128 > 1G)");if(n){var c=n.maxPassLen;if(null==c)c=j;else if(c<=0)throw Error("invalid maxPassLen");var f=n.maxSaltLen;if(null==f)f=A;else if(f<=0)throw Error("invalid maxSaltLen");var s=n.maxDkLen;if(null==s)s=B;else if(s<=0)throw Error("invalid maxDkLen");var u=n.maxThread;if(null==u)u=C;else if(u<=0)throw Error("invalid maxThread");o||(j=0|c,A=0|f,B=0|s,C=0|u)}if(!o){var l=Math.ceil(a/C),h=Math.ceil(a/l);x.config(t,e,a,h,j,A,B),T=3}}function v(n){return r.strToBytes(n)}function m(n){return r.bytesToStr(n)}function p(n){if(n.length%2)throw Error("invalid hex length");return r.hexToBytes(n)}function w(n){return r.bytesToHex(n)}function g(r){/\/$/.test(r)||(r+="/"),S=r}function y(r){L=r}var x,E,b={asmjs:n,flash:o},T=0,S="",k=0,L=3e4,j=64,A=64,B=64,C=1;t.hash=c,t.getAvailableMod=f,t.load=s,t.stop=u,t.free=l,t.unload=h,t.config=d,t.strToBin=v,t.binToStr=m,t.hexToBin=p,t.binToHex=w,t.setResPath=g,t.setResTimeout=y,window.scrypt=t}(t||(t={}))}();
2 | //# sourceMappingURL=sourcemap/scrypt.js.map
--------------------------------------------------------------------------------
/src/c/pbkdf2.c:
--------------------------------------------------------------------------------
1 | #define NDEBUG
2 | #include
3 | #include
4 | #include
5 | #include "sysendian.h"
6 |
7 |
8 | /* Elementary functions used by SHA256 */
9 | #define Ch(x, y, z) ((x & (y ^ z)) ^ z)
10 | #define Maj(x, y, z) ((x & (y | z)) | (y & z))
11 | #define SHR(x, n) (x >> n)
12 | #define ROTR(x, n) ((x >> n) | (x << (32 - n)))
13 | #define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
14 | #define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
15 | #define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
16 | #define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
17 |
18 | /* SHA256 round function */
19 | #define RND(a, b, c, d, e, f, g, h, k) \
20 | t0 = h + S1(e) + Ch(e, f, g) + k; \
21 | t1 = S0(a) + Maj(a, b, c); \
22 | d += t0; \
23 | h = t0 + t1;
24 |
25 | /* Adjusted round function for rotating state */
26 | #define RNDr(S, W, i, k) \
27 | RND(S[(64 - i) % 8], S[(65 - i) % 8], \
28 | S[(66 - i) % 8], S[(67 - i) % 8], \
29 | S[(68 - i) % 8], S[(69 - i) % 8], \
30 | S[(70 - i) % 8], S[(71 - i) % 8], \
31 | W[i] + k)
32 |
33 | /*
34 | * Encode a length len/4 vector of (uint32_t) into a length len vector of
35 | * (uint8_t) in big-endian form. Assumes len is a multiple of 4.
36 | */
37 | static void
38 | be32enc_vect(uint8_t * dst, const uint32_t * src, size_t len)
39 | {
40 | size_t i;
41 |
42 | /* Sanity-check. */
43 | assert(len % 4 == 0);
44 |
45 | /* Encode vector, one word at a time. */
46 | for (i = 0; i < len / 4; i++)
47 | be32enc(dst + i * 4, src[i]);
48 | }
49 |
50 | /*
51 | * Decode a big-endian length len vector of (uint8_t) into a length
52 | * len/4 vector of (uint32_t). Assumes len is a multiple of 4.
53 | */
54 | static void
55 | be32dec_vect(uint32_t * dst, const uint8_t * src, size_t len)
56 | {
57 | size_t i;
58 |
59 | /* Sanity-check. */
60 | assert(len % 4 == 0);
61 |
62 | /* Decode vector, one word at a time. */
63 | for (i = 0; i < len / 4; i++)
64 | dst[i] = be32dec(src + i * 4);
65 | }
66 |
67 | /* Context structure for SHA256 operations. */
68 | typedef struct {
69 | uint32_t state[8];
70 | // uint64_t count;
71 | uint32_t count;
72 | uint8_t buf[64];
73 | } SHA256_CTX;
74 |
75 | /* Context structure for HMAC-SHA256 operations. */
76 | typedef struct {
77 | SHA256_CTX ictx;
78 | SHA256_CTX octx;
79 | } HMAC_SHA256_CTX;
80 |
81 |
82 | static const uint32_t K[64] = {
83 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
84 | 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
85 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
86 | 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
87 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
88 | 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
89 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
90 | 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
91 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
92 | 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
93 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
94 | 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
95 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
96 | 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
97 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
98 | 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
99 | };
100 |
101 | /**
102 | * SHA256_Init(ctx):
103 | * Initialize the SHA256 context ${ctx}.
104 | */
105 | static void
106 | SHA256_Init(SHA256_CTX * ctx)
107 | {
108 | /* Zero bits processed so far. */
109 | ctx->count = 0;
110 |
111 | /* Initialize state. */
112 | ctx->state[0] = 0x6A09E667;
113 | ctx->state[1] = 0xBB67AE85;
114 | ctx->state[2] = 0x3C6EF372;
115 | ctx->state[3] = 0xA54FF53A;
116 | ctx->state[4] = 0x510E527F;
117 | ctx->state[5] = 0x9B05688C;
118 | ctx->state[6] = 0x1F83D9AB;
119 | ctx->state[7] = 0x5BE0CD19;
120 | }
121 |
122 | /*
123 | * SHA256 block compression function. The 256-bit state is transformed via
124 | * the 512-bit input block to produce a new state.
125 | */
126 | static void
127 | SHA256_Transform(uint32_t * state, const uint8_t block[64])
128 | {
129 | static uint32_t W[64];
130 | static uint32_t S[8];
131 | uint32_t t0, t1;
132 | int i;
133 |
134 | /* 1. Prepare message schedule W. */
135 | be32dec_vect(W, block, 64);
136 | for (i = 16; i < 64; i++)
137 | W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16];
138 |
139 | /* 2. Initialize working variables. */
140 | memcpy(S, state, 32);
141 |
142 | /* 3. Mix. */
143 | for (i = 0; i < 64; i++) {
144 | RNDr(S, W, i, K[i]);
145 | }
146 |
147 | /* 4. Mix local working variables into global state. */
148 | for (i = 0; i < 8; i++)
149 | state[i] += S[i];
150 |
151 | /* Clean the stack. */
152 | // insecure_memzero(W, 256);
153 | // insecure_memzero(S, 32);
154 | // insecure_memzero(&t0, sizeof(uint32_t));
155 | // insecure_memzero(&t1, sizeof(uint32_t));
156 | }
157 |
158 | /**
159 | * SHA256_Update(ctx, in, len):
160 | * Input ${len} bytes from ${in} into the SHA256 context ${ctx}.
161 | */
162 | void
163 | SHA256_Update(SHA256_CTX * ctx, const void * in, size_t len)
164 | {
165 | uint32_t r;
166 | const uint8_t * src = in;
167 |
168 | /* Return immediately if we have nothing to do. */
169 | if (len == 0)
170 | return;
171 |
172 | /* Number of bytes left in the buffer from previous updates. */
173 | r = (ctx->count >> 3) & 0x3f;
174 |
175 | /* Update number of bits. */
176 | ctx->count += (uint64_t)(len) << 3;
177 |
178 | /* Handle the case where we don't need to perform any transforms. */
179 | if (len < 64 - r) {
180 | memcpy(&ctx->buf[r], src, len);
181 | return;
182 | }
183 |
184 | /* Finish the current block. */
185 | memcpy(&ctx->buf[r], src, 64 - r);
186 | SHA256_Transform(ctx->state, ctx->buf);
187 | src += 64 - r;
188 | len -= 64 - r;
189 |
190 | /* Perform complete blocks. */
191 | while (len >= 64) {
192 | SHA256_Transform(ctx->state, src);
193 | src += 64;
194 | len -= 64;
195 | }
196 |
197 | /* Copy left over data into buffer. */
198 | memcpy(ctx->buf, src, len);
199 | }
200 |
201 |
202 | /* Add padding and terminating bit-count. */
203 | static uint8_t PAD[64] = {
204 | 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
205 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
206 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
208 | };
209 |
210 | static void
211 | SHA256_Pad(SHA256_CTX * ctx)
212 | {
213 | static uint8_t len[8];
214 | uint32_t r, plen;
215 |
216 | /*
217 | * Convert length to a vector of bytes -- we do this now rather
218 | * than later because the length will change after we pad.
219 | */
220 | // be64enc(len, ctx->count);
221 | be32enc(len + 4, ctx->count);
222 | be32enc(len, 0); // never used
223 |
224 | /* Add 1--64 bytes so that the resulting length is 56 mod 64. */
225 | r = (ctx->count >> 3) & 0x3f;
226 | plen = (r < 56) ? (56 - r) : (120 - r);
227 | SHA256_Update(ctx, PAD, (size_t)plen);
228 |
229 | /* Add the terminating bit-count. */
230 | SHA256_Update(ctx, len, 8);
231 | }
232 |
233 |
234 | /**
235 | * SHA256_Final(digest, ctx):
236 | * Output the SHA256 hash of the data input to the context ${ctx} into the
237 | * buffer ${digest}.
238 | */
239 | static void
240 | SHA256_Final(uint8_t digest[32], SHA256_CTX * ctx)
241 | {
242 | /* Add padding. */
243 | SHA256_Pad(ctx);
244 |
245 | /* Write the hash. */
246 | be32enc_vect(digest, ctx->state, 32);
247 | }
248 |
249 |
250 |
251 | /**
252 | * HMAC_SHA256_Init(ctx, K, Klen):
253 | * Initialize the HMAC-SHA256 context ${ctx} with ${Klen} bytes of key from
254 | * ${K}.
255 | */
256 | static void
257 | HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen)
258 | {
259 | static uint8_t pad[64];
260 | static uint8_t khash[32];
261 | const uint8_t * K = _K;
262 | size_t i;
263 |
264 | /* If Klen > 64, the key is really SHA256(K). */
265 | if (Klen > 64) {
266 | SHA256_Init(&ctx->ictx);
267 | SHA256_Update(&ctx->ictx, K, Klen);
268 | SHA256_Final(khash, &ctx->ictx);
269 | K = khash;
270 | Klen = 32;
271 | }
272 |
273 | /* Inner SHA256 operation is SHA256(K xor [block of 0x36] || data). */
274 | SHA256_Init(&ctx->ictx);
275 | memset(pad, 0x36, 64);
276 | for (i = 0; i < Klen; i++)
277 | pad[i] ^= K[i];
278 | SHA256_Update(&ctx->ictx, pad, 64);
279 |
280 | /* Outer SHA256 operation is SHA256(K xor [block of 0x5c] || hash). */
281 | SHA256_Init(&ctx->octx);
282 | memset(pad, 0x5c, 64);
283 | for (i = 0; i < Klen; i++)
284 | pad[i] ^= K[i];
285 | SHA256_Update(&ctx->octx, pad, 64);
286 |
287 | /* Clean the stack. */
288 | // insecure_memzero(khash, 32);
289 | // insecure_memzero(pad, 64);
290 | }
291 |
292 | /**
293 | * HMAC_SHA256_Update(ctx, in, len):
294 | * Input ${len} bytes from ${in} into the HMAC-SHA256 context ${ctx}.
295 | */
296 | static void
297 | HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void * in, size_t len)
298 | {
299 | /* Feed data to the inner SHA256 operation. */
300 | SHA256_Update(&ctx->ictx, in, len);
301 | }
302 |
303 | /**
304 | * HMAC_SHA256_Final(digest, ctx):
305 | * Output the HMAC-SHA256 of the data input to the context ${ctx} into the
306 | * buffer ${digest}.
307 | */
308 | static void
309 | HMAC_SHA256_Final(uint8_t digest[32], HMAC_SHA256_CTX * ctx)
310 | {
311 | static uint8_t ihash[32];
312 |
313 | /* Finish the inner SHA256 operation. */
314 | SHA256_Final(ihash, &ctx->ictx);
315 |
316 | /* Feed the inner hash to the outer SHA256 operation. */
317 | SHA256_Update(&ctx->octx, ihash, 32);
318 |
319 | /* Finish the outer SHA256 operation. */
320 | SHA256_Final(digest, &ctx->octx);
321 |
322 | /* Clean the stack. */
323 | // insecure_memzero(ihash, 32);
324 | }
325 |
326 | /**
327 | * HMAC_SHA256_Buf(K, Klen, in, len, digest):
328 | * Compute the HMAC-SHA256 of ${len} bytes from ${in} using the key ${K} of
329 | * length ${Klen}, and write the result to ${digest}.
330 | */
331 | static void
332 | HMAC_SHA256_Buf(const void * K, size_t Klen, const void * in, size_t len,
333 | uint8_t digest[32])
334 | {
335 | HMAC_SHA256_CTX ctx;
336 |
337 | HMAC_SHA256_Init(&ctx, K, Klen);
338 | HMAC_SHA256_Update(&ctx, in, len);
339 | HMAC_SHA256_Final(digest, &ctx);
340 | }
341 |
342 | /**
343 | * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen):
344 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and
345 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1).
346 | */
347 | static void
348 | PBKDF2_SHA256(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt,
349 | size_t saltlen, uint64_t c, uint8_t * buf, size_t dkLen)
350 | {
351 | static HMAC_SHA256_CTX PShctx, hctx;
352 | static uint8_t ivec[4];
353 | static uint8_t U[32];
354 | static uint8_t T[32];
355 | size_t i;
356 | uint64_t j;
357 | int k;
358 | size_t clen;
359 |
360 | /* Sanity-check. */
361 | assert(dkLen <= 32 * (size_t)(UINT32_MAX));
362 |
363 | /* Compute HMAC state after processing P and S. */
364 | HMAC_SHA256_Init(&PShctx, passwd, passwdlen);
365 | HMAC_SHA256_Update(&PShctx, salt, saltlen);
366 |
367 | /* Iterate through the blocks. */
368 | for (i = 0; i * 32 < dkLen; i++) {
369 | /* Generate INT(i + 1). */
370 | be32enc(ivec, (uint32_t)(i + 1));
371 |
372 | /* Compute U_1 = PRF(P, S || INT(i)). */
373 | memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX));
374 | HMAC_SHA256_Update(&hctx, ivec, 4);
375 | HMAC_SHA256_Final(U, &hctx);
376 |
377 | /* T_i = U_1 ... */
378 | memcpy(T, U, 32);
379 |
380 | for (j = 2; j <= c; j++) {
381 | /* Compute U_j. */
382 | HMAC_SHA256_Init(&hctx, passwd, passwdlen);
383 | HMAC_SHA256_Update(&hctx, U, 32);
384 | HMAC_SHA256_Final(U, &hctx);
385 |
386 | /* ... xor U_j ... */
387 | for (k = 0; k < 32; k++)
388 | T[k] ^= U[k];
389 | }
390 |
391 | /* Copy as many bytes as necessary into buf. */
392 | clen = dkLen - i * 32;
393 | if (clen > 32)
394 | clen = 32;
395 | memcpy(&buf[i * 32], T, clen);
396 | }
397 |
398 | /* Clean PShctx, since we never called _Final on it. */
399 | // insecure_memzero(&PShctx, sizeof(HMAC_SHA256_CTX));
400 | }
401 |
402 |
403 | void PBKDF2_OneIter(
404 | const uint8_t *passBuf, const size_t passLen,
405 | const uint8_t *saltBuf, const size_t saltLen,
406 | uint8_t *dkBuf, const size_t dkLen
407 | ) {
408 | PBKDF2_SHA256(
409 | passBuf, passLen,
410 | saltBuf, saltLen,
411 | 1,
412 | dkBuf, dkLen
413 | );
414 | }
415 |
--------------------------------------------------------------------------------
/src/c/pbkdf2.h:
--------------------------------------------------------------------------------
1 | void PBKDF2_OneIter(
2 | const uint8_t *passBuf, const size_t passLen,
3 | const uint8_t *saltBuf, const size_t saltLen,
4 | uint8_t *dkBuf, const size_t dkLen
5 | );
6 |
--------------------------------------------------------------------------------
/src/c/smix.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "sysendian.h"
4 |
5 |
6 | static void
7 | blkcpy(void * dest, const void * src, size_t len)
8 | {
9 | size_t * D = (size_t *) dest;
10 | const size_t * S = (const size_t *) src;
11 | size_t L = len / sizeof(size_t);
12 | size_t i;
13 |
14 | for (i = 0; i < L; i++)
15 | D[i] = S[i];
16 | }
17 |
18 |
19 | static void
20 | blkxor(void * dest, const void * src, size_t len)
21 | {
22 | size_t * D = (size_t *) dest;
23 | const size_t * S = (const size_t *) src;
24 | size_t L = len / sizeof(size_t);
25 | size_t i;
26 |
27 | for (i = 0; i < L; i++)
28 | D[i] ^= S[i];
29 | }
30 |
31 |
32 | static void
33 | blockmix_salsa8(const uint32_t * Bin, uint32_t * Bout, const uint32_t r)
34 | {
35 | uint32_t T00, T01, T02, T03,
36 | T04, T05, T06, T07,
37 | T08, T09, T10, T11,
38 | T12, T13, T14, T15;
39 |
40 | // salsa20_8
41 | uint32_t x00, x01, x02, x03,
42 | x04, x05, x06, x07,
43 | x08, x09, x10, x11,
44 | x12, x13, x14, x15;
45 |
46 | #define R(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
47 |
48 | #define ARX() \
49 | x04 ^= R(x00+x12, 7); x08 ^= R(x04+x00, 9); \
50 | x12 ^= R(x08+x04,13); x00 ^= R(x12+x08,18); \
51 | x09 ^= R(x05+x01, 7); x13 ^= R(x09+x05, 9); \
52 | x01 ^= R(x13+x09,13); x05 ^= R(x01+x13,18); \
53 | x14 ^= R(x10+x06, 7); x02 ^= R(x14+x10, 9); \
54 | x06 ^= R(x02+x14,13); x10 ^= R(x06+x02,18); \
55 | x03 ^= R(x15+x11, 7); x07 ^= R(x03+x15, 9); \
56 | x11 ^= R(x07+x03,13); x15 ^= R(x11+x07,18); \
57 | x01 ^= R(x00+x03, 7); x02 ^= R(x01+x00, 9); \
58 | x03 ^= R(x02+x01,13); x00 ^= R(x03+x02,18); \
59 | x06 ^= R(x05+x04, 7); x07 ^= R(x06+x05, 9); \
60 | x04 ^= R(x07+x06,13); x05 ^= R(x04+x07,18); \
61 | x11 ^= R(x10+x09, 7); x08 ^= R(x11+x10, 9); \
62 | x09 ^= R(x08+x11,13); x10 ^= R(x09+x08,18); \
63 | x12 ^= R(x15+x14, 7); x13 ^= R(x12+x15, 9); \
64 | x14 ^= R(x13+x12,13); x15 ^= R(x14+x13,18); \
65 |
66 | #define SALAS20() \
67 | x00 = T00; x01 = T01; x02 = T02; x03 = T03; \
68 | x04 = T04; x05 = T05; x06 = T06; x07 = T07; \
69 | x08 = T08; x09 = T09; x10 = T10; x11 = T11; \
70 | x12 = T12; x13 = T13; x14 = T14; x15 = T15; \
71 | ARX(); ARX(); ARX(); ARX(); \
72 | T00 += x00; T01 += x01; T02 += x02; T03 += x03; \
73 | T04 += x04; T05 += x05; T06 += x06; T07 += x07; \
74 | T08 += x08; T09 += x09; T10 += x10; T11 += x11; \
75 | T12 += x12; T13 += x13; T14 += x14; T15 += x15; \
76 |
77 |
78 | const uint32_t *ptr1;
79 | const uint32_t *ptr2;
80 | const uint32_t *ptr4;
81 | uint32_t *ptr3;
82 | uint32_t *ptr5;
83 |
84 | size_t i;
85 |
86 | /* 1: X <-- B_{2r - 1} */
87 | // blkcpy(X, &Bin[(2 * r - 1) * 16], 64);
88 |
89 | ptr1 = &Bin[(2 * r - 1) * 16];
90 |
91 | T00 = ptr1[ 0]; T01 = ptr1[ 1];
92 | T02 = ptr1[ 2]; T03 = ptr1[ 3];
93 | T04 = ptr1[ 4]; T05 = ptr1[ 5];
94 | T06 = ptr1[ 6]; T07 = ptr1[ 7];
95 | T08 = ptr1[ 8]; T09 = ptr1[ 9];
96 | T10 = ptr1[10]; T11 = ptr1[11];
97 | T12 = ptr1[12]; T13 = ptr1[13];
98 | T14 = ptr1[14]; T15 = ptr1[15];
99 |
100 |
101 | /* 2: for i = 0 to 2r - 1 do */
102 | for (i = 0; i < 2 * r; i += 2) {
103 | /* 3: X <-- H(X \xor B_i) */
104 | // blkxor(X, &Bin[i * 16], 64);
105 |
106 | ptr2 = &Bin[i * 16];
107 |
108 | T00 ^= ptr2[ 0]; T01 ^= ptr2[ 1];
109 | T02 ^= ptr2[ 2]; T03 ^= ptr2[ 3];
110 | T04 ^= ptr2[ 4]; T05 ^= ptr2[ 5];
111 | T06 ^= ptr2[ 6]; T07 ^= ptr2[ 7];
112 | T08 ^= ptr2[ 8]; T09 ^= ptr2[ 9];
113 | T10 ^= ptr2[10]; T11 ^= ptr2[11];
114 | T12 ^= ptr2[12]; T13 ^= ptr2[13];
115 | T14 ^= ptr2[14]; T15 ^= ptr2[15];
116 |
117 | // salsa20_8(X);
118 | SALAS20()
119 |
120 | /* 4: Y_i <-- X */
121 | /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
122 | // blkcpy(&Bout[i * 8], X, 64);
123 |
124 | ptr3 = &Bout[i * 8];
125 |
126 | ptr3[ 0] = T00; ptr3[ 1] = T01;
127 | ptr3[ 2] = T02; ptr3[ 3] = T03;
128 | ptr3[ 4] = T04; ptr3[ 5] = T05;
129 | ptr3[ 6] = T06; ptr3[ 7] = T07;
130 | ptr3[ 8] = T08; ptr3[ 9] = T09;
131 | ptr3[10] = T10; ptr3[11] = T11;
132 | ptr3[12] = T12; ptr3[13] = T13;
133 | ptr3[14] = T14; ptr3[15] = T15;
134 |
135 |
136 | /* 3: X <-- H(X \xor B_i) */
137 | // blkxor(X, &Bin[i * 16 + 16], 64);
138 |
139 | ptr4 = &Bin[i * 16 + 16];
140 |
141 | T00 ^= ptr4[ 0]; T01 ^= ptr4[ 1];
142 | T02 ^= ptr4[ 2]; T03 ^= ptr4[ 3];
143 | T04 ^= ptr4[ 4]; T05 ^= ptr4[ 5];
144 | T06 ^= ptr4[ 6]; T07 ^= ptr4[ 7];
145 | T08 ^= ptr4[ 8]; T09 ^= ptr4[ 9];
146 | T10 ^= ptr4[10]; T11 ^= ptr4[11];
147 | T12 ^= ptr4[12]; T13 ^= ptr4[13];
148 | T14 ^= ptr4[14]; T15 ^= ptr4[15];
149 |
150 | // salsa20_8(X);
151 | SALAS20()
152 |
153 | /* 4: Y_i <-- X */
154 | /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
155 | // blkcpy(&Bout[i * 8 + r * 16], X, 64);
156 | ptr5 = &Bout[i * 8 + r * 16];
157 |
158 | ptr5[ 0] = T00; ptr5[ 1] = T01;
159 | ptr5[ 2] = T02; ptr5[ 3] = T03;
160 | ptr5[ 4] = T04; ptr5[ 5] = T05;
161 | ptr5[ 6] = T06; ptr5[ 7] = T07;
162 | ptr5[ 8] = T08; ptr5[ 9] = T09;
163 | ptr5[10] = T10; ptr5[11] = T11;
164 | ptr5[12] = T12; ptr5[13] = T13;
165 | ptr5[14] = T14; ptr5[15] = T15;
166 | }
167 |
168 | #undef R
169 | }
170 |
171 |
172 | /**
173 | * integerify(B, r):
174 | * Return the result of parsing B_{2r-1} as a little-endian integer.
175 | */
176 | // static uint64_t
177 | static uint32_t
178 | integerify(const void *B, const uint32_t r)
179 | {
180 | const uint32_t *T = (const void *) (B + (2 * r - 1) * 64);
181 |
182 | // return (((uint64_t)(T[1]) << 32) + T[0]);
183 | return T[0];
184 | }
185 |
186 |
187 | // void
188 | // SMix(const uint32_t N, const uint32_t r, uint8_t *B, uint8_t *XYV)
189 | // {
190 | // const size_t BLOCK_SIZE = r * 128;
191 |
192 | // uint32_t *X = (uint32_t *) (XYV + BLOCK_SIZE * 0);
193 | // uint32_t *Y = (uint32_t *) (XYV + BLOCK_SIZE * 1);
194 | // uint32_t *V = (uint32_t *) (XYV + BLOCK_SIZE * 2);
195 |
196 | // size_t i, j, k;
197 |
198 |
199 | // /* 1: X <-- B */
200 | // for (k = 0; k < 32 * r; k++)
201 | // X[k] = le32dec(&B[4 * k]);
202 |
203 | // /* 2: for i = 0 to N - 1 do */
204 | // for (i = 0; i < N; i += 2) {
205 | // /* 3: V_i <-- X */
206 | // blkcpy(&V[i * (32 * r)], X, BLOCK_SIZE);
207 |
208 | // /* 4: X <-- H(X) */
209 | // blockmix_salsa8(X, Y, r);
210 |
211 | // /* 3: V_i <-- X */
212 | // blkcpy(&V[(i + 1) * (32 * r)], Y, BLOCK_SIZE);
213 |
214 | // /* 4: X <-- H(X) */
215 | // blockmix_salsa8(Y, X, r);
216 | // }
217 |
218 | // /* 6: for i = 0 to N - 1 do */
219 | // for (i = 0; i < N; i += 2) {
220 | // /* 7: j <-- Integerify(X) mod N */
221 | // j = integerify(X, r) & (N - 1);
222 |
223 | // /* 8: X <-- H(X \xor V_j) */
224 | // blkxor(X, &V[j * (32 * r)], BLOCK_SIZE);
225 | // blockmix_salsa8(X, Y, r);
226 |
227 | // /* 7: j <-- Integerify(X) mod N */
228 | // j = integerify(Y, r) & (N - 1);
229 |
230 | // /* 8: X <-- H(X \xor V_j) */
231 | // blkxor(Y, &V[j * (32 * r)], BLOCK_SIZE);
232 | // blockmix_salsa8(Y, X, r);
233 | // }
234 |
235 | // /* 10: B' <-- X */
236 | // for (k = 0; k < 32 * r; k++)
237 | // le32enc(&B[4 * k], X[k]);
238 | // }
239 |
240 |
241 |
242 | void SMix(
243 | const uint32_t N, const uint32_t r, uint8_t *B, uint8_t *XYV,
244 | const uint32_t stage, const uint32_t beg, const uint32_t end
245 | ) {
246 | const uint32_t BLOCK_SIZE = r * 128;
247 | const uint32_t R32 = r * 32;
248 |
249 | uint32_t *X = (uint32_t *) (XYV + BLOCK_SIZE * 0);
250 | uint32_t *Y = (uint32_t *) (XYV + BLOCK_SIZE * 1);
251 | uint32_t *V = (uint32_t *) (XYV + BLOCK_SIZE * 2);
252 |
253 | size_t i, j, k;
254 |
255 |
256 | if (stage == 0) {
257 | if (beg == 0) {
258 | /* 1: X <-- B */
259 | for (k = 0; k < R32; k++) {
260 | X[k] = le32dec(&B[4 * k]);
261 | }
262 | }
263 |
264 | /* 2: for i = 0 to N - 1 do */
265 | // for (i = beg; i < end; i += 2) {
266 | i = beg * R32;
267 | size_t e = end * R32;
268 |
269 | while (i < e) {
270 | /* 3: V_i <-- X */
271 | // blkcpy(&V[i * R32], X, BLOCK_SIZE);
272 | blkcpy(&V[i], X, BLOCK_SIZE);
273 | i += R32;
274 |
275 | /* 4: X <-- H(X) */
276 | blockmix_salsa8(X, Y, r);
277 |
278 | /* 3: V_i <-- X */
279 | blkcpy(&V[i], Y, BLOCK_SIZE);
280 | i += R32;
281 |
282 | /* 4: X <-- H(X) */
283 | blockmix_salsa8(Y, X, r);
284 | }
285 | } else {
286 | /* 6: for i = 0 to N - 1 do */
287 | for (i = beg; i < end; i += 2) {
288 | /* 7: j <-- Integerify(X) mod N */
289 | j = integerify(X, r) & (N - 1);
290 |
291 | /* 8: X <-- H(X \xor V_j) */
292 | blkxor(X, &V[j * R32], BLOCK_SIZE);
293 | blockmix_salsa8(X, Y, r);
294 |
295 | /* 7: j <-- Integerify(X) mod N */
296 | j = integerify(Y, r) & (N - 1);
297 |
298 | /* 8: X <-- H(X \xor V_j) */
299 | blkxor(Y, &V[j * R32], BLOCK_SIZE);
300 | blockmix_salsa8(Y, X, r);
301 | }
302 |
303 | if (end == N) {
304 | /* 10: B' <-- X */
305 | for (k = 0; k < R32; k++) {
306 | le32enc(&B[4 * k], X[k]);
307 | }
308 | }
309 | }
310 | }
--------------------------------------------------------------------------------
/src/c/smix.h:
--------------------------------------------------------------------------------
1 | void SMix(
2 | const uint32_t N, const uint32_t r, uint8_t *B, uint8_t *XYV,
3 | const uint32_t stage, const uint32_t beg, const uint32_t end
4 | );
5 |
--------------------------------------------------------------------------------
/src/c/sysendian.h:
--------------------------------------------------------------------------------
1 | #ifndef _SYSENDIAN_H_
2 | #define _SYSENDIAN_H_
3 |
4 | #include
5 |
6 |
7 |
8 | static inline uint16_t
9 | be16dec(const void * pp)
10 | {
11 | const uint8_t * p = (uint8_t const *)pp;
12 |
13 | return ((uint16_t)(p[1]) + ((uint16_t)(p[0]) << 8));
14 | }
15 |
16 | static inline void
17 | be16enc(void * pp, uint16_t x)
18 | {
19 | uint8_t * p = (uint8_t *)pp;
20 |
21 | p[1] = x & 0xff;
22 | p[0] = (x >> 8) & 0xff;
23 | }
24 |
25 | static inline uint32_t
26 | be32dec(const void * pp)
27 | {
28 | const uint8_t * p = (uint8_t const *)pp;
29 |
30 | return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) +
31 | ((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24));
32 | }
33 |
34 | static inline void
35 | be32enc(void * pp, uint32_t x)
36 | {
37 | uint8_t * p = (uint8_t *)pp;
38 |
39 | p[3] = x & 0xff;
40 | p[2] = (x >> 8) & 0xff;
41 | p[1] = (x >> 16) & 0xff;
42 | p[0] = (x >> 24) & 0xff;
43 | }
44 |
45 | static inline uint64_t
46 | be64dec(const void * pp)
47 | {
48 | const uint8_t * p = (uint8_t const *)pp;
49 |
50 | return ((uint64_t)(p[7]) + ((uint64_t)(p[6]) << 8) +
51 | ((uint64_t)(p[5]) << 16) + ((uint64_t)(p[4]) << 24) +
52 | ((uint64_t)(p[3]) << 32) + ((uint64_t)(p[2]) << 40) +
53 | ((uint64_t)(p[1]) << 48) + ((uint64_t)(p[0]) << 56));
54 | }
55 |
56 | static inline void
57 | be64enc(void * pp, uint64_t x)
58 | {
59 | uint8_t * p = (uint8_t *)pp;
60 |
61 | p[7] = x & 0xff;
62 | p[6] = (x >> 8) & 0xff;
63 | p[5] = (x >> 16) & 0xff;
64 | p[4] = (x >> 24) & 0xff;
65 | p[3] = (x >> 32) & 0xff;
66 | p[2] = (x >> 40) & 0xff;
67 | p[1] = (x >> 48) & 0xff;
68 | p[0] = (x >> 56) & 0xff;
69 | }
70 |
71 | static inline uint16_t
72 | le16dec(const void * pp)
73 | {
74 | const uint8_t * p = (uint8_t const *)pp;
75 |
76 | return ((uint16_t)(p[0]) + ((uint16_t)(p[1]) << 8));
77 | }
78 |
79 | static inline void
80 | le16enc(void * pp, uint16_t x)
81 | {
82 | uint8_t * p = (uint8_t *)pp;
83 |
84 | p[0] = x & 0xff;
85 | p[1] = (x >> 8) & 0xff;
86 | }
87 |
88 | static inline uint32_t
89 | le32dec(const void * pp)
90 | {
91 | const uint8_t * p = (uint8_t const *)pp;
92 |
93 | return ((uint32_t)(p[0]) + ((uint32_t)(p[1]) << 8) +
94 | ((uint32_t)(p[2]) << 16) + ((uint32_t)(p[3]) << 24));
95 | }
96 |
97 | static inline void
98 | le32enc(void * pp, uint32_t x)
99 | {
100 | uint8_t * p = (uint8_t *)pp;
101 |
102 | p[0] = x & 0xff;
103 | p[1] = (x >> 8) & 0xff;
104 | p[2] = (x >> 16) & 0xff;
105 | p[3] = (x >> 24) & 0xff;
106 | }
107 |
108 | static inline uint64_t
109 | le64dec(const void * pp)
110 | {
111 | const uint8_t * p = (uint8_t const *)pp;
112 |
113 | return ((uint64_t)(p[0]) + ((uint64_t)(p[1]) << 8) +
114 | ((uint64_t)(p[2]) << 16) + ((uint64_t)(p[3]) << 24) +
115 | ((uint64_t)(p[4]) << 32) + ((uint64_t)(p[5]) << 40) +
116 | ((uint64_t)(p[6]) << 48) + ((uint64_t)(p[7]) << 56));
117 | }
118 |
119 | static inline void
120 | le64enc(void * pp, uint64_t x)
121 | {
122 | uint8_t * p = (uint8_t *)pp;
123 |
124 | p[0] = x & 0xff;
125 | p[1] = (x >> 8) & 0xff;
126 | p[2] = (x >> 16) & 0xff;
127 | p[3] = (x >> 24) & 0xff;
128 | p[4] = (x >> 32) & 0xff;
129 | p[5] = (x >> 40) & 0xff;
130 | p[6] = (x >> 48) & 0xff;
131 | p[7] = (x >> 56) & 0xff;
132 | }
133 |
134 | #endif /* !_SYSENDIAN_H_ */
135 |
--------------------------------------------------------------------------------
/src/main/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "0.1.0",
5 | "command": "tsc",
6 | "isShellCommand": true,
7 | "args": ["-p", "."],
8 | "showOutput": "silent",
9 | "problemMatcher": "$tsc"
10 | }
--------------------------------------------------------------------------------
/src/main/Makefile:
--------------------------------------------------------------------------------
1 |
2 | JS = scrypt.js
3 | MAP = ${JS}.map
4 |
5 | .PHONY: build
6 | build: debug/${JS}
7 | mkdir -p release
8 |
9 | uglifyjs \
10 | "$<" \
11 | --output "release/${JS}" \
12 | --define _RELEASE=1 \
13 | --enclose \
14 | --support-ie8 \
15 | --mangle \
16 | --compress drop_console \
17 | --in-source-map "debug/${MAP}" \
18 | --source-map "release/${MAP}" \
19 | --source-map-include-sources \
20 | --source-map-url "sourcemap/${MAP}"
21 |
22 | cp "release/${JS}" "../../release/${JS}"
23 | cp "release/${MAP}" "../../release/sourcemap/${MAP}"
24 |
25 |
26 | debug/${JS}:
27 | tsc --project .
28 |
29 |
30 | .PHONY: clean
31 | clean:
32 | rm -rf ./debug
33 | rm -rf ./release
34 |
--------------------------------------------------------------------------------
/src/main/debug/scrypt.js:
--------------------------------------------------------------------------------
1 | /** @const */
2 | var _DEBUG = (typeof _RELEASE == 'undefined');
3 | var Util;
4 | (function (Util) {
5 | var _Uint8Array = window['Uint8Array'];
6 | function allocBuf(size) {
7 | return _Uint8Array ? new _Uint8Array(size) : new Array(size);
8 | }
9 | function createBuf(buf) {
10 | return _Uint8Array ? new _Uint8Array(buf) : buf;
11 | }
12 | function hexToBytes(inStr) {
13 | var outLen = inStr.length / 2;
14 | var outBuf = allocBuf(outLen);
15 | for (var i = 0; i < outLen; i++) {
16 | var byte = parseInt(inStr.substr(i * 2, 2), 16);
17 | if (isNaN(byte)) {
18 | throw Error('invalid hex data');
19 | }
20 | outBuf[i] = byte;
21 | }
22 | return outBuf;
23 | }
24 | Util.hexToBytes = hexToBytes;
25 | function bytesToHex(inBuf) {
26 | var inLen = inBuf.length;
27 | var outStr = '';
28 | for (var i = 0; i < inLen; i++) {
29 | var byte = inBuf[i] & 0xff;
30 | var hex = byte.toString(16);
31 | if (byte < 16) {
32 | hex = '0' + hex;
33 | }
34 | outStr += hex;
35 | }
36 | return outStr;
37 | }
38 | Util.bytesToHex = bytesToHex;
39 | function strToBytes(inStr) {
40 | var _TextEncoder = window['TextEncoder'];
41 | if (_TextEncoder) {
42 | return new _TextEncoder().encode(inStr);
43 | }
44 | var outBuf = [], i = 0, j = 0, s = encodeURI(inStr), n = s.length;
45 | while (i < n) {
46 | var ch = s.charCodeAt(i);
47 | if (ch == 37) {
48 | var hex = s.substr(i + 1, 2);
49 | ch = parseInt(hex, 16);
50 | i += 3;
51 | }
52 | else {
53 | i++;
54 | }
55 | outBuf[j++] = ch;
56 | }
57 | return createBuf(outBuf);
58 | }
59 | Util.strToBytes = strToBytes;
60 | function bytesToStr(inBuf) {
61 | var inLen = inBuf.length;
62 | var outStr = '';
63 | for (var i = 0; i < inLen; i++) {
64 | var byte = inBuf[i];
65 | var hex = byte.toString(16);
66 | if (byte < 16) {
67 | hex = '0' + hex;
68 | }
69 | outStr += ('%' + hex);
70 | }
71 | return decodeURIComponent(outStr);
72 | }
73 | Util.bytesToStr = bytesToStr;
74 | function hideDom(el) {
75 | el.style.cssText = 'position:absolute;top:-999px';
76 | }
77 | Util.hideDom = hideDom;
78 | })(Util || (Util = {}));
79 | ///
80 | ///
81 | var ModAsmJs;
82 | (function (ModAsmJs) {
83 | var mAsmMod;
84 | function check() {
85 | return 'Worker' in window;
86 | }
87 | ModAsmJs.check = check;
88 | function load(path) {
89 | Scrypt['__asmjs_cb'] = callbackHandler;
90 | var url = path + 'asmjs.js';
91 | if (_DEBUG) {
92 | url = '/src/mod_asmjs/debug/asmjs.js';
93 | }
94 | var spt = document.createElement('script');
95 | spt.onerror = function () {
96 | ModAsmJs.onerror('script load fail');
97 | };
98 | spt.src = url;
99 | document.body.appendChild(spt);
100 | }
101 | ModAsmJs.load = load;
102 | function hash(pass, salt, dkLen) {
103 | mAsmMod.hash(pass, salt, dkLen);
104 | }
105 | ModAsmJs.hash = hash;
106 | function config(N, r, P, thread, maxPassLen, maxSaltLen, maxDkLen) {
107 | mAsmMod.config.apply(this, arguments);
108 | }
109 | ModAsmJs.config = config;
110 | function stop() {
111 | mAsmMod.stop();
112 | }
113 | ModAsmJs.stop = stop;
114 | function free() {
115 | mAsmMod.free();
116 | }
117 | ModAsmJs.free = free;
118 | function unload() {
119 | if (mAsmMod) {
120 | mAsmMod.unload();
121 | mAsmMod = null;
122 | }
123 | }
124 | ModAsmJs.unload = unload;
125 | function callbackHandler(msg, data) {
126 | if (msg == 'onload') {
127 | mAsmMod = data;
128 | }
129 | ModAsmJs[msg](data);
130 | }
131 | })(ModAsmJs || (ModAsmJs = {}));
132 | ///
133 | ///
134 | var ModFlash;
135 | (function (ModFlash) {
136 | var mSwf;
137 | var mActiveX;
138 | function check() {
139 | return getVer() >= 18;
140 | }
141 | ModFlash.check = check;
142 | function load(path) {
143 | Scrypt['__flash_cb'] = callbackHandler;
144 | var url = path + 'flash.swf';
145 | if (_DEBUG) {
146 | url = '/src/mod_flash/bin-debug/Scrypt.swf';
147 | }
148 | mSwf = createSwf(url);
149 | Util.hideDom(mSwf);
150 | }
151 | ModFlash.load = load;
152 | function hash(pass, salt, dkLen) {
153 | var passHex = Util.bytesToHex(pass);
154 | var saltHex = Util.bytesToHex(salt);
155 | mSwf['hash'](passHex, saltHex, dkLen);
156 | }
157 | ModFlash.hash = hash;
158 | function config(N, r, P, thread, maxPassLen, maxSaltLen, maxDkLen) {
159 | mSwf['config'].apply(mSwf, arguments);
160 | }
161 | ModFlash.config = config;
162 | function stop() {
163 | mSwf['cancel']();
164 | }
165 | ModFlash.stop = stop;
166 | function free() {
167 | mSwf['free']();
168 | mSwf = null;
169 | }
170 | ModFlash.free = free;
171 | function unload() {
172 | document.body.removeChild(mSwf);
173 | mSwf = null;
174 | }
175 | ModFlash.unload = unload;
176 | function callbackHandler(msg, data) {
177 | if (msg == 'oncomplete') {
178 | data = Util.hexToBytes(data);
179 | }
180 | try {
181 | ModFlash[msg](data);
182 | }
183 | catch (err) {
184 | throw err;
185 | }
186 | }
187 | function createSwf(url) {
188 | var box = document.createElement('div');
189 | var id = '_' + (Math.random() * 1e6 | 0);
190 | url = encodeURI(url);
191 | box.innerHTML = mActiveX
192 | ? ""
193 | : "";
194 | var swf = box.firstChild;
195 | document.body.appendChild(swf);
196 | return swf;
197 | }
198 | var R_VER = /\d+\.\d+/;
199 | function getPluginVer() {
200 | var plugins = navigator.plugins;
201 | if (!plugins) {
202 | return;
203 | }
204 | var item = plugins['Shockwave Flash'];
205 | if (!item) {
206 | return;
207 | }
208 | var desc = item.description;
209 | if (!desc) {
210 | return;
211 | }
212 | return +desc.match(R_VER);
213 | }
214 | function getActiveXVer() {
215 | var ACTIVEX = window['ActiveXObject'];
216 | if (!ACTIVEX) {
217 | return;
218 | }
219 | var ver = '';
220 | try {
221 | ver = new ACTIVEX('ShockwaveFlash.ShockwaveFlash')
222 | .GetVariable('$version')
223 | .replace(',', '.');
224 | }
225 | catch (err) {
226 | return;
227 | }
228 | return +ver.match(R_VER);
229 | }
230 | function getVer() {
231 | var v = getPluginVer();
232 | if (v > 0) {
233 | return v;
234 | }
235 | v = getActiveXVer();
236 | if (v > 0) {
237 | mActiveX = true;
238 | return v;
239 | }
240 | return 0;
241 | }
242 | })(ModFlash || (ModFlash = {}));
243 | ///
244 | ///
245 | ///
246 | ///
247 | ///
248 | var Scrypt;
249 | (function (Scrypt) {
250 | var MOD_MAP = {
251 | 'asmjs': ModAsmJs,
252 | 'flash': ModFlash
253 | };
254 | ;
255 | ;
256 | var mState = 0 /* NONE */, mMod, mAvailableAPI, mResPath = '', mLoaderTimer = 0, mTimeout = 30000 /* LOAD_TIMEOUT */;
257 | var mMaxPassLen = 64, mMaxSaltLen = 64, mMaxDkLen = 64, mMaxThread = 1;
258 | function chooseBestMod() {
259 | var list = getAvailableMod();
260 | var set = {};
261 | for (var i = 0; i < list.length; i++) {
262 | set[list[i]] = true;
263 | }
264 | // TODO
265 | var ua = navigator.userAgent;
266 | if (/Chrome|Firefox|Edge|Safari/.test(ua)) {
267 | if ('asmjs' in set) {
268 | return 'asmjs';
269 | }
270 | }
271 | if ('flash' in set) {
272 | return 'flash';
273 | }
274 | return null;
275 | }
276 | function raise(fn, arg1) {
277 | if (!fn) {
278 | return;
279 | }
280 | switch (arguments.length) {
281 | case 1: return fn();
282 | case 2: return fn(arg1);
283 | }
284 | }
285 | function clearTimer() {
286 | if (mLoaderTimer) {
287 | clearTimeout(mLoaderTimer);
288 | mLoaderTimer = 0;
289 | }
290 | }
291 | // function test() {
292 | // const pass = strToBin('pleaseletmein');
293 | // const salt = strToBin('SodiumChloride');
294 | // const expected = [
295 | // 0x25, 0xa9, 0xfa, 0x20, 0x7f, 0x87, 0xca, 0x09,
296 | // 0xa4, 0xef, 0x8b, 0x9f, 0x77, 0x7a, 0xca, 0x16,
297 | // 0xbe, 0xb7, 0x84, 0xae, 0x18, 0x30, 0xbf, 0xbf,
298 | // 0xd3, 0x83, 0x25, 0xaa, 0xbb, 0x93, 0x77, 0xdf,
299 | // 0x1b, 0xa7, 0x84, 0xd7, 0x46, 0xea, 0x27, 0x3b,
300 | // 0xf5, 0x16, 0xa4, 0x6f, 0xbf, 0xac, 0xf5, 0x11,
301 | // 0xc5, 0xbe, 0xba, 0x4c, 0x4a, 0xb3, 0xac, 0xc7,
302 | // 0xfa, 0x6f, 0x46, 0x0b, 0x6c, 0x0f, 0x47, 0x7b,
303 | // ];
304 | // hash(pass, salt);
305 | // }
306 | function hash(pass, salt, dkLen) {
307 | // check state
308 | if (mState < 2 /* LOADED */) {
309 | throw Error('scrypt not loaded');
310 | }
311 | if (mState < 4 /* READY */) {
312 | throw Error('scrypt not configed');
313 | }
314 | if (mState == 5 /* RUNNING */) {
315 | throw Error('scrypt is running');
316 | }
317 | mState = 5 /* RUNNING */;
318 | // null check
319 | dkLen = dkLen || mMaxDkLen;
320 | pass = pass || [];
321 | salt = salt || [];
322 | // check length
323 | if (pass.length > mMaxPassLen) {
324 | throw Error('pass.length > maxPassLen');
325 | }
326 | if (salt.length > mMaxSaltLen) {
327 | throw Error('salt.length > maxSaltLen');
328 | }
329 | if (dkLen > mMaxDkLen) {
330 | throw Error('dkLen > maxDkLen');
331 | }
332 | mMod.hash(pass, salt, dkLen);
333 | }
334 | Scrypt.hash = hash;
335 | function getAvailableMod() {
336 | if (!mAvailableAPI) {
337 | mAvailableAPI = [];
338 | for (var k in MOD_MAP) {
339 | if (MOD_MAP[k].check()) {
340 | mAvailableAPI.push(k);
341 | }
342 | }
343 | }
344 | return mAvailableAPI;
345 | }
346 | Scrypt.getAvailableMod = getAvailableMod;
347 | function load(mod) {
348 | if (mState >= 1 /* LOADING */) {
349 | return;
350 | }
351 | if (!mod) {
352 | mod = chooseBestMod();
353 | if (!mod) {
354 | throw Error('no available mod');
355 | }
356 | }
357 | mMod = MOD_MAP[mod];
358 | if (!mMod) {
359 | throw Error('unsupported mod: ' + mod);
360 | }
361 | mMod.onload = function () {
362 | clearTimer();
363 | raise(Scrypt.onload);
364 | };
365 | mMod.onerror = function (err) {
366 | unload();
367 | raise(Scrypt.onerror, err);
368 | };
369 | mMod.onready = function () {
370 | mState = 4 /* READY */;
371 | raise(Scrypt.onready);
372 | };
373 | mMod.onprogress = function (percent) {
374 | raise(Scrypt.onprogress, percent);
375 | };
376 | mMod.oncomplete = function (dk) {
377 | mState = 4 /* READY */;
378 | raise(Scrypt.onprogress, 1);
379 | raise(Scrypt.oncomplete, dk);
380 | };
381 | // 加载计时
382 | clearTimer();
383 | mLoaderTimer = setTimeout(function () {
384 | unload();
385 | raise(Scrypt.onerror, 'load timeout');
386 | }, mTimeout);
387 | mState = 1 /* LOADING */;
388 | mMod.load(mResPath);
389 | }
390 | Scrypt.load = load;
391 | function stop() {
392 | mMod.stop();
393 | mState = 4 /* READY */;
394 | }
395 | Scrypt.stop = stop;
396 | function free() {
397 | if (mState == 4 /* READY */) {
398 | mMod.free();
399 | mState = 2 /* LOADED */;
400 | }
401 | }
402 | Scrypt.free = free;
403 | function unload() {
404 | if (mState != 0 /* NONE */) {
405 | mMod.unload();
406 | mState = 0 /* NONE */;
407 | }
408 | clearTimer();
409 | }
410 | Scrypt.unload = unload;
411 | function config(param, opt, test) {
412 | if (!param) {
413 | throw Error('config() takes at least 1 argument');
414 | }
415 | var N = param['N'] | 0;
416 | if (!(1 < N && N <= 8388608 /* MAX_N */)) {
417 | throw Error("param N out of range (1 < N <= 2^23)");
418 | }
419 | if (N & (N - 1)) {
420 | throw Error('param N must be power of 2');
421 | }
422 | var r = param['r'] | 0;
423 | if (!(0 < r && r < 256)) {
424 | throw Error('param r out of range (0 < r < 256)');
425 | }
426 | var P = param['P'] | 0;
427 | if (!(0 < P && P < 256)) {
428 | throw Error('param P out of range (0 < P < 256)');
429 | }
430 | var memCost = N * r * 128;
431 | if (memCost > 1073741824 /* MAX_MEM */) {
432 | throw Error('memory limit exceeded (N * r * 128 > 1G)');
433 | }
434 | // option param
435 | if (opt) {
436 | var maxPassLen = opt['maxPassLen'];
437 | if (maxPassLen == null) {
438 | maxPassLen = mMaxPassLen;
439 | }
440 | else if (maxPassLen <= 0) {
441 | throw Error('invalid maxPassLen');
442 | }
443 | var maxSaltLen = opt['maxSaltLen'];
444 | if (maxSaltLen == null) {
445 | maxSaltLen = mMaxSaltLen;
446 | }
447 | else if (maxSaltLen <= 0) {
448 | throw Error('invalid maxSaltLen');
449 | }
450 | var maxDkLen = opt['maxDkLen'];
451 | if (maxDkLen == null) {
452 | maxDkLen = mMaxDkLen;
453 | }
454 | else if (maxDkLen <= 0) {
455 | throw Error('invalid maxDkLen');
456 | }
457 | var maxThread = opt['maxThread'];
458 | if (maxThread == null) {
459 | maxThread = mMaxThread;
460 | }
461 | else if (maxThread <= 0) {
462 | throw Error('invalid maxThread');
463 | }
464 | if (!test) {
465 | mMaxPassLen = maxPassLen | 0;
466 | mMaxSaltLen = maxSaltLen | 0;
467 | mMaxDkLen = maxDkLen | 0;
468 | mMaxThread = maxThread | 0;
469 | }
470 | }
471 | // test param
472 | if (test) {
473 | return;
474 | }
475 | var taskPerThread = Math.ceil(P / mMaxThread);
476 | var threadCount = Math.ceil(P / taskPerThread);
477 | mMod.config(N, r, P, threadCount, mMaxPassLen, mMaxSaltLen, mMaxDkLen);
478 | mState = 3 /* CONFIGING */;
479 | }
480 | Scrypt.config = config;
481 | function strToBin(str) {
482 | return Util.strToBytes(str);
483 | }
484 | Scrypt.strToBin = strToBin;
485 | function binToStr(bin) {
486 | return Util.bytesToStr(bin);
487 | }
488 | Scrypt.binToStr = binToStr;
489 | function hexToBin(hex) {
490 | if (hex.length % 2) {
491 | throw Error('invalid hex length');
492 | }
493 | return Util.hexToBytes(hex);
494 | }
495 | Scrypt.hexToBin = hexToBin;
496 | function binToHex(bin) {
497 | return Util.bytesToHex(bin);
498 | }
499 | Scrypt.binToHex = binToHex;
500 | function setResPath(path) {
501 | if (!/\/$/.test(path)) {
502 | path += '/';
503 | }
504 | mResPath = path;
505 | }
506 | Scrypt.setResPath = setResPath;
507 | function setResTimeout(ms) {
508 | mTimeout = ms;
509 | }
510 | Scrypt.setResTimeout = setResTimeout;
511 | window['scrypt'] = Scrypt;
512 | })(Scrypt || (Scrypt = {}));
513 | //# sourceMappingURL=scrypt.js.map
--------------------------------------------------------------------------------
/src/main/release/scrypt.js:
--------------------------------------------------------------------------------
1 | !function(){var r;!function(r){function n(r){return f?new f(r):new Array(r)}function o(r){return f?new f(r):r}function t(r){for(var o=r.length/2,t=n(o),e=0;e=18}function e(n){t.__flash_cb=u;var o=n+"flash.swf";m=l(o),r.hideDom(m)}function a(n,o,t){var e=r.bytesToHex(n),a=r.bytesToHex(o);m.hash(e,a,t)}function i(r,n,o,t,e,a,i){m.config.apply(m,arguments)}function c(){m.cancel()}function f(){m.free(),m=null}function s(){document.body.removeChild(m),m=null}function u(o,t){"oncomplete"==o&&(t=r.hexToBytes(t));try{n[o](t)}catch(e){throw e}}function l(r){var n=document.createElement("div"),o="_"+(1e6*Math.random()|0);r=encodeURI(r),n.innerHTML=p?"":"";var t=n.firstChild;return document.body.appendChild(t),t}function h(){var r=navigator.plugins;if(r){var n=r["Shockwave Flash"];if(n){var o=n.description;if(o)return+o.match(w)}}}function d(){var r=window.ActiveXObject;if(r){var n="";try{n=new r("ShockwaveFlash.ShockwaveFlash").GetVariable("$version").replace(",",".")}catch(o){return}return+n.match(w)}}function v(){var r=h();return r>0?r:(r=d(),r>0?(p=!0,r):0)}var m,p;n.check=o,n.load=e,n.hash=a,n.config=i,n.stop=c,n.free=f,n.unload=s;var w=/\d+\.\d+/}(o||(o={}));var t;!function(t){function e(){for(var r=f(),n={},o=0;oj)throw Error("pass.length > maxPassLen");if(n.length>A)throw Error("salt.length > maxSaltLen");if(o>B)throw Error("dkLen > maxDkLen");x.hash(r,n,o)}function f(){if(!E){E=[];for(var r in b)b[r].check()&&E.push(r)}return E}function s(r){if(!(T>=1)){if(!r&&(r=e(),!r))throw Error("no available mod");if(x=b[r],!x)throw Error("unsupported mod: "+r);x.onload=function(){i(),a(t.onload)},x.onerror=function(r){h(),a(t.onerror,r)},x.onready=function(){T=4,a(t.onready)},x.onprogress=function(r){a(t.onprogress,r)},x.oncomplete=function(r){T=4,a(t.onprogress,1),a(t.oncomplete,r)},i(),k=setTimeout(function(){h(),a(t.onerror,"load timeout")},L),T=1,x.load(S)}}function u(){x.stop(),T=4}function l(){4==T&&(x.free(),T=2)}function h(){0!=T&&(x.unload(),T=0),i()}function d(r,n,o){if(!r)throw Error("config() takes at least 1 argument");var t=0|r.N;if(!(11073741824)throw Error("memory limit exceeded (N * r * 128 > 1G)");if(n){var c=n.maxPassLen;if(null==c)c=j;else if(c<=0)throw Error("invalid maxPassLen");var f=n.maxSaltLen;if(null==f)f=A;else if(f<=0)throw Error("invalid maxSaltLen");var s=n.maxDkLen;if(null==s)s=B;else if(s<=0)throw Error("invalid maxDkLen");var u=n.maxThread;if(null==u)u=C;else if(u<=0)throw Error("invalid maxThread");o||(j=0|c,A=0|f,B=0|s,C=0|u)}if(!o){var l=Math.ceil(a/C),h=Math.ceil(a/l);x.config(t,e,a,h,j,A,B),T=3}}function v(n){return r.strToBytes(n)}function m(n){return r.bytesToStr(n)}function p(n){if(n.length%2)throw Error("invalid hex length");return r.hexToBytes(n)}function w(n){return r.bytesToHex(n)}function g(r){/\/$/.test(r)||(r+="/"),S=r}function y(r){L=r}var x,E,b={asmjs:n,flash:o},T=0,S="",k=0,L=3e4,j=64,A=64,B=64,C=1;t.hash=c,t.getAvailableMod=f,t.load=s,t.stop=u,t.free=l,t.unload=h,t.config=d,t.strToBin=v,t.binToStr=m,t.hexToBin=p,t.binToHex=w,t.setResPath=g,t.setResTimeout=y,window.scrypt=t}(t||(t={}))}();
2 | //# sourceMappingURL=sourcemap/scrypt.js.map
--------------------------------------------------------------------------------
/src/main/src/Const.ts:
--------------------------------------------------------------------------------
1 | /** defined by uglifyjs */
2 | declare const _RELEASE;
3 |
4 | /** @const */
5 | const _DEBUG = (typeof _RELEASE == 'undefined');
6 |
7 | type Bytes = Uint8Array | number[];
8 |
--------------------------------------------------------------------------------
/src/main/src/IMod.d.ts:
--------------------------------------------------------------------------------
1 | interface IMod {
2 | onload?: () => void;
3 | onerror?: (err: string) => void;
4 | onready?: () => void;
5 | onprogress?: (percent: number) => void;
6 | oncomplete?: (dkHex: Bytes) => void;
7 |
8 | check() : boolean;
9 | load(path: string) : void;
10 | config(N: number, r: number, P: number, thread: number, maxPassLen, maxSaltLen, maxDkLen) : void;
11 | hash(pass: Bytes, salt: Bytes, dkLen: number);
12 | stop() : void;
13 | free() : void;
14 | unload() : void;
15 | }
--------------------------------------------------------------------------------
/src/main/src/ModAsmJs.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 |
5 | module ModAsmJs {
6 | let mAsmMod;
7 |
8 |
9 | export function check() {
10 | return 'Worker' in window;
11 | }
12 |
13 | export function load(path: string) {
14 | Scrypt['__asmjs_cb'] = callbackHandler;
15 |
16 | let url = path + 'asmjs.js';
17 | if (_DEBUG) {
18 | url = '/src/mod_asmjs/debug/asmjs.js';
19 | }
20 |
21 | let spt = document.createElement('script');
22 |
23 | spt.onerror = function() {
24 | (ModAsmJs as IMod).onerror('script load fail');
25 | };
26 | spt.src = url;
27 | document.body.appendChild(spt);
28 | }
29 |
30 | export function hash(pass: Bytes, salt: Bytes, dkLen: number) {
31 | mAsmMod.hash(pass, salt, dkLen);
32 | }
33 |
34 | export function config(
35 | N: number, r: number, P: number,
36 | thread: number,
37 | maxPassLen: number, maxSaltLen: number, maxDkLen: number
38 | ) {
39 | mAsmMod.config.apply(this, arguments);
40 | }
41 |
42 | export function stop() {
43 | mAsmMod.stop();
44 | }
45 |
46 | export function free() {
47 | mAsmMod.free();
48 | }
49 |
50 | export function unload() {
51 | if (mAsmMod) {
52 | mAsmMod.unload();
53 | mAsmMod = null;
54 | }
55 | }
56 |
57 | function callbackHandler(msg, data?) {
58 | if (msg == 'onload') {
59 | mAsmMod = data;
60 | }
61 | ModAsmJs[msg](data);
62 | }
63 | }
--------------------------------------------------------------------------------
/src/main/src/ModFlash.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | module ModFlash {
5 | let mSwf: HTMLElement;
6 | let mActiveX: boolean;
7 |
8 |
9 | export function check() {
10 | return getVer() >= 18;
11 | }
12 |
13 | export function load(path: string) {
14 | Scrypt['__flash_cb'] = callbackHandler;
15 |
16 | let url = path + 'flash.swf';
17 | if (_DEBUG) {
18 | url = '/src/mod_flash/bin-debug/Scrypt.swf';
19 | }
20 |
21 | mSwf = createSwf(url);
22 | Util.hideDom(mSwf);
23 | }
24 |
25 | export function hash(pass: Bytes, salt: Bytes, dkLen: number) {
26 | let passHex = Util.bytesToHex(pass);
27 | let saltHex = Util.bytesToHex(salt);
28 |
29 | mSwf['hash'](passHex, saltHex, dkLen);
30 | }
31 |
32 | export function config(
33 | N: number, r: number, P: number,
34 | thread: number,
35 | maxPassLen: number, maxSaltLen: number, maxDkLen: number
36 | ) {
37 | mSwf['config'].apply(mSwf, arguments);
38 | }
39 |
40 | export function stop() {
41 | mSwf['cancel']();
42 | }
43 |
44 | export function free() {
45 | mSwf['free']();
46 | mSwf = null;
47 | }
48 |
49 | export function unload() {
50 | document.body.removeChild(mSwf);
51 | mSwf = null;
52 | }
53 |
54 |
55 | function callbackHandler(msg, data?) {
56 | if (msg == 'oncomplete') {
57 | data = Util.hexToBytes(data);
58 | }
59 | try {
60 | ModFlash[msg](data);
61 | } catch (err) {
62 | throw err;
63 | }
64 | }
65 |
66 | function createSwf(url: string) : HTMLElement {
67 | let box = document.createElement('div');
68 | let id = '_' + (Math.random() * 1e6 | 0);
69 |
70 | url = encodeURI(url);
71 |
72 | box.innerHTML = mActiveX
73 | ? ``
74 | : ``
75 |
76 | let swf = box.firstChild as HTMLElement;
77 | document.body.appendChild(swf);
78 | return swf;
79 | }
80 |
81 |
82 | const R_VER = /\d+\.\d+/;
83 |
84 | function getPluginVer() {
85 | let plugins = navigator.plugins;
86 | if (!plugins) {
87 | return;
88 | }
89 | let item = plugins['Shockwave Flash'];
90 | if (!item) {
91 | return;
92 | }
93 | let desc = item.description;
94 | if (!desc) {
95 | return;
96 | }
97 | return +desc.match(R_VER);
98 | }
99 |
100 | function getActiveXVer() {
101 | let ACTIVEX = window['ActiveXObject'];
102 | if (!ACTIVEX) {
103 | return;
104 | }
105 | let ver = '';
106 | try {
107 | ver = new ACTIVEX('ShockwaveFlash.ShockwaveFlash')
108 | .GetVariable('$version')
109 | .replace(',', '.');
110 | } catch (err) {
111 | return;
112 | }
113 | return +ver.match(R_VER);
114 | }
115 |
116 | function getVer() : number {
117 | let v = getPluginVer();
118 | if (v > 0) {
119 | return v;
120 | }
121 |
122 | v = getActiveXVer();
123 | if (v > 0) {
124 | mActiveX = true;
125 | return v;
126 | }
127 |
128 | return 0;
129 | }
130 | }
--------------------------------------------------------------------------------
/src/main/src/Scrypt.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 | ///
5 | ///
6 |
7 |
8 | module Scrypt {
9 |
10 | const MOD_MAP : { [k:string] : IMod} = {
11 | 'asmjs': ModAsmJs,
12 | 'flash': ModFlash,
13 | };
14 |
15 | const enum CONST {
16 | MAX_MEM = 1 << 30, // 1G
17 | MAX_N = MAX_MEM / 128,
18 | LOAD_TIMEOUT = 30 * 1000, // 30s
19 | };
20 |
21 |
22 | const enum STATE {
23 | NONE,
24 | LOADING,
25 | LOADED,
26 | CONFIGING,
27 | READY,
28 | RUNNING,
29 | };
30 |
31 | let mState = STATE.NONE,
32 | mMod: IMod,
33 | mAvailableAPI: string[],
34 | mResPath = '',
35 | mLoaderTimer = 0,
36 | mTimeout = CONST.LOAD_TIMEOUT;
37 |
38 |
39 | let mMaxPassLen = 64,
40 | mMaxSaltLen = 64,
41 | mMaxDkLen = 64,
42 | mMaxThread = 1;
43 |
44 |
45 | export let onload: () => void;
46 | export let onerror: (err: string) => void;
47 | export let onready: () => void;
48 | export let onprogress: (percent: number) => void;
49 | export let oncomplete: (dkHex: string) => void;
50 |
51 |
52 | function chooseBestMod() {
53 | let list = getAvailableMod();
54 | let set = {};
55 |
56 | for (let i = 0; i < list.length; i++) {
57 | set[ list[i] ] = true;
58 | }
59 |
60 | // TODO
61 | let ua = navigator.userAgent;
62 | if (/Chrome|Firefox|Edge|Safari/.test(ua)) {
63 | if ('asmjs' in set) {
64 | return 'asmjs';
65 | }
66 | }
67 | if ('flash' in set) {
68 | return 'flash';
69 | }
70 | return null;
71 | }
72 |
73 | function raise(fn: Function, arg1?) {
74 | if (!fn) {
75 | return;
76 | }
77 | switch (arguments.length) {
78 | case 1: return fn();
79 | case 2: return fn(arg1);
80 | // ...
81 | }
82 | }
83 |
84 | function clearTimer() {
85 | if (mLoaderTimer) {
86 | clearTimeout(mLoaderTimer);
87 | mLoaderTimer = 0;
88 | }
89 | }
90 |
91 | // function test() {
92 | // const pass = strToBin('pleaseletmein');
93 | // const salt = strToBin('SodiumChloride');
94 | // const expected = [
95 | // 0x25, 0xa9, 0xfa, 0x20, 0x7f, 0x87, 0xca, 0x09,
96 | // 0xa4, 0xef, 0x8b, 0x9f, 0x77, 0x7a, 0xca, 0x16,
97 | // 0xbe, 0xb7, 0x84, 0xae, 0x18, 0x30, 0xbf, 0xbf,
98 | // 0xd3, 0x83, 0x25, 0xaa, 0xbb, 0x93, 0x77, 0xdf,
99 | // 0x1b, 0xa7, 0x84, 0xd7, 0x46, 0xea, 0x27, 0x3b,
100 | // 0xf5, 0x16, 0xa4, 0x6f, 0xbf, 0xac, 0xf5, 0x11,
101 | // 0xc5, 0xbe, 0xba, 0x4c, 0x4a, 0xb3, 0xac, 0xc7,
102 | // 0xfa, 0x6f, 0x46, 0x0b, 0x6c, 0x0f, 0x47, 0x7b,
103 | // ];
104 | // hash(pass, salt);
105 | // }
106 |
107 | export function hash(pass: Bytes, salt: Bytes, dkLen: number) {
108 | // check state
109 | if (mState < STATE.LOADED) {
110 | throw Error('scrypt not loaded');
111 | }
112 | if (mState < STATE.READY) {
113 | throw Error('scrypt not configed');
114 | }
115 | if (mState == STATE.RUNNING) {
116 | throw Error('scrypt is running');
117 | }
118 | mState = STATE.RUNNING;
119 |
120 | // null check
121 | dkLen = dkLen || mMaxDkLen;
122 | pass = pass || [];
123 | salt = salt || [];
124 |
125 | // check length
126 | if (pass.length > mMaxPassLen) {
127 | throw Error('pass.length > maxPassLen');
128 | }
129 | if (salt.length > mMaxSaltLen) {
130 | throw Error('salt.length > maxSaltLen');
131 | }
132 | if (dkLen > mMaxDkLen) {
133 | throw Error('dkLen > maxDkLen');
134 | }
135 |
136 | mMod.hash(pass, salt, dkLen);
137 | }
138 |
139 | export function getAvailableMod() {
140 | if (!mAvailableAPI) {
141 | mAvailableAPI = [];
142 |
143 | for (let k in MOD_MAP) {
144 | if (MOD_MAP[k].check()) {
145 | mAvailableAPI.push(k);
146 | }
147 | }
148 | }
149 | return mAvailableAPI;
150 | }
151 |
152 | export function load(mod?: string) {
153 | if (mState >= STATE.LOADING) {
154 | return;
155 | }
156 | if (!mod) {
157 | mod = chooseBestMod();
158 | if (!mod) {
159 | throw Error('no available mod');
160 | }
161 | }
162 | mMod = MOD_MAP[mod];
163 | if (!mMod) {
164 | throw Error('unsupported mod: ' + mod);
165 | }
166 |
167 | mMod.onload = function() {
168 | clearTimer();
169 | raise(onload);
170 | };
171 |
172 | mMod.onerror = function(err) {
173 | unload();
174 | raise(onerror, err);
175 | };
176 |
177 | mMod.onready = function() {
178 | mState = STATE.READY;
179 | raise(onready);
180 | };
181 |
182 | mMod.onprogress = function(percent) {
183 | raise(onprogress, percent);
184 | };
185 |
186 | mMod.oncomplete = function(dk) {
187 | mState = STATE.READY;
188 | raise(onprogress, 1);
189 | raise(oncomplete, dk);
190 | };
191 |
192 | // 加载计时
193 | clearTimer();
194 |
195 | mLoaderTimer = setTimeout(function() {
196 | unload();
197 | raise(onerror, 'load timeout');
198 | }, mTimeout);
199 |
200 | mState = STATE.LOADING;
201 | mMod.load(mResPath);
202 | }
203 |
204 | export function stop() {
205 | mMod.stop();
206 | mState = STATE.READY;
207 | }
208 |
209 | export function free() {
210 | if (mState == STATE.READY) {
211 | mMod.free();
212 | mState = STATE.LOADED;
213 | }
214 | }
215 |
216 | export function unload() {
217 | if (mState != STATE.NONE) {
218 | mMod.unload();
219 | mState = STATE.NONE;
220 | }
221 | clearTimer();
222 | }
223 |
224 | export function config(param, opt?, test?: boolean) {
225 | if (!param) {
226 | throw Error('config() takes at least 1 argument');
227 | }
228 |
229 | let N = param['N'] | 0;
230 | if (! (1 < N && N <= CONST.MAX_N)) {
231 | throw Error(`param N out of range (1 < N <= 2^23)`);
232 | }
233 | if (N & (N - 1)) {
234 | throw Error('param N must be power of 2');
235 | }
236 |
237 | let r = param['r'] | 0;
238 | if (! (0 < r && r < 256)) {
239 | throw Error('param r out of range (0 < r < 256)');
240 | }
241 |
242 | let P = param['P'] | 0;
243 | if (! (0 < P && P < 256)) {
244 | throw Error('param P out of range (0 < P < 256)');
245 | }
246 |
247 | let memCost = N * r * 128;
248 | if (memCost > CONST.MAX_MEM) {
249 | throw Error('memory limit exceeded (N * r * 128 > 1G)')
250 | }
251 |
252 | // option param
253 | if (opt) {
254 | let maxPassLen = opt['maxPassLen'];
255 | if (maxPassLen == null) {
256 | maxPassLen = mMaxPassLen;
257 | } else if (maxPassLen <= 0) {
258 | throw Error('invalid maxPassLen');
259 | }
260 |
261 | let maxSaltLen = opt['maxSaltLen'];
262 | if (maxSaltLen == null) {
263 | maxSaltLen = mMaxSaltLen;
264 | } else if (maxSaltLen <= 0) {
265 | throw Error('invalid maxSaltLen');
266 | }
267 |
268 | let maxDkLen = opt['maxDkLen'];
269 | if (maxDkLen == null) {
270 | maxDkLen = mMaxDkLen;
271 | } else if (maxDkLen <= 0) {
272 | throw Error('invalid maxDkLen');
273 | }
274 |
275 | let maxThread = opt['maxThread'];
276 | if (maxThread == null) {
277 | maxThread = mMaxThread;
278 | } else if (maxThread <= 0) {
279 | throw Error('invalid maxThread');
280 | }
281 |
282 | if (!test) {
283 | mMaxPassLen = maxPassLen | 0;
284 | mMaxSaltLen = maxSaltLen | 0;
285 | mMaxDkLen = maxDkLen | 0;
286 | mMaxThread = maxThread | 0;
287 | }
288 | }
289 |
290 | // test param
291 | if (test) {
292 | return;
293 | }
294 |
295 | let taskPerThread = Math.ceil(P / mMaxThread);
296 | let threadCount = Math.ceil(P / taskPerThread);
297 |
298 | mMod.config(N, r, P, threadCount, mMaxPassLen, mMaxSaltLen, mMaxDkLen);
299 |
300 | mState = STATE.CONFIGING;
301 | }
302 |
303 | export function strToBin(str: string) : Bytes {
304 | return Util.strToBytes(str);
305 | }
306 |
307 | export function binToStr(bin: Bytes) : string {
308 | return Util.bytesToStr(bin);
309 | }
310 |
311 | export function hexToBin(hex: string) : Bytes {
312 | if (hex.length % 2) {
313 | throw Error('invalid hex length');
314 | }
315 | return Util.hexToBytes(hex);
316 | }
317 |
318 | export function binToHex(bin: Bytes) : string {
319 | return Util.bytesToHex(bin);
320 | }
321 |
322 | export function setResPath(path: string) {
323 | if (! /\/$/.test(path)) {
324 | path += '/';
325 | }
326 | mResPath = path;
327 | }
328 |
329 | export function setResTimeout(ms: number) {
330 | mTimeout = ms;
331 | }
332 |
333 | window['scrypt'] = Scrypt;
334 | }
335 |
--------------------------------------------------------------------------------
/src/main/src/Util.ts:
--------------------------------------------------------------------------------
1 |
2 | module Util {
3 | const _Uint8Array: Uint8ArrayConstructor = window['Uint8Array'];
4 |
5 | function allocBuf(size: number) : Bytes {
6 | return _Uint8Array? new _Uint8Array(size) : new Array(size);
7 | }
8 |
9 | function createBuf(buf: number[]) : Bytes {
10 | return _Uint8Array? new _Uint8Array(buf) : buf;
11 | }
12 |
13 |
14 | export function hexToBytes(inStr: string) : Bytes {
15 | let outLen = inStr.length / 2;
16 | let outBuf = allocBuf(outLen);
17 |
18 | for (let i = 0; i < outLen; i++) {
19 | let byte = parseInt(inStr.substr(i * 2, 2), 16);
20 | if (isNaN(byte)) {
21 | throw Error('invalid hex data');
22 | }
23 | outBuf[i] = byte;
24 | }
25 | return outBuf;
26 | }
27 |
28 | export function bytesToHex(inBuf: Bytes) : string {
29 | let inLen = inBuf.length;
30 | let outStr = '';
31 |
32 | for (let i = 0; i < inLen; i++) {
33 | let byte = inBuf[i] & 0xff;
34 | let hex = byte.toString(16);
35 | if (byte < 16) {
36 | hex = '0' + hex;
37 | }
38 | outStr += hex;
39 | }
40 | return outStr;
41 | }
42 |
43 |
44 | export function strToBytes(inStr: string) : Bytes {
45 | let _TextEncoder = window['TextEncoder'];
46 | if (_TextEncoder) {
47 | return new _TextEncoder().encode(inStr);
48 | }
49 |
50 | let outBuf = [],
51 | i = 0,
52 | j = 0,
53 | s = encodeURI(inStr),
54 | n = s.length;
55 |
56 | while (i < n) {
57 | let ch = s.charCodeAt(i);
58 | if (ch == 37) { // '%'
59 | let hex = s.substr(i + 1, 2);
60 | ch = parseInt(hex, 16);
61 | i += 3;
62 | }
63 | else {
64 | i++;
65 | }
66 | outBuf[j++] = ch;
67 | }
68 | return createBuf(outBuf);
69 | }
70 |
71 | export function bytesToStr(inBuf: Bytes) : string {
72 | let inLen = inBuf.length;
73 | let outStr = '';
74 |
75 | for (let i = 0; i < inLen; i++) {
76 | let byte = inBuf[i];
77 | let hex = byte.toString(16);
78 | if (byte < 16) {
79 | hex = '0' + hex;
80 | }
81 | outStr += ('%' + hex);
82 | }
83 |
84 | return decodeURIComponent(outStr);
85 | }
86 |
87 | export function hideDom(el: HTMLElement) {
88 | el.style.cssText = 'position:absolute;top:-999px';
89 | }
90 | }
--------------------------------------------------------------------------------
/src/main/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "out": "debug/scrypt.js",
4 | "sourceMap": true,
5 | "inlineSources": true
6 | }
7 | }
--------------------------------------------------------------------------------
/src/mod_asmjs/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "0.1.0",
5 | "command": "tsc",
6 | "isShellCommand": true,
7 | "args": ["-p", "."],
8 | "showOutput": "silent",
9 | "problemMatcher": "$tsc"
10 | }
--------------------------------------------------------------------------------
/src/mod_asmjs/Makefile:
--------------------------------------------------------------------------------
1 | EMCC_OUT = ./c-bind/emout
2 |
3 | PBKDF2_FN = '["_PBKDF2_OneIter"]'
4 | SMIX_FN = '["_SMix"]'
5 |
6 |
7 | JS = asmjs.js
8 | MAP = ${JS}.map
9 |
10 |
11 | release/${JS}: debug/${JS}
12 | mkdir -p release
13 |
14 | uglifyjs \
15 | "$<" \
16 | --output release/${JS} \
17 | --define _RELEASE=1 \
18 | --enclose \
19 | --mangle \
20 | --compress drop_console \
21 | --in-source-map debug/${MAP} \
22 | --source-map release/${MAP} \
23 | --source-map-include-sources \
24 | --source-map-url ../sourcemap/${MAP}
25 |
26 |
27 | cp release/${JS} ../../release/asset/${JS}
28 | cp release/${MAP} ../../release/sourcemap/${MAP}
29 |
30 | #
31 | # TypeScript Project
32 | #
33 | debug/${JS}: lib/pbkdf2.js lib/smix.js
34 | tsc --project .
35 |
36 |
37 | # used in WorkerMain.ts
38 | $(EMCC_OUT)/pbkdf2.js: ../c/pbkdf2.c
39 | mkdir -p $(EMCC_OUT)
40 |
41 | emcc \
42 | "$<" \
43 | -o "$@" \
44 | -g -Os \
45 | -DV_ASMJS \
46 | -s EXPORTED_FUNCTIONS=$(PBKDF2_FN) \
47 | --memory-init-file 0
48 |
49 |
50 | lib/pbkdf2.js: $(EMCC_OUT)/pbkdf2.js
51 | mkdir -p lib
52 |
53 | node c-bind/reduce \
54 | asm_pbkdf2 "$<" "$@" $(PBKDF2_FN) 1
55 |
56 |
57 | # used in WorkerChild.ts
58 | $(EMCC_OUT)/smix.js: ../c/smix.c
59 | mkdir -p $(EMCC_OUT)
60 |
61 | emcc \
62 | "$<" \
63 | -o "$@" \
64 | -DV_ASMJS \
65 | -g -O3 \
66 | -s EXPORTED_FUNCTIONS=$(SMIX_FN) \
67 | --memory-init-file 0
68 |
69 |
70 | lib/smix.js: $(EMCC_OUT)/smix.js
71 | mkdir -p lib
72 |
73 | node c-bind/reduce \
74 | asm_smix "$<" "$@" $(SMIX_FN)
75 |
76 |
77 |
78 | .PHONY: release
79 | release: release/${JS}
80 |
81 | .PHONY: debug
82 | debug: debug/${JS}
83 |
84 | .PHONY: lib
85 | lib: lib/pbkdf2.js lib/smix.js
86 |
87 | .PHONY: clean
88 | clean:
89 | rm -rf $(EMCC_OUT)
90 | rm -rf ./lib
91 | rm -rf ./debug
92 | rm -rf ./release
93 |
--------------------------------------------------------------------------------
/src/mod_asmjs/c-bind/reduce.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const name = process.argv[2]
3 | const infile = process.argv[3]
4 | const outfile = process.argv[4]
5 | const apis = JSON.parse(process.argv[5])
6 | const initmem = !!process.argv[6]
7 | const stackSize = +process.argv[7] || 8192
8 |
9 | let code = fs.readFileSync(infile, 'utf8')
10 |
11 | //
12 | // memory initializer
13 | //
14 | let mem
15 | let memBase64
16 |
17 | if (initmem) {
18 | mem = code
19 | .split('/* memory initializer */ allocate([')[1]
20 | .split(']', 1)[0]
21 | .split(',')
22 | .map(v => +v)
23 |
24 | memBase64 = new Buffer(mem).toString('base64')
25 | }
26 |
27 | //
28 | // remove unused func and var
29 | //
30 | let funMap = {}, funUsed = {}
31 | let varMap = {}, varUsed = {}
32 |
33 | // get all func
34 | code
35 | .split('// EMSCRIPTEN_START_FUNCS')[1]
36 | .split('// EMSCRIPTEN_END_FUNCS')[0]
37 |
38 | // temp
39 | .replace(/(.*_emscripten_memcpy_big.*)/, '')
40 |
41 | .split(/\s*function\s+/)
42 |
43 | .forEach(body => {
44 | if (body) {
45 | let key = body.split('(', 1)[0]
46 | let val = 'function ' + body
47 |
48 | funMap[key] = val
49 | }
50 | })
51 |
52 | // get all var
53 | code
54 | .split("'use asm';")[1]
55 | .split('// EMSCRIPTEN_START_FUNCS')[0]
56 |
57 | .replace(/var/g, '')
58 |
59 | .split(/[;,]/)
60 |
61 | .forEach(assign => {
62 | let arr = assign.trim().split(/\s*=\s*/)
63 | let key = arr[0]
64 | let val = arr[1]
65 | if (key && val)
66 | varMap[key] = val
67 | })
68 |
69 |
70 | function parseDep(fn) {
71 | if (fn in funUsed)
72 | return
73 | funUsed[fn] = true
74 |
75 | console.log('keep function:', fn)
76 |
77 | let fbody = funMap[fn]
78 |
79 | // enum symbols
80 | fbody.split(/[^\w\$]+/).forEach(v => {
81 | if (v in funMap)
82 | parseDep(v)
83 | else if (v in varMap)
84 | varUsed[v] = true
85 | })
86 | }
87 |
88 |
89 | apis.forEach(fn => {
90 | if (fn in funMap)
91 | parseDep(fn)
92 | else
93 | throw 'func `' + fn + '` not found!'
94 | })
95 |
96 |
97 | let varCode = Object.keys(varUsed)
98 | .map(key => key + ' = ' + varMap[key])
99 | .join(',\n')
100 |
101 |
102 | let funCode = Object.keys(funUsed)
103 | .map(key => funMap[key])
104 | .join('\n')
105 |
106 |
107 | let retCode = apis
108 | .map(v => v + ': ' + v)
109 | .join(',\n')
110 |
111 |
112 | let basePtr = 8
113 |
114 | if (initmem) {
115 | heapPtr = basePtr + mem.length + stackSize
116 | } else
117 | heapPtr = basePtr + stackSize
118 |
119 |
120 | let fncreate = initmem ?
121 | `
122 | function create(buffer) {
123 | var arr = new Uint8Array(buffer);
124 | var bin = atob('${memBase64}');
125 | var base = ${basePtr};
126 |
127 | for (var i = 0; i < bin.length; i++) {
128 | arr[base + i] = bin.charCodeAt(i);
129 | }
130 |
131 | return asm(self, {}, buffer);
132 | }
133 | `
134 | :
135 | `
136 | function create(buffer) {
137 | return asm(self, {}, buffer);
138 | }
139 | `
140 |
141 |
142 | let js = `
143 | function ${name}() {
144 | 'use strict';
145 |
146 | var asm = function(global, env, buffer) {
147 | 'use asm';
148 |
149 | var
150 | ${varCode};
151 |
152 | ${funCode}
153 |
154 | return {
155 | ${retCode}
156 | };
157 | }
158 |
159 | ${fncreate}
160 |
161 | function getHeap() {
162 | return ${heapPtr};
163 | }
164 |
165 | return {
166 | create: create,
167 | getHeap: getHeap
168 | };
169 | };
170 | `
171 |
172 | fs.writeFileSync(outfile, js)
173 |
--------------------------------------------------------------------------------
/src/mod_asmjs/lib/pbkdf2.js:
--------------------------------------------------------------------------------
1 |
2 | function asm_pbkdf2() {
3 | 'use strict';
4 |
5 | var asm = function(global, env, buffer) {
6 | 'use asm';
7 |
8 | var
9 | HEAP32 = new global.Int32Array(buffer),
10 | HEAP8 = new global.Int8Array(buffer),
11 | HEAPU8 = new global.Uint8Array(buffer);
12 |
13 | function _PBKDF2_OneIter($0, $1, $2, $3, $4, $5) {
14 | $0 = $0 | 0;
15 | $1 = $1 | 0;
16 | $2 = $2 | 0;
17 | $3 = $3 | 0;
18 | $4 = $4 | 0;
19 | $5 = $5 | 0;
20 | var $$01$i = 0, $$025$i$i = 0, $$026$i$i = 0, $$pr$i$i = 0, $16 = 0, $29 = 0, $43 = 0, $44 = 0, dest = 0, src = 0, stop = 0;
21 | if ($1 >>> 0 > 64) {
22 | HEAP32[191] = 0;
23 | HEAP32[183] = 1779033703;
24 | HEAP32[184] = -1150833019;
25 | HEAP32[185] = 1013904242;
26 | HEAP32[186] = -1521486534;
27 | HEAP32[187] = 1359893119;
28 | HEAP32[188] = -1694144372;
29 | HEAP32[189] = 528734635;
30 | HEAP32[190] = 1541459225;
31 | _SHA256_Update(732, $0, $1);
32 | _SHA256_Final(1816, 732);
33 | $$025$i$i = 1816;
34 | $$026$i$i = 32;
35 | } else {
36 | $$025$i$i = $0;
37 | $$026$i$i = $1;
38 | }
39 | HEAP32[191] = 0;
40 | HEAP32[183] = 1779033703;
41 | HEAP32[184] = -1150833019;
42 | HEAP32[185] = 1013904242;
43 | HEAP32[186] = -1521486534;
44 | HEAP32[187] = 1359893119;
45 | HEAP32[188] = -1694144372;
46 | HEAP32[189] = 528734635;
47 | HEAP32[190] = 1541459225;
48 | dest = 1752;
49 | stop = dest + 64 | 0;
50 | do {
51 | HEAP8[dest >> 0] = 54;
52 | dest = dest + 1 | 0;
53 | } while ((dest | 0) < (stop | 0));
54 | if (!$$026$i$i) $$pr$i$i = 1; else {
55 | HEAP8[1752] = HEAP8[$$025$i$i >> 0] ^ 54;
56 | if (($$026$i$i | 0) == 1) $$pr$i$i = 0; else {
57 | HEAP8[1753] = HEAP8[$$025$i$i + 1 >> 0] ^ 54;
58 | if (($$026$i$i | 0) == 2) $$pr$i$i = 0; else {
59 | HEAP8[1754] = HEAP8[$$025$i$i + 2 >> 0] ^ 54;
60 | if (($$026$i$i | 0) == 3) $$pr$i$i = 0; else {
61 | $16 = 3;
62 | do {
63 | HEAP8[1752 + $16 >> 0] = HEAP8[$$025$i$i + $16 >> 0] ^ HEAP8[1752 + $16 >> 0];
64 | $16 = $16 + 1 | 0;
65 | } while (($16 | 0) != ($$026$i$i | 0));
66 | $$pr$i$i = 0;
67 | }
68 | }
69 | }
70 | }
71 | _SHA256_Update(732, 1752, 64);
72 | HEAP32[216] = 0;
73 | HEAP32[208] = 1779033703;
74 | HEAP32[209] = -1150833019;
75 | HEAP32[210] = 1013904242;
76 | HEAP32[211] = -1521486534;
77 | HEAP32[212] = 1359893119;
78 | HEAP32[213] = -1694144372;
79 | HEAP32[214] = 528734635;
80 | HEAP32[215] = 1541459225;
81 | dest = 1752;
82 | stop = dest + 64 | 0;
83 | do {
84 | HEAP8[dest >> 0] = 92;
85 | dest = dest + 1 | 0;
86 | } while ((dest | 0) < (stop | 0));
87 | if (!$$pr$i$i) {
88 | HEAP8[1752] = HEAP8[$$025$i$i >> 0] ^ 92;
89 | if (($$026$i$i | 0) != 1) {
90 | HEAP8[1753] = HEAP8[$$025$i$i + 1 >> 0] ^ 92;
91 | if (($$026$i$i | 0) != 2) {
92 | HEAP8[1754] = HEAP8[$$025$i$i + 2 >> 0] ^ 92;
93 | if (($$026$i$i | 0) != 3) {
94 | $29 = 3;
95 | do {
96 | HEAP8[1752 + $29 >> 0] = HEAP8[$$025$i$i + $29 >> 0] ^ HEAP8[1752 + $29 >> 0];
97 | $29 = $29 + 1 | 0;
98 | } while (($29 | 0) != ($$026$i$i | 0));
99 | }
100 | }
101 | }
102 | }
103 | _SHA256_Update(832, 1752, 64);
104 | _SHA256_Update(732, $2, $3);
105 | if ($5 | 0) {
106 | $$01$i = 0;
107 | $44 = 0;
108 | do {
109 | $$01$i = $$01$i + 1 | 0;
110 | HEAP8[1687] = $$01$i;
111 | HEAP8[1686] = $$01$i >>> 8;
112 | HEAP8[1685] = $$01$i >>> 16;
113 | HEAP8[1684] = $$01$i >>> 24;
114 | _memcpy(932, 732, 200) | 0;
115 | _SHA256_Update(932, 1684, 4);
116 | _SHA256_Final(1848, 932);
117 | _SHA256_Update(1032, 1848, 32);
118 | _SHA256_Final(1688, 1032);
119 | dest = 1720;
120 | src = 1688;
121 | stop = dest + 32 | 0;
122 | do {
123 | HEAP8[dest >> 0] = HEAP8[src >> 0] | 0;
124 | dest = dest + 1 | 0;
125 | src = src + 1 | 0;
126 | } while ((dest | 0) < (stop | 0));
127 | $43 = $5 - $44 | 0;
128 | _memcpy($4 + $44 | 0, 1720, ($43 >>> 0 > 32 ? 32 : $43) | 0) | 0;
129 | $44 = $$01$i << 5;
130 | } while ($44 >>> 0 < $5 >>> 0);
131 | }
132 | return;
133 | }
134 | function _SHA256_Update($0, $1, $2) {
135 | $0 = $0 | 0;
136 | $1 = $1 | 0;
137 | $2 = $2 | 0;
138 | var $$0$lcssa = 0, $$027$lcssa = 0, $$02728 = 0, $$029 = 0, $10 = 0, $12 = 0, $13 = 0, $14 = 0, $15 = 0, $18 = 0, $19 = 0, $21 = 0, $4 = 0, $5 = 0, $7 = 0;
139 | do if ($2 | 0) {
140 | $4 = $0 + 32 | 0;
141 | $5 = HEAP32[$4 >> 2] | 0;
142 | $7 = $5 >>> 3 & 63;
143 | HEAP32[$4 >> 2] = $5 + ($2 << 3);
144 | $10 = 64 - $7 | 0;
145 | $12 = $0 + 36 + $7 | 0;
146 | if ($10 >>> 0 > $2 >>> 0) {
147 | _memcpy($12 | 0, $1 | 0, $2 | 0) | 0;
148 | break;
149 | }
150 | _memcpy($12 | 0, $1 | 0, $10 | 0) | 0;
151 | $13 = $0 + 36 | 0;
152 | _SHA256_Transform($0, $13);
153 | $14 = $1 + $10 | 0;
154 | $15 = $2 - $10 | 0;
155 | if ($15 >>> 0 > 63) {
156 | $18 = $7 + $2 + -128 | 0;
157 | $19 = $18 & -64;
158 | $21 = $19 + 128 - $7 | 0;
159 | $$02728 = $15;
160 | $$029 = $14;
161 | while (1) {
162 | _SHA256_Transform($0, $$029);
163 | $$02728 = $$02728 + -64 | 0;
164 | if ($$02728 >>> 0 <= 63) break; else $$029 = $$029 + 64 | 0;
165 | }
166 | $$0$lcssa = $1 + $21 | 0;
167 | $$027$lcssa = $18 - $19 | 0;
168 | } else {
169 | $$0$lcssa = $14;
170 | $$027$lcssa = $15;
171 | }
172 | _memcpy($13 | 0, $$0$lcssa | 0, $$027$lcssa | 0) | 0;
173 | } while (0);
174 | return;
175 | }
176 | function _memcpy(dest, src, num) {
177 | dest = dest | 0;
178 | src = src | 0;
179 | num = num | 0;
180 | var ret = 0;
181 |
182 | ret = dest | 0;
183 | if ((dest & 3) == (src & 3)) {
184 | while (dest & 3) {
185 | if (!num) return ret | 0;
186 | HEAP8[dest >> 0] = HEAP8[src >> 0] | 0;
187 | dest = dest + 1 | 0;
188 | src = src + 1 | 0;
189 | num = num - 1 | 0;
190 | }
191 | while ((num | 0) >= 4) {
192 | HEAP32[dest >> 2] = HEAP32[src >> 2];
193 | dest = dest + 4 | 0;
194 | src = src + 4 | 0;
195 | num = num - 4 | 0;
196 | }
197 | }
198 | while ((num | 0) > 0) {
199 | HEAP8[dest >> 0] = HEAP8[src >> 0] | 0;
200 | dest = dest + 1 | 0;
201 | src = src + 1 | 0;
202 | num = num - 1 | 0;
203 | }
204 | return ret | 0;
205 | }
206 | function _SHA256_Transform($0, $1) {
207 | $0 = $0 | 0;
208 | $1 = $1 | 0;
209 | var $$01$i = 0, $$059 = 0, $$158 = 0, $$257 = 0, $112 = 0, $116 = 0, $123 = 0, $131 = 0, $25 = 0, $3 = 0, $51 = 0, $58 = 0, $63 = 0, $82 = 0, $93 = 0, $97 = 0, $51$looptemp = 0;
210 | $$01$i = 0;
211 | do {
212 | $3 = $1 + ($$01$i << 2) | 0;
213 | HEAP32[444 + ($$01$i << 2) >> 2] = (HEAPU8[$3 + 2 >> 0] | 0) << 8 | (HEAPU8[$3 + 3 >> 0] | 0) | (HEAPU8[$3 + 1 >> 0] | 0) << 16 | (HEAPU8[$3 >> 0] | 0) << 24;
214 | $$01$i = $$01$i + 1 | 0;
215 | } while (($$01$i | 0) != 16);
216 | $$059 = 16;
217 | $51 = HEAP32[111] | 0;
218 | do {
219 | $25 = HEAP32[444 + ($$059 + -2 << 2) >> 2] | 0;
220 | $51$looptemp = $51;
221 | $51 = HEAP32[444 + ($$059 + -15 << 2) >> 2] | 0;
222 | HEAP32[444 + ($$059 << 2) >> 2] = $51$looptemp + (HEAP32[444 + ($$059 + -7 << 2) >> 2] | 0) + (($25 >>> 19 | $25 << 13) ^ $25 >>> 10 ^ ($25 >>> 17 | $25 << 15)) + (($51 >>> 18 | $51 << 14) ^ $51 >>> 3 ^ ($51 >>> 7 | $51 << 25));
223 | $$059 = $$059 + 1 | 0;
224 | } while (($$059 | 0) != 64);
225 | HEAP32[175] = HEAP32[$0 >> 2];
226 | HEAP32[176] = HEAP32[$0 + 4 >> 2];
227 | HEAP32[177] = HEAP32[$0 + 8 >> 2];
228 | HEAP32[178] = HEAP32[$0 + 12 >> 2];
229 | HEAP32[179] = HEAP32[$0 + 16 >> 2];
230 | HEAP32[180] = HEAP32[$0 + 20 >> 2];
231 | HEAP32[181] = HEAP32[$0 + 24 >> 2];
232 | HEAP32[182] = HEAP32[$0 + 28 >> 2];
233 | $$158 = 0;
234 | do {
235 | $58 = 700 + (((71 - $$158 | 0) % 8 | 0) << 2) | 0;
236 | $63 = HEAP32[700 + (((68 - $$158 | 0) % 8 | 0) << 2) >> 2] | 0;
237 | $82 = HEAP32[700 + (((70 - $$158 | 0) % 8 | 0) << 2) >> 2] | 0;
238 | $93 = (HEAP32[444 + ($$158 << 2) >> 2] | 0) + (HEAP32[$58 >> 2] | 0) + (($63 >>> 6 | $63 << 26) ^ ($63 >>> 11 | $63 << 21) ^ ($63 >>> 25 | $63 << 7)) + (HEAP32[8 + ($$158 << 2) >> 2] | 0) + (($82 ^ HEAP32[700 + (((69 - $$158 | 0) % 8 | 0) << 2) >> 2]) & $63 ^ $82) | 0;
239 | $97 = HEAP32[700 + (((64 - $$158 | 0) % 8 | 0) << 2) >> 2] | 0;
240 | $112 = HEAP32[700 + (((65 - $$158 | 0) % 8 | 0) << 2) >> 2] | 0;
241 | $116 = HEAP32[700 + (((66 - $$158 | 0) % 8 | 0) << 2) >> 2] | 0;
242 | $123 = 700 + (((67 - $$158 | 0) % 8 | 0) << 2) | 0;
243 | HEAP32[$123 >> 2] = (HEAP32[$123 >> 2] | 0) + $93;
244 | HEAP32[$58 >> 2] = (($97 >>> 2 | $97 << 30) ^ ($97 >>> 13 | $97 << 19) ^ ($97 >>> 22 | $97 << 10)) + $93 + (($116 | $112) & $97 | $116 & $112);
245 | $$158 = $$158 + 1 | 0;
246 | } while (($$158 | 0) != 64);
247 | $$257 = 0;
248 | do {
249 | $131 = $0 + ($$257 << 2) | 0;
250 | HEAP32[$131 >> 2] = (HEAP32[$131 >> 2] | 0) + (HEAP32[700 + ($$257 << 2) >> 2] | 0);
251 | $$257 = $$257 + 1 | 0;
252 | } while (($$257 | 0) != 8);
253 | return;
254 | }
255 | function _SHA256_Final($0, $1) {
256 | $0 = $0 | 0;
257 | $1 = $1 | 0;
258 | var $$01$i = 0, $12 = 0, $16 = 0, $18 = 0, $3 = 0;
259 | $3 = HEAP32[$1 + 32 >> 2] | 0;
260 | HEAP8[1139] = $3;
261 | HEAP8[1138] = $3 >>> 8;
262 | HEAP8[1137] = $3 >>> 16;
263 | HEAP8[1136] = $3 >>> 24;
264 | $12 = $3 >>> 3 & 63;
265 | HEAP32[283] = 0;
266 | _SHA256_Update($1, 380, ($12 >>> 0 < 56 ? 56 : 120) - $12 | 0);
267 | _SHA256_Update($1, 1132, 8);
268 | $$01$i = 0;
269 | do {
270 | $16 = $0 + ($$01$i << 2) | 0;
271 | $18 = HEAP32[$1 + ($$01$i << 2) >> 2] | 0;
272 | HEAP8[$16 + 3 >> 0] = $18;
273 | HEAP8[$16 + 2 >> 0] = $18 >>> 8;
274 | HEAP8[$16 + 1 >> 0] = $18 >>> 16;
275 | HEAP8[$16 >> 0] = $18 >>> 24;
276 | $$01$i = $$01$i + 1 | 0;
277 | } while (($$01$i | 0) != 8);
278 | return;
279 | }
280 |
281 | return {
282 | _PBKDF2_OneIter: _PBKDF2_OneIter
283 | };
284 | }
285 |
286 |
287 | function create(buffer) {
288 | var arr = new Uint8Array(buffer);
289 | var bin = atob('mC+KQpFEN3HP+8C1pdu16VvCVjnxEfFZpII/ktVeHKuYqgfYAVuDEr6FMSTDfQxVdF2+cv6x3oCnBtybdPGbwcFpm+SGR77vxp3BD8yhDCRvLOktqoR0StypsFzaiPl2UlE+mG3GMajIJwOwx39Zv/ML4MZHkafVUWPKBmcpKRSFCrcnOCEbLvxtLE0TDThTVHMKZbsKanYuycKBhSxykqHov6JLZhqocItLwqNRbMcZ6JLRJAaZ1oU1DvRwoGoQFsGkGQhsNx5Md0gntbywNLMMHDlKqthOT8qcW/NvLmjugo90b2OleBR4yIQIAseM+v++kOtsUKT3o/m+8nhxxgUAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAYAcAAAAEAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAr/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAQAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==');
290 | var base = 8;
291 |
292 | for (var i = 0; i < bin.length; i++) {
293 | arr[base + i] = bin.charCodeAt(i);
294 | }
295 |
296 | return asm(self, {}, buffer);
297 | }
298 |
299 |
300 | function getHeap() {
301 | return 8636;
302 | }
303 |
304 | return {
305 | create: create,
306 | getHeap: getHeap
307 | };
308 | };
309 |
--------------------------------------------------------------------------------
/src/mod_asmjs/release/asmjs.js:
--------------------------------------------------------------------------------
1 | !function(){function A(){"use strict";function A(A){return r(self,{},A)}function e(){return 8200}var r=function(A,e,r){"use asm";var n=new A.Int32Array(r),t=A.Math.imul,i=new A.Int8Array(r),a=new A.Uint8Array(r);function o(A,e,r,o,u,c,s){A=A|0;e=e|0;r=r|0;o=o|0;u=u|0;c=c|0;s=s|0;var l=0,w=0,h=0,v=0,d=0,y=0,p=0,g=0,b=0,m=0,M=0,k=0,U=0,L=0,I=0,x=0,E=0,D=0,K=0,R=0,B=0,F=0,O=0,_=0,C=0,H=0,j=0,q=0,G=0,N=0,P=0,S=0;C=e<<7;q=e<<5;N=o+C|0;M=o+(e<<8)|0;if(u|0){A:do if(c>>>0>>0){k=C+-64|0;U=o+k|0;L=A+-1|0;I=q&1073741792;x=N+k|0;if(!I){b=c;while(1){f(o,N,e);f(N,o,e);b=b+2|0;if(b>>>0>=s>>>0)break A}}else g=c;do{_=M+((t(n[U>>2]&L,q)|0)<<2)|0;v=0;do{H=o+(v<<2)|0;n[H>>2]=n[H>>2]^n[_+(v<<2)>>2];v=v+1|0}while((v|0)!=(I|0));f(o,N,e);j=M+((t(n[x>>2]&L,q)|0)<<2)|0;d=0;do{G=N+(d<<2)|0;n[G>>2]=n[G>>2]^n[j+(d<<2)>>2];d=d+1|0}while((d|0)!=(I|0));f(N,o,e);g=g+2|0}while(g>>>0>>0)}while(0);if((s|0)==(A|0)&(q|0)!=0)m=0;else return;do{P=r+(m<<2)|0;S=n[o+(m<<2)>>2]|0;i[P>>0]=S;i[P+1>>0]=S>>>8;i[P+2>>0]=S>>>16;i[P+3>>0]=S>>>24;m=m+1|0}while((m|0)!=(q|0));return}if((c|0)==0&(q|0)!=0){p=0;do{E=r+(p<<2)|0;n[o+(p<<2)>>2]=(a[E+1>>0]|0)<<8|(a[E>>0]|0)|(a[E+2>>0]|0)<<16|(a[E+3>>0]|0)<<24;p=p+1|0}while((p|0)!=(q|0))}D=t(q,c)|0;K=t(q,s)|0;if(D>>>0>=K>>>0)return;R=q&1073741792;if(!R){w=D;do{f(o,N,e);w=w+q+q|0;f(N,o,e)}while(w>>>0>>0);return}else l=D;do{B=M+(l<<2)|0;h=0;do{n[B+(h<<2)>>2]=n[o+(h<<2)>>2];h=h+1|0}while((h|0)!=(R|0));F=l+q|0;f(o,N,e);O=M+(F<<2)|0;y=0;do{n[O+(y<<2)>>2]=n[N+(y<<2)>>2];y=y+1|0}while((y|0)!=(R|0));l=F+q|0;f(N,o,e)}while(l>>>0>>0);return}function f(A,e,r){A=A|0;e=e|0;r=r|0;var t=0,i=0,a=0,o=0,f=0,u=0,c=0,s=0,l=0,w=0,h=0,v=0,d=0,y=0,p=0,g=0,b=0,m=0,M=0,k=0,U=0,L=0,I=0,x=0,E=0,D=0,K=0,R=0,B=0,F=0,O=0,_=0,C=0,H=0,j=0,q=0,G=0,N=0,P=0,S=0,V=0,Z=0,Q=0,T=0,J=0,Y=0,W=0,z=0,X=0,$=0,AA=0,eA=0,rA=0,nA=0,tA=0,iA=0,aA=0,oA=0,fA=0,uA=0,cA=0,sA=0,lA=0,wA=0,hA=0,vA=0,dA=0,yA=0,pA=0,gA=0,bA=0,mA=0,MA=0,kA=0,UA=0,LA=0,IA=0,xA=0,EA=0,DA=0,KA=0,RA=0,BA=0,FA=0,OA=0,_A=0,CA=0,HA=0,jA=0,qA=0,GA=0,NA=0,PA=0,SA=0,VA=0,ZA=0,QA=0,TA=0,JA=0,YA=0,WA=0,zA=0,XA=0,$A=0,Ae=0,ee=0,re=0,ne=0,te=0,ie=0,ae=0,oe=0,fe=0,ue=0,ce=0,se=0,le=0,we=0,he=0,ve=0,de=0,ye=0,pe=0,ge=0,be=0,me=0,Me=0,ke=0,Ue=0,Le=0,Ie=0,xe=0,Ee=0,De=0,Ke=0,Re=0,Be=0,Fe=0,Oe=0,_e=0,Ce=0,He=0,je=0,qe=0,Ge=0,Ne=0,Pe=0,Se=0,Ve=0,Ze=0,Qe=0,Te=0,Je=0,Ye=0,We=0,ze=0,Xe=0,$e=0,Ar=0,er=0,rr=0,nr=0,tr=0,ir=0,ar=0,or=0,fr=0,ur=0,cr=0,sr=0,lr=0,wr=0,hr=0,vr=0,dr=0,yr=0,pr=0,gr=0,br=0,mr=0,Mr=0,kr=0,Ur=0,Lr=0,Ir=0,xr=0,Er=0,Dr=0,Kr=0,Rr=0,Br=0,Fr=0,Or=0,_r=0,Cr=0,Hr=0,jr=0,qr=0,Gr=0,Nr=0,Pr=0,Sr=0,Vr=0,Zr=0,Qr=0,Tr=0,Jr=0,Yr=0,Wr=0,zr=0,Xr=0,$r=0,An=0,en=0,rn=0,nn=0,tn=0,an=0,on=0,fn=0,un=0,cn=0,sn=0,ln=0,wn=0,hn=0,vn=0,dn=0,yn=0,pn=0,gn=0,bn=0,mn=0,Mn=0,kn=0,Un=0,Ln=0,In=0,xn=0,En=0,Dn=0,Kn=0,Rn=0,Bn=0,Fn=0,On=0,_n=0,Cn=0,Hn=0,jn=0,qn=0,Gn=0,Nn=0,Pn=0,Sn=0,Vn=0,Zn=0,Qn=0,Tn=0,Jn=0,Yn=0,Wn=0,zn=0,Xn=0,$n=0,At=0,et=0,rt=0,nt=0,tt=0,it=0,at=0,ot=0,ft=0,ut=0,ct=0,st=0,lt=0,wt=0,ht=0,vt=0,dt=0,yt=0,pt=0,gt=0,bt=0,mt=0,Mt=0,kt=0,Ut=0,Lt=0,It=0,xt=0,Et=0,Dt=0,Kt=0,Rt=0,Bt=0,Ft=0,Ot=0,_t=0,Ct=0,Ht=0,jt=0,qt=0,Gt=0,Nt=0,Pt=0,St=0,Vt=0,Zt=0,Qt=0,Tt=0,Jt=0,Yt=0,Wt=0,zt=0,Xt=0,$t=0,Ai=0,ei=0,ri=0,ni=0,ti=0,ii=0,ai=0,oi=0,fi=0,ui=0,ci=0,si=0,li=0,wi=0,hi=0,vi=0,di=0,yi=0,pi=0,gi=0,bi=0,mi=0,Mi=0,ki=0,Ui=0,Li=0,Ii=0,xi=0,Ei=0,Di=0,Ki=0,Ri=0,Bi=0,Fi=0,Oi=0,_i=0,Ci=0,Hi=0,ji=0,qi=0,Gi=0,Ni=0,Pi=0,Si=0,Vi=0,Zi=0,Qi=0,Ti=0,Ji=0,Yi=0,Wi=0,zi=0,Xi=0,$i=0,Aa=0,ea=0,ra=0,na=0,ta=0,ia=0,aa=0,oa=0,fa=0,ua=0,ca=0,sa=0,la=0,wa=0,ha=0,va=0,da=0,ya=0,pa=0,ga=0,ba=0,ma=0,Ma=0,ka=0,Ua=0,La=0,Ia=0,xa=0,Ea=0,Da=0,Ka=0,Ra=0,Ba=0,Fa=0,Oa=0,_a=0,Ca=0,Ha=0,ja=0,qa=0,Ga=0,Na=0,Pa=0,Sa=0,Va=0,Za=0,Qa=0,Ta=0,Ja=0,Ya=0,Wa=0,za=0,Xa=0,$a=0,Ao=0,eo=0,ro=0,no=0,to=0,io=0,ao=0,oo=0,fo=0,uo=0,co=0,so=0,lo=0,wo=0,ho=0,vo=0,yo=0,po=0,go=0,bo=0,mo=0,Mo=0,ko=0,Uo=0,Lo=0,Io=0,xo=0,Eo=0,Do=0,Ko=0,Ro=0,Bo=0,Fo=0,Oo=0,_o=0,Co=0,Ho=0,jo=0,qo=0,Go=0,No=0,Po=0,So=0,Vo=0,Zo=0,Qo=0,To=0,Jo=0,Yo=0,Wo=0,zo=0,Xo=0,$o=0,Af=0,ef=0,rf=0,nf=0,tf=0,af=0,of=0,ff=0,uf=0,cf=0,sf=0,lf=0,wf=0,hf=0,vf=0,df=0,yf=0,pf=0,gf=0,bf=0,mf=0,Mf=0,kf=0,Uf=0,Lf=0,If=0,xf=0,Ef=0,Df=0,Kf=0,Rf=0,Bf=0,Ff=0,Of=0,_f=0,Cf=0,Hf=0,jf=0,qf=0,Gf=0,Nf=0,Pf=0,Sf=0,Vf=0,Zf=0,Qf=0,Tf=0,Jf=0,Yf=0,Wf=0,zf=0,Xf=0,$f=0,Au=0,eu=0,ru=0,nu=0,tu=0,iu=0,au=0,ou=0,fu=0,uu=0,cu=0,su=0,lu=0,wu=0,hu=0,vu=0,du=0,yu=0,pu=0,gu=0,bu=0,mu=0,Mu=0,ku=0,Uu=0,Lu=0,Iu=0,xu=0,Eu=0,Du=0,Ku=0,Ru=0,Bu=0,Fu=0,Ou=0,_u=0,Cu=0,Hu=0,ju=0,qu=0,Gu=0,Nu=0,Pu=0,Su=0,Vu=0;ct=r<<1;Ba=A+((r<<5)+-16<<2)|0;if(!ct)return;Tt=r<<4;t=n[Ba>>2]|0;i=n[Ba+4>>2]|0;a=n[Ba+8>>2]|0;o=n[Ba+12>>2]|0;f=n[Ba+16>>2]|0;u=n[Ba+20>>2]|0;c=n[Ba+24>>2]|0;s=n[Ba+28>>2]|0;l=n[Ba+32>>2]|0;w=n[Ba+36>>2]|0;h=n[Ba+40>>2]|0;v=n[Ba+44>>2]|0;d=n[Ba+48>>2]|0;y=n[Ba+52>>2]|0;p=n[Ba+56>>2]|0;g=n[Ba+60>>2]|0;b=0;do{Xt=b<<4;ni=A+(Xt<<2)|0;li=n[ni>>2]^t;Li=n[ni+4>>2]^i;ji=n[ni+8>>2]^a;zi=n[ni+12>>2]^o;ca=n[ni+16>>2]^f;ka=n[ni+20>>2]^u;Ha=n[ni+24>>2]^c;Wa=n[ni+28>>2]^s;fo=n[ni+32>>2]^l;mo=n[ni+36>>2]^w;Bo=n[ni+40>>2]^h;Wo=n[ni+44>>2]^v;ff=n[ni+48>>2]^d;gf=n[ni+52>>2]^y;Bf=n[ni+56>>2]^p;Zf=n[ni+60>>2]^g;Wf=ff+li|0;wu=(Wf<<7|Wf>>>25)^ca;pu=wu+li|0;_u=(pu<<9|pu>>>23)^fo;Gu=_u+wu|0;F=(Gu<<13|Gu>>>19)^ff;j=F+_u|0;eA=(j<<18|j>>>14)^li;aA=ka+Li|0;kA=mo^(aA<<7|aA>>>25);EA=kA+ka|0;ZA=gf^(EA<<9|EA>>>23);WA=ZA+kA|0;we=(WA<<13|WA>>>19)^Li;pe=we+ZA|0;_e=(pe<<18|pe>>>14)^ka;Ge=Bo+Ha|0;nr=Bf^(Ge<<7|Ge>>>25);fr=nr+Bo|0;Lr=(fr<<9|fr>>>23)^ji;Kr=Lr+nr|0;Zr=(Kr<<13|Kr>>>19)^Ha;Yr=Zr+Lr|0;en=(Yr<<18|Yr>>>14)^Bo;rn=Zf+Wo|0;nn=(rn<<7|rn>>>25)^zi;tn=nn+Zf|0;an=(tn<<9|tn>>>23)^Wa;on=an+nn|0;fn=(on<<13|on>>>19)^Wo;un=fn+an|0;cn=(un<<18|un>>>14)^Zf;sn=eA+nn|0;ln=(sn<<7|sn>>>25)^we;wn=ln+eA|0;hn=(wn<<9|wn>>>23)^Lr;vn=hn+ln|0;dn=(vn<<13|vn>>>19)^nn;yn=dn+hn|0;pn=(yn<<18|yn>>>14)^eA;gn=_e+wu|0;bn=(gn<<7|gn>>>25)^Zr;mn=bn+_e|0;Mn=(mn<<9|mn>>>23)^an;kn=Mn+bn|0;Un=(kn<<13|kn>>>19)^wu;Ln=Un+Mn|0;In=(Ln<<18|Ln>>>14)^_e;xn=en+kA|0;En=(xn<<7|xn>>>25)^fn;Dn=En+en|0;Kn=(Dn<<9|Dn>>>23)^_u;Rn=Kn+En|0;Bn=(Rn<<13|Rn>>>19)^kA;Fn=Bn+Kn|0;On=(Fn<<18|Fn>>>14)^en;_n=cn+nr|0;Cn=(_n<<7|_n>>>25)^F;Hn=Cn+cn|0;jn=(Hn<<9|Hn>>>23)^ZA;qn=jn+Cn|0;Gn=(qn<<13|qn>>>19)^nr;Nn=Gn+jn|0;Pn=(Nn<<18|Nn>>>14)^cn;Sn=pn+Cn|0;Vn=(Sn<<7|Sn>>>25)^Un;Zn=Vn+pn|0;Qn=(Zn<<9|Zn>>>23)^Kn;Tn=Qn+Vn|0;Jn=(Tn<<13|Tn>>>19)^Cn;Yn=Jn+Qn|0;Wn=(Yn<<18|Yn>>>14)^pn;zn=In+ln|0;Xn=(zn<<7|zn>>>25)^Bn;$n=Xn+In|0;At=($n<<9|$n>>>23)^jn;et=At+Xn|0;rt=(et<<13|et>>>19)^ln;nt=rt+At|0;tt=(nt<<18|nt>>>14)^In;it=On+bn|0;at=(it<<7|it>>>25)^Gn;ot=at+On|0;ft=(ot<<9|ot>>>23)^hn;ut=ft+at|0;st=(ut<<13|ut>>>19)^bn;lt=st+ft|0;wt=(lt<<18|lt>>>14)^On;ht=Pn+En|0;vt=(ht<<7|ht>>>25)^dn;dt=vt+Pn|0;yt=(dt<<9|dt>>>23)^Mn;pt=yt+vt|0;gt=(pt<<13|pt>>>19)^En;bt=gt+yt|0;mt=(bt<<18|bt>>>14)^Pn;Mt=Wn+vt|0;kt=(Mt<<7|Mt>>>25)^rt;Ut=kt+Wn|0;Lt=(Ut<<9|Ut>>>23)^ft;It=Lt+kt|0;xt=(It<<13|It>>>19)^vt;Et=xt+Lt|0;Dt=(Et<<18|Et>>>14)^Wn;Kt=tt+Vn|0;Rt=(Kt<<7|Kt>>>25)^st;Bt=Rt+tt|0;Ft=(Bt<<9|Bt>>>23)^yt;Ot=Ft+Rt|0;_t=(Ot<<13|Ot>>>19)^Vn;Ct=_t+Ft|0;Ht=(Ct<<18|Ct>>>14)^tt;jt=wt+Xn|0;qt=(jt<<7|jt>>>25)^gt;Gt=qt+wt|0;Nt=(Gt<<9|Gt>>>23)^Qn;Pt=Nt+qt|0;St=(Pt<<13|Pt>>>19)^Xn;Vt=St+Nt|0;Zt=(Vt<<18|Vt>>>14)^wt;Qt=mt+at|0;Jt=(Qt<<7|Qt>>>25)^Jn;Yt=Jt+mt|0;Wt=(Yt<<9|Yt>>>23)^At;zt=Wt+Jt|0;$t=(zt<<13|zt>>>19)^at;Ai=$t+Wt|0;ei=(Ai<<18|Ai>>>14)^mt;ri=Dt+Jt|0;ti=(ri<<7|ri>>>25)^_t;ii=ti+Dt|0;ai=(ii<<9|ii>>>23)^Nt;oi=ai+ti|0;fi=(oi<<13|oi>>>19)^Jt;ui=fi+ai|0;ci=(ui<<18|ui>>>14)^Dt;si=Ht+kt|0;wi=(si<<7|si>>>25)^St;hi=wi+Ht|0;vi=(hi<<9|hi>>>23)^Wt;di=vi+wi|0;yi=(di<<13|di>>>19)^kt;pi=yi+vi|0;gi=(pi<<18|pi>>>14)^Ht;bi=Zt+Rt|0;mi=(bi<<7|bi>>>25)^$t;Mi=mi+Zt|0;ki=(Mi<<9|Mi>>>23)^Lt;Ui=ki+mi|0;Ii=(Ui<<13|Ui>>>19)^Rt;xi=Ii+ki|0;Ei=(xi<<18|xi>>>14)^Zt;Di=ei+qt|0;Ki=(Di<<7|Di>>>25)^xt;Ri=Ki+ei|0;Bi=(Ri<<9|Ri>>>23)^Ft;Fi=Bi+Ki|0;Oi=(Fi<<13|Fi>>>19)^qt;_i=Oi+Bi|0;Ci=(_i<<18|_i>>>14)^ei;Hi=ci+Ki|0;qi=(Hi<<7|Hi>>>25)^yi;Gi=qi+ci|0;Ni=(Gi<<9|Gi>>>23)^ki;Pi=Ni+qi|0;Si=(Pi<<13|Pi>>>19)^Ki;Vi=Si+Ni|0;Zi=(Vi<<18|Vi>>>14)^ci;Qi=gi+ti|0;Ti=(Qi<<7|Qi>>>25)^Ii;Ji=Ti+gi|0;Yi=(Ji<<9|Ji>>>23)^Bi;Wi=Yi+Ti|0;Xi=(Wi<<13|Wi>>>19)^ti;$i=Xi+Yi|0;Aa=($i<<18|$i>>>14)^gi;ea=Ei+wi|0;ra=(ea<<7|ea>>>25)^Oi;na=ra+Ei|0;ta=(na<<9|na>>>23)^ai;ia=ta+ra|0;aa=(ia<<13|ia>>>19)^wi;oa=aa+ta|0;fa=(oa<<18|oa>>>14)^Ei;ua=Ci+mi|0;sa=(ua<<7|ua>>>25)^fi;la=sa+Ci|0;wa=(la<<9|la>>>23)^vi;ha=wa+sa|0;va=(ha<<13|ha>>>19)^mi;da=va+wa|0;ya=(da<<18|da>>>14)^Ci;pa=Zi+sa|0;ga=(pa<<7|pa>>>25)^Xi;ba=ga+Zi|0;ma=(ba<<9|ba>>>23)^ta;Ma=ma+ga|0;Ua=(Ma<<13|Ma>>>19)^sa;La=Ua+ma|0;Ia=(La<<18|La>>>14)^Zi;xa=Aa+qi|0;Ea=(xa<<7|xa>>>25)^aa;Da=Ea+Aa|0;Ka=(Da<<9|Da>>>23)^wa;Ra=Ka+Ea|0;Fa=(Ra<<13|Ra>>>19)^qi;Oa=Fa+Ka|0;_a=(Oa<<18|Oa>>>14)^Aa;Ca=fa+Ti|0;ja=(Ca<<7|Ca>>>25)^va;qa=ja+fa|0;Ga=(qa<<9|qa>>>23)^Ni;Na=Ga+ja|0;Pa=(Na<<13|Na>>>19)^Ti;Sa=Pa+Ga|0;Va=(Sa<<18|Sa>>>14)^fa;Za=ya+ra|0;Qa=(Za<<7|Za>>>25)^Si;Ta=Qa+ya|0;Ja=(Ta<<9|Ta>>>23)^Yi;Ya=Ja+Qa|0;za=(Ya<<13|Ya>>>19)^ra;Xa=za+Ja|0;$a=(Xa<<18|Xa>>>14)^ya;Ao=Ia+Qa|0;eo=(Ao<<7|Ao>>>25)^Fa;ro=eo+Ia|0;no=(ro<<9|ro>>>23)^Ga;to=no+eo|0;io=(to<<13|to>>>19)^Qa;ao=io+no|0;oo=_a+ga|0;uo=(oo<<7|oo>>>25)^Pa;co=uo+_a|0;so=(co<<9|co>>>23)^Ja;lo=so+uo|0;wo=(lo<<13|lo>>>19)^ga;ho=wo+so|0;vo=Va+Ea|0;yo=(vo<<7|vo>>>25)^za;po=yo+Va|0;go=(po<<9|po>>>23)^ma;bo=go+yo|0;Mo=(bo<<13|bo>>>19)^Ea;ko=Mo+go|0;Uo=$a+ja|0;Lo=(Uo<<7|Uo>>>25)^Ua;Io=Lo+$a|0;xo=(Io<<9|Io>>>23)^Ka;Eo=xo+Lo|0;Do=(Eo<<13|Eo>>>19)^ja;Ko=Do+xo|0;Ro=((ao<<18|ao>>>14)^Ia)+li|0;Fo=eo+Li|0;Oo=no+ji|0;_o=io+zi|0;Co=wo+ca|0;Ho=((ho<<18|ho>>>14)^_a)+ka|0;jo=uo+Ha|0;qo=so+Wa|0;Go=go+fo|0;No=Mo+mo|0;Po=((ko<<18|ko>>>14)^Va)+Bo|0;So=yo+Wo|0;Vo=Lo+ff|0;Zo=xo+gf|0;Qo=Do+Bf|0;To=((Ko<<18|Ko>>>14)^$a)+Zf|0;Jo=b<<3;Yo=e+(Jo<<2)|0;n[Yo>>2]=Ro;n[Yo+4>>2]=Fo;n[Yo+8>>2]=Oo;n[Yo+12>>2]=_o;n[Yo+16>>2]=Co;n[Yo+20>>2]=Ho;n[Yo+24>>2]=jo;n[Yo+28>>2]=qo;n[Yo+32>>2]=Go;n[Yo+36>>2]=No;n[Yo+40>>2]=Po;n[Yo+44>>2]=So;n[Yo+48>>2]=Vo;n[Yo+52>>2]=Zo;n[Yo+56>>2]=Qo;n[Yo+60>>2]=To;zo=A+((Xt|16)<<2)|0;Xo=Ro^n[zo>>2];$o=Fo^n[zo+4>>2];Af=Oo^n[zo+8>>2];ef=_o^n[zo+12>>2];rf=Co^n[zo+16>>2];nf=Ho^n[zo+20>>2];tf=jo^n[zo+24>>2];af=qo^n[zo+28>>2];of=Go^n[zo+32>>2];uf=No^n[zo+36>>2];cf=Po^n[zo+40>>2];sf=So^n[zo+44>>2];lf=Vo^n[zo+48>>2];wf=Zo^n[zo+52>>2];hf=Qo^n[zo+56>>2];vf=To^n[zo+60>>2];df=Xo+lf|0;yf=(df<<7|df>>>25)^rf;pf=yf+Xo|0;bf=(pf<<9|pf>>>23)^of;mf=bf+yf|0;Mf=(mf<<13|mf>>>19)^lf;kf=Mf+bf|0;Uf=(kf<<18|kf>>>14)^Xo;Lf=nf+$o|0;If=(Lf<<7|Lf>>>25)^uf;xf=If+nf|0;Ef=(xf<<9|xf>>>23)^wf;Df=Ef+If|0;Kf=(Df<<13|Df>>>19)^$o;Rf=Kf+Ef|0;Ff=(Rf<<18|Rf>>>14)^nf;Of=cf+tf|0;_f=(Of<<7|Of>>>25)^hf;Cf=_f+cf|0;Hf=(Cf<<9|Cf>>>23)^Af;jf=Hf+_f|0;qf=(jf<<13|jf>>>19)^tf;Gf=qf+Hf|0;Nf=(Gf<<18|Gf>>>14)^cf;Pf=vf+sf|0;Sf=(Pf<<7|Pf>>>25)^ef;Vf=Sf+vf|0;Qf=(Vf<<9|Vf>>>23)^af;Tf=Qf+Sf|0;Jf=(Tf<<13|Tf>>>19)^sf;Yf=Jf+Qf|0;zf=(Yf<<18|Yf>>>14)^vf;Xf=Uf+Sf|0;$f=(Xf<<7|Xf>>>25)^Kf;Au=$f+Uf|0;eu=(Au<<9|Au>>>23)^Hf;ru=eu+$f|0;nu=(ru<<13|ru>>>19)^Sf;tu=nu+eu|0;iu=(tu<<18|tu>>>14)^Uf;au=Ff+yf|0;ou=(au<<7|au>>>25)^qf;fu=ou+Ff|0;uu=(fu<<9|fu>>>23)^Qf;cu=uu+ou|0;su=(cu<<13|cu>>>19)^yf;lu=su+uu|0;hu=(lu<<18|lu>>>14)^Ff;vu=Nf+If|0;du=(vu<<7|vu>>>25)^Jf;yu=du+Nf|0;gu=(yu<<9|yu>>>23)^bf;bu=gu+du|0;mu=(bu<<13|bu>>>19)^If;Mu=mu+gu|0;ku=(Mu<<18|Mu>>>14)^Nf;Uu=zf+_f|0;Lu=(Uu<<7|Uu>>>25)^Mf;Iu=Lu+zf|0;xu=(Iu<<9|Iu>>>23)^Ef;Eu=xu+Lu|0;Du=(Eu<<13|Eu>>>19)^_f;Ku=Du+xu|0;Ru=(Ku<<18|Ku>>>14)^zf;Bu=iu+Lu|0;Fu=(Bu<<7|Bu>>>25)^su;Ou=Fu+iu|0;Cu=(Ou<<9|Ou>>>23)^gu;Hu=Cu+Fu|0;ju=(Hu<<13|Hu>>>19)^Lu;qu=ju+Cu|0;Nu=(qu<<18|qu>>>14)^iu;Pu=hu+$f|0;Su=(Pu<<7|Pu>>>25)^mu;Vu=Su+hu|0;m=(Vu<<9|Vu>>>23)^xu;M=m+Su|0;k=(M<<13|M>>>19)^$f;U=k+m|0;L=(U<<18|U>>>14)^hu;I=ku+ou|0;x=(I<<7|I>>>25)^Du;E=x+ku|0;D=(E<<9|E>>>23)^eu;K=D+x|0;R=(K<<13|K>>>19)^ou;B=R+D|0;O=(B<<18|B>>>14)^ku;_=Ru+du|0;C=(_<<7|_>>>25)^nu;H=C+Ru|0;q=(H<<9|H>>>23)^uu;G=q+C|0;N=(G<<13|G>>>19)^du;P=N+q|0;S=(P<<18|P>>>14)^Ru;V=Nu+C|0;Z=(V<<7|V>>>25)^k;Q=Z+Nu|0;T=(Q<<9|Q>>>23)^D;J=T+Z|0;Y=(J<<13|J>>>19)^C;W=Y+T|0;z=(W<<18|W>>>14)^Nu;X=L+Fu|0;$=(X<<7|X>>>25)^R;AA=$+L|0;rA=(AA<<9|AA>>>23)^q;nA=rA+$|0;tA=(nA<<13|nA>>>19)^Fu;iA=tA+rA|0;oA=(iA<<18|iA>>>14)^L;fA=O+Su|0;uA=(fA<<7|fA>>>25)^N;cA=uA+O|0;sA=(cA<<9|cA>>>23)^Cu;lA=sA+uA|0;wA=(lA<<13|lA>>>19)^Su;hA=wA+sA|0;vA=(hA<<18|hA>>>14)^O;dA=S+x|0;yA=(dA<<7|dA>>>25)^ju;pA=yA+S|0;gA=(pA<<9|pA>>>23)^m;bA=gA+yA|0;mA=(bA<<13|bA>>>19)^x;MA=mA+gA|0;UA=(MA<<18|MA>>>14)^S;LA=z+yA|0;IA=(LA<<7|LA>>>25)^tA;xA=IA+z|0;DA=(xA<<9|xA>>>23)^sA;KA=DA+IA|0;RA=(KA<<13|KA>>>19)^yA;BA=RA+DA|0;FA=(BA<<18|BA>>>14)^z;OA=oA+Z|0;_A=(OA<<7|OA>>>25)^wA;CA=_A+oA|0;HA=(CA<<9|CA>>>23)^gA;jA=HA+_A|0;qA=(jA<<13|jA>>>19)^Z;GA=qA+HA|0;NA=(GA<<18|GA>>>14)^oA;PA=vA+$|0;SA=(PA<<7|PA>>>25)^mA;VA=SA+vA|0;QA=(VA<<9|VA>>>23)^T;TA=QA+SA|0;JA=(TA<<13|TA>>>19)^$;YA=JA+QA|0;zA=(YA<<18|YA>>>14)^vA;XA=UA+uA|0;$A=(XA<<7|XA>>>25)^Y;Ae=$A+UA|0;ee=(Ae<<9|Ae>>>23)^rA;re=ee+$A|0;ne=(re<<13|re>>>19)^uA;te=ne+ee|0;ie=(te<<18|te>>>14)^UA;ae=FA+$A|0;oe=(ae<<7|ae>>>25)^qA;fe=oe+FA|0;ue=(fe<<9|fe>>>23)^QA;ce=ue+oe|0;se=(ce<<13|ce>>>19)^$A;le=se+ue|0;he=(le<<18|le>>>14)^FA;ve=NA+IA|0;de=(ve<<7|ve>>>25)^JA;ye=de+NA|0;ge=(ye<<9|ye>>>23)^ee;be=ge+de|0;me=(be<<13|be>>>19)^IA;Me=me+ge|0;ke=(Me<<18|Me>>>14)^NA;Ue=zA+_A|0;Le=(Ue<<7|Ue>>>25)^ne;Ie=Le+zA|0;xe=(Ie<<9|Ie>>>23)^DA;Ee=xe+Le|0;De=(Ee<<13|Ee>>>19)^_A;Ke=De+xe|0;Re=(Ke<<18|Ke>>>14)^zA;Be=ie+SA|0;Fe=(Be<<7|Be>>>25)^RA;Oe=Fe+ie|0;Ce=(Oe<<9|Oe>>>23)^HA;He=Ce+Fe|0;je=(He<<13|He>>>19)^SA;qe=je+Ce|0;Ne=(qe<<18|qe>>>14)^ie;Pe=he+Fe|0;Se=(Pe<<7|Pe>>>25)^me;Ve=Se+he|0;Ze=(Ve<<9|Ve>>>23)^xe;Qe=Ze+Se|0;Te=(Qe<<13|Qe>>>19)^Fe;Je=Te+Ze|0;Ye=(Je<<18|Je>>>14)^he;We=ke+oe|0;ze=(We<<7|We>>>25)^De;Xe=ze+ke|0;$e=(Xe<<9|Xe>>>23)^Ce;Ar=$e+ze|0;er=(Ar<<13|Ar>>>19)^oe;rr=er+$e|0;tr=(rr<<18|rr>>>14)^ke;ir=Re+de|0;ar=(ir<<7|ir>>>25)^je;or=ar+Re|0;ur=(or<<9|or>>>23)^ue;cr=ur+ar|0;sr=(cr<<13|cr>>>19)^de;lr=sr+ur|0;wr=(lr<<18|lr>>>14)^Re;hr=Ne+Le|0;vr=(hr<<7|hr>>>25)^se;dr=vr+Ne|0;yr=(dr<<9|dr>>>23)^ge;pr=yr+vr|0;gr=(pr<<13|pr>>>19)^Le;br=gr+yr|0;mr=(br<<18|br>>>14)^Ne;Mr=Ye+vr|0;kr=(Mr<<7|Mr>>>25)^er;Ur=kr+Ye|0;Ir=(Ur<<9|Ur>>>23)^ur;xr=Ir+kr|0;Er=(xr<<13|xr>>>19)^vr;Dr=Er+Ir|0;Rr=tr+Se|0;Br=(Rr<<7|Rr>>>25)^sr;Fr=Br+tr|0;Or=(Fr<<9|Fr>>>23)^yr;_r=Or+Br|0;Cr=(_r<<13|_r>>>19)^Se;Hr=Cr+Or|0;jr=wr+ze|0;qr=(jr<<7|jr>>>25)^gr;Gr=qr+wr|0;Nr=(Gr<<9|Gr>>>23)^Ze;Pr=Nr+qr|0;Sr=(Pr<<13|Pr>>>19)^ze;Vr=Sr+Nr|0;Qr=mr+ar|0;Tr=(Qr<<7|Qr>>>25)^Te;Jr=Tr+mr|0;Wr=(Jr<<9|Jr>>>23)^$e;zr=Wr+Tr|0;Xr=(zr<<13|zr>>>19)^ar;$r=Xr+Wr|0;t=((Dr<<18|Dr>>>14)^Ye)+Xo|0;i=kr+$o|0;a=Ir+Af|0;o=Er+ef|0;f=Cr+rf|0;u=((Hr<<18|Hr>>>14)^tr)+nf|0;c=Br+tf|0;s=Or+af|0;l=Nr+of|0;w=Sr+uf|0;h=((Vr<<18|Vr>>>14)^wr)+cf|0;v=qr+sf|0;d=Tr+lf|0;y=Wr+wf|0;p=Xr+hf|0;g=(($r<<18|$r>>>14)^mr)+vf|0;An=e+(Jo+Tt<<2)|0;n[An>>2]=t;n[An+4>>2]=i;n[An+8>>2]=a;n[An+12>>2]=o;n[An+16>>2]=f;n[An+20>>2]=u;n[An+24>>2]=c;n[An+28>>2]=s;n[An+32>>2]=l;n[An+36>>2]=w;n[An+40>>2]=h;n[An+44>>2]=v;n[An+48>>2]=d;n[An+52>>2]=y;n[An+56>>2]=p;n[An+60>>2]=g;b=b+2|0}while(b>>>0>>0);return}return{_SMix:o}};return{create:A,getHeap:e}}function e(){"use strict";function A(A){for(var e=new Uint8Array(A),n=atob("mC+KQpFEN3HP+8C1pdu16VvCVjnxEfFZpII/ktVeHKuYqgfYAVuDEr6FMSTDfQxVdF2+cv6x3oCnBtybdPGbwcFpm+SGR77vxp3BD8yhDCRvLOktqoR0StypsFzaiPl2UlE+mG3GMajIJwOwx39Zv/ML4MZHkafVUWPKBmcpKRSFCrcnOCEbLvxtLE0TDThTVHMKZbsKanYuycKBhSxykqHov6JLZhqocItLwqNRbMcZ6JLRJAaZ1oU1DvRwoGoQFsGkGQhsNx5Md0gntbywNLMMHDlKqthOT8qcW/NvLmjugo90b2OleBR4yIQIAseM+v++kOtsUKT3o/m+8nhxxgUAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAYAcAAAAEAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAr/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAQAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="),t=8,i=0;i>>0>64){n[191]=0;n[183]=1779033703;n[184]=-1150833019;n[185]=1013904242;n[186]=-1521486534;n[187]=1359893119;n[188]=-1694144372;n[189]=528734635;n[190]=1541459225;o(732,A,e);c(1816,732);l=1816;w=32}else{l=A;w=e}n[191]=0;n[183]=1779033703;n[184]=-1150833019;n[185]=1013904242;n[186]=-1521486534;n[187]=1359893119;n[188]=-1694144372;n[189]=528734635;n[190]=1541459225;g=1752;m=g+64|0;do{t[g>>0]=54;g=g+1|0}while((g|0)<(m|0));if(!w)h=1;else{t[1752]=t[l>>0]^54;if((w|0)==1)h=0;else{t[1753]=t[l+1>>0]^54;if((w|0)==2)h=0;else{t[1754]=t[l+2>>0]^54;if((w|0)==3)h=0;else{v=3;do{t[1752+v>>0]=t[l+v>>0]^t[1752+v>>0];v=v+1|0}while((v|0)!=(w|0));h=0}}}}o(732,1752,64);n[216]=0;n[208]=1779033703;n[209]=-1150833019;n[210]=1013904242;n[211]=-1521486534;n[212]=1359893119;n[213]=-1694144372;n[214]=528734635;n[215]=1541459225;g=1752;m=g+64|0;do{t[g>>0]=92;g=g+1|0}while((g|0)<(m|0));if(!h){t[1752]=t[l>>0]^92;if((w|0)!=1){t[1753]=t[l+1>>0]^92;if((w|0)!=2){t[1754]=t[l+2>>0]^92;if((w|0)!=3){d=3;do{t[1752+d>>0]=t[l+d>>0]^t[1752+d>>0];d=d+1|0}while((d|0)!=(w|0))}}}}o(832,1752,64);o(732,r,i);if(u|0){s=0;p=0;do{s=s+1|0;t[1687]=s;t[1686]=s>>>8;t[1685]=s>>>16;t[1684]=s>>>24;f(932,732,200)|0;o(932,1684,4);c(1848,932);o(1032,1848,32);c(1688,1032);g=1720;b=1688;m=g+32|0;do{t[g>>0]=t[b>>0]|0;g=g+1|0;b=b+1|0}while((g|0)<(m|0));y=u-p|0;f(a+p|0,1720,(y>>>0>32?32:y)|0)|0;p=s<<5}while(p>>>0>>0)}return}function o(A,e,r){A=A|0;e=e|0;r=r|0;var t=0,i=0,a=0,o=0,c=0,s=0,l=0,w=0,h=0,v=0,d=0,y=0,p=0,g=0,b=0;do if(r|0){p=A+32|0;g=n[p>>2]|0;b=g>>>3&63;n[p>>2]=g+(r<<3);c=64-b|0;s=A+36+b|0;if(c>>>0>r>>>0){f(s|0,e|0,r|0)|0;break}f(s|0,e|0,c|0)|0;l=A+36|0;u(A,l);w=e+c|0;h=r-c|0;if(h>>>0>63){v=b+r+-128|0;d=v&-64;y=d+128-b|0;a=h;o=w;while(1){u(A,o);a=a+-64|0;if(a>>>0<=63)break;else o=o+64|0}t=e+y|0;i=v-d|0}else{t=w;i=h}f(l|0,t|0,i|0)|0}while(0);return}function f(A,e,r){A=A|0;e=e|0;r=r|0;var i=0;i=A|0;if((A&3)==(e&3)){while(A&3){if(!r)return i|0;t[A>>0]=t[e>>0]|0;A=A+1|0;e=e+1|0;r=r-1|0}while((r|0)>=4){n[A>>2]=n[e>>2];A=A+4|0;e=e+4|0;r=r-4|0}}while((r|0)>0){t[A>>0]=t[e>>0]|0;A=A+1|0;e=e+1|0;r=r-1|0}return i|0}function u(A,e){A=A|0;e=e|0;var r=0,t=0,a=0,o=0,f=0,u=0,c=0,s=0,l=0,w=0,h=0,v=0,d=0,y=0,p=0,g=0,b=0;r=0;do{w=e+(r<<2)|0;n[444+(r<<2)>>2]=(i[w+2>>0]|0)<<8|(i[w+3>>0]|0)|(i[w+1>>0]|0)<<16|(i[w>>0]|0)<<24;r=r+1|0}while((r|0)!=16);t=16;h=n[111]|0;do{l=n[444+(t+-2<<2)>>2]|0;b=h;h=n[444+(t+-15<<2)>>2]|0;n[444+(t<<2)>>2]=b+(n[444+(t+-7<<2)>>2]|0)+((l>>>19|l<<13)^l>>>10^(l>>>17|l<<15))+((h>>>18|h<<14)^h>>>3^(h>>>7|h<<25));t=t+1|0}while((t|0)!=64);n[175]=n[A>>2];n[176]=n[A+4>>2];n[177]=n[A+8>>2];n[178]=n[A+12>>2];n[179]=n[A+16>>2];n[180]=n[A+20>>2];n[181]=n[A+24>>2];n[182]=n[A+28>>2];a=0;do{v=700+(((71-a|0)%8|0)<<2)|0;d=n[700+(((68-a|0)%8|0)<<2)>>2]|0;y=n[700+(((70-a|0)%8|0)<<2)>>2]|0;p=(n[444+(a<<2)>>2]|0)+(n[v>>2]|0)+((d>>>6|d<<26)^(d>>>11|d<<21)^(d>>>25|d<<7))+(n[8+(a<<2)>>2]|0)+((y^n[700+(((69-a|0)%8|0)<<2)>>2])&d^y)|0;g=n[700+(((64-a|0)%8|0)<<2)>>2]|0;f=n[700+(((65-a|0)%8|0)<<2)>>2]|0;u=n[700+(((66-a|0)%8|0)<<2)>>2]|0;c=700+(((67-a|0)%8|0)<<2)|0;n[c>>2]=(n[c>>2]|0)+p;n[v>>2]=((g>>>2|g<<30)^(g>>>13|g<<19)^(g>>>22|g<<10))+p+((u|f)&g|u&f);a=a+1|0}while((a|0)!=64);o=0;do{s=A+(o<<2)|0;n[s>>2]=(n[s>>2]|0)+(n[700+(o<<2)>>2]|0);o=o+1|0}while((o|0)!=8);return}function c(A,e){A=A|0;e=e|0;var r=0,i=0,a=0,f=0,u=0;u=n[e+32>>2]|0;t[1139]=u;t[1138]=u>>>8;t[1137]=u>>>16;t[1136]=u>>>24;i=u>>>3&63;n[283]=0;o(e,380,(i>>>0<56?56:120)-i|0);o(e,1132,8);r=0;do{a=A+(r<<2)|0;f=n[e+(r<<2)>>2]|0;t[a+3>>0]=f;t[a+2>>0]=f>>>8;t[a+1>>0]=f>>>16;t[a>>0]=f>>>24;r=r+1|0}while((r|0)!=8);return}return{_PBKDF2_OneIter:a}};return{create:A,getHeap:e}}var r,n=function(){"use strict";function e(){}function r(A,r){u=A,c=r,f=128*r,o=d+f;var n=d+f*(3+A),t=16777216*Math.ceil(n/16777216);if(!s||s.byteLength>1<<1}function i(){var A,e=h,r=w,n=r+e,i=0;switch(n>=u&&(n=u,e=n-r,i=v?1:2),2!=i&&g(e),0==i&&(A=b()),p._SMix(u,c,d,o,v?0:1,r,n),i){case 0:A=b()-A,w=n,t(100/A);break;case 1:w=0,v=!1,t(.7);break;case 2:var a=s.slice(d,d+f);g({state:"done",step:e,output:a},[a])}}function a(A){var e=A.data;if(e===!0)return void i();switch(e.cmd){case"task":n(e.input),i();break;case"config":var t=r(e.N,e.r);g({state:t?"ready":"fail"});break;case"free":p=l=s=null}}var o,f,u,c,s,l,w,h,v,d=64,y=A(),p=null,g=self.postMessage,b=Date.now;addEventListener("message",a)};!function(r){function t(A,e,r,n,t,i,a){L=128*e,h=r,E=r*A*2,I=n,K=0;var o=O.getHeap();p=o,o+=t,g=o,o+=i,b=o,o+=a,m=o,o+=L*r,o=65536*Math.ceil(o/65536),(!d||d.byteLength32?U:32);var A=new Uint8Array(d,b,U);x=!1,w("oncomplete",A)}function s(A){var e=this,r=A.data;if("number"==typeof r){if(!x)return;e.postMessage(!0),D+=r;var n=Date.now();return n-F>50&&w("onprogress",D/E),void(F=n)}switch(r.state){case"done":if(!x)return;var t=new Uint8Array(r.output),i=e.tag;y.set(t,m+L*i),D+=r.step,++B==h?c():R
2 |
3 |
4 | const Child = function() {
5 | 'use strict';
6 |
7 | const enum CONST {
8 | INIT_ITER = 262144,
9 | INTERVAL = 100,
10 | STAGE2_RATE = 0.7,
11 | };
12 |
13 |
14 | let mPtrB = 64,
15 | mPtrXYV: number,
16 | mBlockSize: number,
17 | mArgN: number,
18 | mArgR: number,
19 |
20 | mAsmBuf: ArrayBuffer,
21 | mAsmU32: Uint32Array,
22 | mAsmU8: Uint8Array,
23 | mAsmMod = asm_smix(),
24 |
25 | // 利用类型推导机制,获取 asm.js 模块的实例类型
26 | mAsmObj = false ? mAsmMod.create(null) : null,
27 |
28 | mProgPos: number,
29 | mProgStep: number,
30 | mFirstStage: boolean;
31 |
32 |
33 | // Worker's postMessage
34 | const postMessage = self.postMessage;
35 | const getTick = Date.now;
36 |
37 |
38 | function prehot() {
39 | // test data
40 | // let s1 = mPtrB >> 2;
41 | // let s2 = mPtrB + mBlockSize >> 2;
42 | // let k = getTick();
43 |
44 | // for (let i = s1; i < s2; i++) {
45 | // k = (k * 13) >>> 0;
46 | // mAsmU32[i] = k;
47 | // }
48 |
49 | // first run
50 | // mAsmObj._SMix(mArgN, mArgR, mPtrB, mPtrXYV, 0, 0, 8192);
51 | // mAsmObj._SMix(mArgN, mArgR, mPtrB, mPtrXYV, 1, 0, 8192);
52 | }
53 |
54 |
55 | function config(N: number, r: number) {
56 | mArgN = N;
57 | mArgR = r;
58 | mBlockSize = 128 * r;
59 | mPtrXYV = mPtrB + mBlockSize;
60 |
61 | let size = mPtrB + mBlockSize * (3 + N);
62 |
63 | // 空间必须是 16M 的整数倍
64 | let need = Math.ceil(size / 16777216) * 16777216;
65 |
66 | if (!mAsmBuf || mAsmBuf.byteLength < need) {
67 | try {
68 | mAsmBuf = new ArrayBuffer(need);
69 | } catch (err) {
70 | return false;
71 | }
72 | // mAsmU32 = new Uint32Array(mAsmBuf);
73 | mAsmU8 = new Uint8Array(mAsmBuf);
74 |
75 | // create instance
76 | mAsmObj = mAsmMod.create(mAsmBuf);
77 | prehot();
78 | }
79 | return true;
80 | }
81 |
82 |
83 | function start(input: ArrayBuffer) {
84 | let buf = new Uint8Array(input);
85 | mAsmU8.set(buf, mPtrB);
86 |
87 | mProgStep = CONST.INIT_ITER / mArgR;
88 | mProgPos = 0;
89 | mFirstStage = true;
90 | }
91 |
92 |
93 | function setRate(p: number) {
94 | // 保持为 2 的倍数
95 | mProgStep = (mProgStep * p >> 1) << 1;
96 | }
97 |
98 | function advance() {
99 | let stp = mProgStep;
100 | let beg = mProgPos;
101 | let end = beg + stp;
102 | let last = 0;
103 | let t;
104 |
105 | if (end >= mArgN) {
106 | end = mArgN;
107 | stp = end - beg;
108 | last = mFirstStage ? 1 : 2;
109 | }
110 |
111 | if (last != 2) {
112 | // 在计算前通知主线程,并发布下一个任务,减少消息通信的间隔
113 | postMessage(stp);
114 | }
115 |
116 | if (last == 0) {
117 | t = getTick();
118 | }
119 | mAsmObj._SMix(mArgN, mArgR, mPtrB, mPtrXYV, mFirstStage ? 0 : 1, beg, end);
120 |
121 | switch (last) {
122 | case 0:
123 | t = getTick() - t;
124 | mProgPos = end;
125 |
126 | // 根据当前的计算速度,调整下一次的迭代次数
127 | // 使计算时间保持在 INTERVAL ms 左右
128 | setRate(CONST.INTERVAL / t);
129 | break;
130 |
131 | case 1:
132 | mProgPos = 0;
133 | mFirstStage = false;
134 | // SMix 第二步的迭代耗时更大
135 | setRate(CONST.STAGE2_RATE);
136 | break;
137 |
138 | case 2:
139 | // complete notify
140 | let output = mAsmBuf.slice(mPtrB, mPtrB + mBlockSize);
141 | postMessage({
142 | state: 'done',
143 | step: stp,
144 | output: output,
145 | }, [output]);
146 | break;
147 | }
148 | }
149 |
150 |
151 | function onMessage(e: MessageEvent) {
152 | let msg = e.data;
153 |
154 | // fast case
155 | if (msg === true) {
156 | advance();
157 | return;
158 | }
159 |
160 | switch (msg.cmd) {
161 | // case 'advance':
162 | // advance();
163 | // break;
164 |
165 | case 'task':
166 | start(msg.input);
167 | advance();
168 | break;
169 |
170 | case 'config':
171 | let success = config(msg.N, msg.r);
172 | postMessage({
173 | state: success ? 'ready' : 'fail',
174 | });
175 | break;
176 |
177 | case 'free':
178 | mAsmObj = mAsmU8 = mAsmBuf = null;
179 | break;
180 | }
181 | }
182 |
183 | addEventListener('message', onMessage);
184 | };
185 |
--------------------------------------------------------------------------------
/src/mod_asmjs/src/WorkerMain.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 | ///
5 |
6 |
7 | module WorkerMain {
8 |
9 | let mArgP: number,
10 | mWorkerUrl: string,
11 |
12 | mAsmBuf: ArrayBuffer,
13 | mAsmU8: Uint8Array,
14 | mAsmMod = asm_pbkdf2(),
15 |
16 | // 利用类型推导机制,获取 asm.js 模块的实例类型
17 | mAsmObj = false ? mAsmMod.create(null) : null,
18 |
19 | mPassPtr: number,
20 | mSaltPtr: number,
21 | mDkPtr: number,
22 | mBlksPtr: number,
23 |
24 | mPassLen: number,
25 | mSaltLen: number,
26 | mDkLen: number,
27 | mBlkLen: number,
28 |
29 | mThreads: number,
30 | mRunning: boolean,
31 | mWorkerPool: Worker[] = [],
32 |
33 | mIterMax: number,
34 | mIterCur: number,
35 |
36 | mReadyCounter: number,
37 | mDoingCounter: number,
38 | mDoneCounter: number,
39 |
40 | mLastNotify: number;
41 |
42 |
43 | export function config(
44 | N: number, r: number, P: number,
45 | thread: number,
46 | maxPassLen: number, maxSaltLen: number, maxDkLen: number
47 | ) {
48 | mBlkLen = 128 * r;
49 | mArgP = P;
50 | mIterMax = P * N * 2;
51 |
52 | mThreads = thread;
53 | mReadyCounter = 0;
54 |
55 | // pbkdf2 memory alloc
56 | let ptr = mAsmMod.getHeap();
57 |
58 | mPassPtr = ptr;
59 | ptr += maxPassLen;
60 |
61 | mSaltPtr = ptr;
62 | ptr += maxSaltLen;
63 |
64 | mDkPtr = ptr;
65 | ptr += maxDkLen;
66 |
67 | mBlksPtr = ptr;
68 | ptr += (mBlkLen * P);
69 |
70 | ptr = Math.ceil(ptr / 65536) * 65536;
71 |
72 | // init asm.js module
73 | if (!mAsmBuf || mAsmBuf.byteLength < ptr) {
74 | mAsmBuf = new ArrayBuffer(ptr);
75 | mAsmU8 = new Uint8Array(mAsmBuf);
76 | mAsmObj = mAsmMod.create(mAsmBuf);
77 | }
78 |
79 | if (!mWorkerUrl) {
80 | mWorkerUrl = createWorkerUrl();
81 | }
82 |
83 | for (let i = 0; i < mThreads; i++) {
84 | let worker = mWorkerPool[i];
85 | if (!worker) {
86 | worker = new Worker(mWorkerUrl);
87 | worker.onmessage = msgHander;
88 | worker['tag'] = 0;
89 | mWorkerPool[i] = worker;
90 | }
91 | worker.postMessage({
92 | cmd: 'config',
93 | N: N,
94 | r: r,
95 | });
96 | }
97 | }
98 |
99 | export function hash(passBin: Bytes, saltBin: Bytes, dkLen: number) {
100 | mAsmU8.set(passBin, mPassPtr);
101 | mAsmU8.set(saltBin, mSaltPtr);
102 |
103 | mPassLen = passBin.length;
104 | mSaltLen = saltBin.length;
105 | mDkLen = dkLen;
106 |
107 | mRunning = true;
108 | mIterCur = 0;
109 | mDoingCounter = 0;
110 | mDoneCounter = 0;
111 |
112 | // [B0, B1, ..., Bp] <- PBKDF2(pass, salt)
113 | mAsmObj._PBKDF2_OneIter(
114 | mPassPtr, mPassLen,
115 | mSaltPtr, mSaltLen,
116 | mBlksPtr, mBlkLen * mArgP
117 | );
118 |
119 | // console.log('smix pre:',
120 | // bytesToHex(mAsmU8, mBlksPtr, mBlkLen * mArgP)
121 | // );
122 |
123 | for (let i = 0; i < mThreads; i++) {
124 | task(mWorkerPool[i]);
125 | }
126 | }
127 |
128 | export function stop() {
129 | mRunning = false;
130 | }
131 |
132 | export function free() {
133 | mWorkerPool.forEach(w => {
134 | w.postMessage({
135 | cmd: 'free'
136 | });
137 | });
138 | }
139 |
140 | export function unload() {
141 | mWorkerPool.forEach(w => {
142 | w.terminate();
143 | });
144 | mWorkerPool = [];
145 | mAsmBuf = mAsmU8 = mAsmMod = null;
146 | URL.revokeObjectURL(mWorkerUrl);
147 | }
148 |
149 |
150 | function createWorkerUrl() {
151 | /**
152 | CODE GEN:
153 | (function Child(..) {
154 | ...
155 | })();
156 | function asm_smix() {
157 | ...
158 | }
159 | */
160 | let code = '(' + Child + ')();' + asm_smix;
161 |
162 | let blob = new Blob([code], {
163 | type: 'text/javascript'
164 | });
165 |
166 | return URL.createObjectURL(blob);
167 | }
168 |
169 | function complete() {
170 | // console.log('smix post:',
171 | // bytesToHex(mAsmU8, mBlksPtr, mBlkLen * mArgP)
172 | // );
173 |
174 | // final hash
175 | mAsmObj._PBKDF2_OneIter(
176 | mPassPtr, mPassLen,
177 | mBlksPtr, mBlkLen * mArgP,
178 | mDkPtr, mDkLen > 32 ? mDkLen : 32
179 | );
180 |
181 | // pass reference
182 | let dkBin = new Uint8Array(mAsmBuf, mDkPtr, mDkLen);
183 |
184 | mRunning = false;
185 | callback('oncomplete', dkBin);
186 | }
187 |
188 |
189 | function msgHander(e: MessageEvent) {
190 | let worker: Worker = this;
191 | let msg = e.data;
192 |
193 | // fast case
194 | if (typeof msg == 'number') {
195 | if (!mRunning) {
196 | return;
197 | }
198 | worker.postMessage(true);
199 |
200 | // progress
201 | mIterCur += msg;
202 |
203 | let now = Date.now();
204 | if (now - mLastNotify > 50) {
205 | callback('onprogress', mIterCur / mIterMax);
206 | }
207 | mLastNotify = now;
208 | return;
209 | }
210 |
211 | switch (msg.state) {
212 | case 'done':
213 | if (!mRunning) {
214 | return;
215 | }
216 | // Bi -> B'i
217 | let buf = new Uint8Array(msg.output);
218 | let id = worker['tag'];
219 | mAsmU8.set(buf, mBlksPtr + mBlkLen * id);
220 |
221 | mIterCur += msg.step;
222 |
223 | if (++mDoneCounter == mArgP) {
224 | complete();
225 | } else if (mDoingCounter < mArgP) {
226 | task(worker);
227 | }
228 | break;
229 |
230 | case 'ready':
231 | if (++mReadyCounter == mThreads) {
232 | callback('onready');
233 | }
234 | break;
235 |
236 | case 'fail':
237 | callback('onerror', 'memory alloc fail');
238 | break;
239 | }
240 | }
241 |
242 | function task(worker: Worker) {
243 | let ptrBi = mBlksPtr + mDoingCounter * mBlkLen;
244 | let bufBi = mAsmBuf.slice(ptrBi, ptrBi + mBlkLen);
245 |
246 | worker['tag'] = mDoingCounter++;
247 | worker.postMessage({
248 | cmd: 'task',
249 | input: bufBi,
250 | }, [bufBi]); // no copy
251 | }
252 |
253 |
254 | function bytesToHex(inBuf: Bytes, inPtr: number, inLen: number) : string {
255 | let str = '';
256 | for (let i = 0; i < inLen; i++) {
257 | let byte = inBuf[inPtr++];
258 | let hex = byte.toString(16);
259 | if (byte < 16) {
260 | hex = '0' + hex;
261 | }
262 | str += hex;
263 | }
264 | return str;
265 | }
266 |
267 |
268 | function callback(msg: string, data?) {
269 | let scrypt = window['scrypt'];
270 | if (scrypt) {
271 | scrypt.__asmjs_cb(msg, data);
272 | }
273 | }
274 |
275 | callback('onload', WorkerMain);
276 | }
--------------------------------------------------------------------------------
/src/mod_asmjs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "lib/smix.js",
4 | "lib/pbkdf2.js",
5 | "src/WorkerMain.ts"
6 | ],
7 | "compilerOptions": {
8 | "allowJs": true,
9 | "out": "debug/asmjs.js",
10 | "sourceMap": true,
11 | "inlineSources": true
12 | }
13 | }
--------------------------------------------------------------------------------
/src/mod_flash/.actionScriptProperties:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/mod_flash/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Scrypt
4 |
5 |
6 |
7 |
8 |
9 | com.adobe.flexbuilder.project.flexbuilder
10 |
11 |
12 |
13 |
14 |
15 | com.adobe.flexbuilder.project.actionscriptnature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/mod_flash/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | #Fri Jan 06 21:25:52 CST 2017
2 | eclipse.preferences.version=1
3 | encoding/=utf-8
4 |
--------------------------------------------------------------------------------
/src/mod_flash/Makefile:
--------------------------------------------------------------------------------
1 | FLASCC = ~/Crossbridge/sdk
2 | FLEX = /Applications/Adobe Flash Builder 4.7/sdks/4.6.0
3 |
4 |
5 | .PHONY: build
6 | build: lib/scrypt.swc
7 | mkdir -p bin-release
8 |
9 | "$(FLEX)/bin/mxmlc" \
10 | src/Scrypt.as \
11 | -o bin-release/Scrypt.swf \
12 | -library-path=lib \
13 | -swf-version=18 \
14 | -debug=false \
15 | -optimize
16 |
17 | python ./c-bind/swf2lzma.py \
18 | bin-release/Scrypt.swf ../../release/asset/flash.swf
19 |
20 |
21 | lib/scrypt.swc: c-bind/flascc.c ../c/pbkdf2.c ../c/smix.c
22 | $(FLASCC)/usr/bin/gcc \
23 | $^ \
24 | -o "$@" \
25 | -std=c99 -O4 \
26 | -flto-api=c-bind/exports.txt \
27 | -emit-swc=com.etherdream.scrypt
28 |
29 |
30 | .PHONY: lib
31 | lib: lib/scrypt.swc
32 |
33 |
34 | .PHONY: pthread_test
35 | pthread_test:
36 | $(FLASCC)/usr/bin/gcc \
37 | ../c/scrypt.c ../c/pbkdf2.c ../c/smix.c \
38 | -flto-api=c-bind/pthread-exports.txt \
39 | -DTEST \
40 | -std=c99 -O4 \
41 | -pthread \
42 | -emit-swf -swf-version=18 \
43 | -o pthread_test.swf
44 |
45 |
46 | .PHONY: clean
47 | clean:
48 | rm -rf bin-debug
49 | rm -rf bin-release
50 | rm lib/scrypt.swc
51 |
--------------------------------------------------------------------------------
/src/mod_flash/README.md:
--------------------------------------------------------------------------------
1 | # scrypt flash
2 |
3 | 该模块为不支持 asm.js 的浏览器提供兼容方案。
4 |
5 | 虽然 Flash 的性能远不如 asm.js(只有 60% 左右),但相比低版本浏览器的 JS 引擎还是快不少。
6 |
7 | 本项目使用 [CrossBridge](https://github.com/adobe-flash/crossbridge) 编译器(开源版的 FlasCC,更早的版本叫 Alchemy),将 C 代码编译成 `lib/scrypt.swc`,供 ActionScript 调用。
8 |
9 | CrossBridge 支持 pthread 库,可无需关心 Flash 的多线程实现细节,直接生成最终的 SWF 文件。
10 |
11 | 但是直接生成的 SWF 文件较大,性能也并没有变高(可通过 make pthread_test 测试)。因此本项目采用了类似 JS 的模型,手动控制多线程。
12 |
13 | 多线程使用 `lib/webworker.swc` 实现。该库封装了一个 HTML5 风格的 Worker,因此可以较大程度共享 JS 模块的代码逻辑。具体源码位于:[webworker.swc](https://github.com/EtherDream/webworker.swc) 项目。
14 |
15 | Flash Player 13+ 支持 LZMA 压缩格式的 SWF,但编译器默认没有开启,这里使用工具 [swf2lzma](https://github.com/jspiro/swf2lzma) 实现压缩,可减少 25% 左右的体积。
16 |
17 | 由于使用了 Shared ByteArray,因此最终 SWF 必须在 Flash Player 18+ 才能执行。
18 |
--------------------------------------------------------------------------------
/src/mod_flash/bin-debug/Scrypt.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherDream/WebScrypt/0620377a9d40372222d9268203c1fcd4354cb3ff/src/mod_flash/bin-debug/Scrypt.swf
--------------------------------------------------------------------------------
/src/mod_flash/bin-release/Scrypt.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherDream/WebScrypt/0620377a9d40372222d9268203c1fcd4354cb3ff/src/mod_flash/bin-release/Scrypt.swf
--------------------------------------------------------------------------------
/src/mod_flash/c-bind/exports.txt:
--------------------------------------------------------------------------------
1 | # built in symbols that must always be preserved
2 | malloc
3 | free
4 | memcpy
5 | memmove
6 |
--------------------------------------------------------------------------------
/src/mod_flash/c-bind/flascc.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "AS3/AS3.h"
6 | #include "../../c/pbkdf2.h"
7 | #include "../../c/smix.h"
8 |
9 |
10 | void AS_PBKDF2_OneIter() __attribute__((used,
11 | annotate("as3sig:public function C_PBKDF2_OneIter(passBuf:uint, passLen:uint, saltBuf:uint, saltLen:uint, dkBuf:uint, dkLen:uint) : void"),
12 | annotate("as3package:com.etherdream.scrypt")));
13 | void AS_PBKDF2_OneIter() {
14 | const uint8_t *passBuf_;
15 | const uint8_t *saltBuf_;
16 | uint8_t *dkBuf_;
17 |
18 | size_t passLen_;
19 | size_t saltLen_;
20 | size_t dkLen_;
21 |
22 | AS3_GetScalarFromVar(passBuf_, passBuf);
23 | AS3_GetScalarFromVar(passLen_, passLen);
24 | AS3_GetScalarFromVar(saltBuf_, saltBuf);
25 | AS3_GetScalarFromVar(saltLen_, saltLen);
26 | AS3_GetScalarFromVar(dkBuf_, dkBuf);
27 | AS3_GetScalarFromVar(dkLen_, dkLen);
28 |
29 | PBKDF2_OneIter(
30 | passBuf_, passLen_,
31 | saltBuf_, saltLen_,
32 | dkBuf_, dkLen_
33 | );
34 | }
35 |
36 |
37 | void AS_SMix() __attribute__((used,
38 | annotate("as3sig:public function C_SMix(N:uint, r:uint, B:uint, XYV:uint, stage:uint, beg:uint, end:uint) : void"),
39 | annotate("as3package:com.etherdream.scrypt")));
40 | void AS_SMix() {
41 | uint32_t N_, r_, B_, XYV_;
42 | uint32_t stage_, beg_, end_;
43 |
44 | AS3_GetScalarFromVar(N_, N);
45 | AS3_GetScalarFromVar(r_, r);
46 | AS3_GetScalarFromVar(B_, B);
47 | AS3_GetScalarFromVar(XYV_, XYV);
48 | AS3_GetScalarFromVar(stage_, stage);
49 | AS3_GetScalarFromVar(beg_, beg);
50 | AS3_GetScalarFromVar(end_, end);
51 |
52 | SMix(N_, r_, (void*) B_, (void*) XYV_, stage_, beg_, end_);
53 | }
54 |
55 |
56 | int main() {
57 | return 0;
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/src/mod_flash/c-bind/pthread-exports.txt:
--------------------------------------------------------------------------------
1 | #
2 | # =BEGIN MIT LICENSE
3 | #
4 | # The MIT License (MIT)
5 | #
6 | # Copyright (c) 2014 The CrossBridge Team
7 | #
8 | # Permission is hereby granted, free of charge, to any person obtaining a copy
9 | # of this software and associated documentation files (the "Software"), to deal
10 | # in the Software without restriction, including without limitation the rights
11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | # copies of the Software, and to permit persons to whom the Software is
13 | # furnished to do so, subject to the following conditions:
14 | #
15 | # The above copyright notice and this permission notice shall be included in
16 | # all copies or substantial portions of the Software.
17 | #
18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | # THE SOFTWARE.
25 | #
26 | # =END MIT LICENSE
27 | #
28 |
29 | # Purpose: List of symbols that must be always preserved using CrossBridge SDK
30 | # Author: Andras Csizmadia
31 | # Important: Always leave one blank line at the end, for appending custom symbols
32 |
33 | # built in symbols that must always be preserved
34 | _start1
35 | malloc
36 | free
37 | memcpy
38 | memmove
39 | flascc_uiTickProc
40 | _sync_synchronize
41 |
42 | # symbols for C++ exception handling
43 | _Unwind_SjLj_Register
44 | _Unwind_SjLj_Resume
45 | _Unwind_SjLj_Unregister
46 | _Unwind_SjLj_RaiseException
47 |
48 | # core specific symbols
49 | __hack_used
50 | _main
51 | main
52 |
53 | # symbols specific to glsl2agal
54 | compileShader
55 |
56 | # symbols specific to libMath
57 | __muldi3
58 | __umuldi3
59 | __udivdi3
60 | __umoddi3
61 |
62 | # symbols specific to libVGL
63 | __avm2_vgl_argb_buffer
64 | vglttyioctl
65 | vgl_cur_mx
66 | vgl_cur_my
67 |
68 | # custom symbols
69 |
--------------------------------------------------------------------------------
/src/mod_flash/c-bind/swf2lzma.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import os
4 | import pylzma
5 | import sys
6 | import struct
7 | import zlib
8 |
9 | def check(test, msg):
10 | test or exit(msg)
11 |
12 | def convert(infile, outfile):
13 | fi = open(infile, "rb")
14 | swf_size = os.path.getsize(infile)
15 | swf_data = fi.read()
16 | fi.close()
17 |
18 | check((swf_data[1] == 'W') and (swf_data[2] == 'S'), "not a SWF file")
19 | check((ord(swf_data[3]) >= 13), "only SWF version 13 or higher is supported")
20 | check((swf_data[0] != 'Z'), "already LZMA compressed")
21 |
22 | dfilesize = struct.unpack("> 8) & 0xFF
42 | zheader[10] = (zsize >> 16) & 0xFF
43 | zheader[11] = (zsize >> 24) & 0xFF
44 |
45 | fo = open(outfile, 'wb')
46 | fo.write(struct.pack("<12B", *zheader))
47 | fo.write(zdata)
48 | fo.close()
49 |
50 | print 'compression: %d%%' % round(100 - (100.0 * zsize / swf_size))
51 |
52 |
53 | # Format of SWF when LZMA is used:
54 | #
55 | # | 4 bytes | 4 bytes | 4 bytes | 5 bytes | n bytes | 6 bytes |
56 | # | 'ZWS'+version | scriptLen | compressedLen | LZMA props | LZMA data | LZMA end marker |
57 | #
58 | # scriptLen is the uncompressed length of the SWF data. Includes 4 bytes SWF header and
59 | # 4 bytes for scriptLen it
60 | #
61 | # compressedLen does not include header (4+4+4 bytes) or lzma props (5 bytes)
62 | # compressedLen does include LZMA end marker (6 bytes)
63 | if __name__ == "__main__":
64 | check((len(sys.argv) == 3), 'usage: swf2lzma input-swf output-swf')
65 |
66 | infile = sys.argv[1]
67 | check((infile[-4:].lower() == '.swf'), 'input file must be of type .swf')
68 |
69 | outfile = sys.argv[2]
70 | check((infile[-4:].lower() == '.swf'), 'output file must be of type .swf')
71 |
72 | convert(infile, outfile)
73 |
74 | sys.exit(0)
75 |
--------------------------------------------------------------------------------
/src/mod_flash/lib/WebWorker.swc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherDream/WebScrypt/0620377a9d40372222d9268203c1fcd4354cb3ff/src/mod_flash/lib/WebWorker.swc
--------------------------------------------------------------------------------
/src/mod_flash/lib/scrypt.swc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EtherDream/WebScrypt/0620377a9d40372222d9268203c1fcd4354cb3ff/src/mod_flash/lib/scrypt.swc
--------------------------------------------------------------------------------
/src/mod_flash/src/Child.as:
--------------------------------------------------------------------------------
1 | package
2 | {
3 | import com.etherdream.scrypt.*;
4 | import com.etherdream.webworker.*;
5 |
6 | import flash.utils.ByteArray;
7 | import flash.utils.getTimer;
8 |
9 |
10 | public class Child extends WebWorkerContext {
11 |
12 | static private const
13 | DEFAULT_ITER: uint = 131072,
14 | INTERVAL: uint = 200; // ms
15 |
16 | private var
17 | mPtrBXYV: uint,
18 | mBlkLen: uint,
19 | mArgN: uint,
20 | mArgR: uint,
21 | mCurSize: uint,
22 | mIOBuf: ByteArray,
23 | mProgPos: uint,
24 | mProgStep: uint,
25 | mFirstStage: Boolean;
26 |
27 |
28 | public function Child() {
29 | mIOBuf = getSharedObject('buf') as ByteArray;
30 | addEventListener('message', msgHander);
31 | }
32 |
33 | private function config(N: uint, r: uint) : void {
34 | mArgN = N;
35 | mArgR = r;
36 | mBlkLen = 128 * r;
37 |
38 | var size: uint = mBlkLen * (3 + N);
39 | if (size > mCurSize) {
40 | if (mPtrBXYV != 0) {
41 | CModule.free(mPtrBXYV);
42 | }
43 | mPtrBXYV = CModule.malloc(size);
44 | mCurSize = size;
45 | }
46 | }
47 |
48 | private function start(input: ByteArray) : void {
49 | // input data -> CModule
50 | mIOBuf.position = 0;
51 | CModule.writeBytes(mPtrBXYV, mBlkLen, mIOBuf);
52 |
53 | mProgStep = DEFAULT_ITER / mArgR;
54 | mProgPos = 0;
55 | mFirstStage = true;
56 | }
57 |
58 | private function setRate(p: Number) : void {
59 | mProgStep = (mProgStep * p >> 1) << 1;
60 | }
61 |
62 | private function advance() : void {
63 | var stp: uint = mProgStep
64 | var beg: uint = mProgPos;
65 | var end: uint = beg + stp;
66 | var last: uint = 0;
67 | var t: uint;
68 |
69 | if (end >= mArgN) {
70 | end = mArgN;
71 | stp = end - beg;
72 | last = mFirstStage ? 1 : 2;
73 | }
74 |
75 | if (last != 2) {
76 | // 在计算前通知主线程,并发布下一个任务,减少消息通信的间隔
77 | postMessage(stp);
78 | }
79 |
80 | if (last == 0) {
81 | t = getTimer();
82 | }
83 |
84 | C_SMix(mArgN, mArgR, mPtrBXYV, mPtrBXYV + mBlkLen,
85 | mFirstStage ? 0 : 1, beg, end);
86 |
87 | switch (last) {
88 | case 0:
89 | t = getTimer() - t;
90 | mProgPos = end;
91 |
92 | // 根据当前的计算速度,调整下一次的迭代次数
93 | // 使计算时间保持在 INTERVAL ms 左右
94 | setRate(INTERVAL / t);
95 | break;
96 |
97 | case 1:
98 | mProgPos = 0;
99 | mFirstStage = false;
100 | // SMix 第二步的迭代耗时更大
101 | setRate(0.7);
102 | break;
103 |
104 | case 2:
105 | // complete notify
106 | // CModule -> output data
107 | mIOBuf.position = 0;
108 | CModule.readBytes(mPtrBXYV, mBlkLen, mIOBuf);
109 |
110 | postMessage({
111 | state: 'done',
112 | step: stp
113 | });
114 | break;
115 | }
116 | }
117 |
118 | private function msgHander(e: WebWorkerEvent) : void {
119 | var msg: * = e.data;
120 |
121 | // fast case
122 | if (msg === true) {
123 | advance();
124 | return;
125 | }
126 |
127 | switch (msg.cmd) {
128 | // case 'advance':
129 | // advance();
130 | // break;
131 |
132 | case 'task':
133 | start(msg.input);
134 | advance();
135 | break;
136 |
137 | case 'config':
138 | config(msg.N, msg.r);
139 | postMessage({
140 | state: 'ready'
141 | });
142 | break;
143 |
144 | case 'free':
145 | mCurSize = 0;
146 | CModule.free(mPtrBXYV);
147 | break;
148 | }
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/mod_flash/src/Main.as:
--------------------------------------------------------------------------------
1 | package {
2 | // https://github.com/EtherDream/webworker.swc
3 | import com.etherdream.webworker.*;
4 | import com.etherdream.scrypt.*;
5 |
6 | import flash.external.ExternalInterface;
7 | import flash.system.Security;
8 | import flash.utils.ByteArray;
9 | import flash.utils.getTimer;
10 |
11 |
12 | public class Main {
13 |
14 | static private const CALLBACK: String = 'scrypt.__flash_cb';
15 |
16 | private var
17 | mArgP: uint,
18 | mWorkerBytes: ByteArray,
19 |
20 | mPassPtr: uint,
21 | mSaltPtr: uint,
22 | mDkPtr: uint,
23 | mBlksPtr: uint,
24 |
25 | mPassLen: uint,
26 | mSaltLen: uint,
27 | mDkLen: uint,
28 | mBlkLen: uint,
29 |
30 | mThreads: uint,
31 | mRunning: Boolean,
32 | mWorkerPool: Array = [],
33 |
34 | mIterMax: uint,
35 | mIterCur: uint,
36 |
37 | mReadyCounter: uint,
38 | mDoingCounter: uint,
39 | mDoneCounter: uint,
40 |
41 | mChunkPtr: uint,
42 | mChunkLen: uint,
43 | mLastNotify: uint;
44 |
45 |
46 |
47 | public function Main(workerBytes: ByteArray) {
48 | Security.allowDomain('*');
49 | mWorkerBytes = workerBytes;
50 |
51 | ExternalInterface.addCallback('config', config);
52 | ExternalInterface.addCallback('hash', hash);
53 | ExternalInterface.addCallback('cancel', cancel);
54 | ExternalInterface.addCallback('free', free);
55 |
56 | callback('onload');
57 | }
58 |
59 |
60 | private function log(str: *) : void {
61 | ExternalInterface.call('console.log', str);
62 | }
63 |
64 |
65 | private function callback(msg: String, ... args) : void {
66 | switch (args.length) {
67 | case 0:
68 | ExternalInterface.call(CALLBACK, msg);
69 | break;
70 | case 1:
71 | ExternalInterface.call(CALLBACK, msg, args[0]);
72 | break;
73 | // ...
74 | }
75 | }
76 |
77 |
78 | private function cancel() : void {
79 | mRunning = false;
80 | }
81 |
82 |
83 | private function free() : void {
84 | mWorkerPool.forEach(function(w: WebWorker) : void {
85 | w.postMessage({
86 | cmd: 'free'
87 | });
88 | });
89 | }
90 |
91 |
92 | private function config(
93 | N: uint, r: uint, P: uint,
94 | thread: uint,
95 | maxPassLen: uint, maxSaltLen: uint, maxDkLen: uint
96 | ) : void {
97 | mBlkLen = 128 * r;
98 | mArgP = P;
99 | mIterMax = P * N * 2;
100 |
101 | mThreads = thread;
102 | mReadyCounter = 0;
103 |
104 | // pbkdf2 memory alloc
105 | var size: uint = maxPassLen + maxSaltLen + maxDkLen + (mBlkLen * P);
106 | if (size > mChunkLen) {
107 | if (mChunkPtr) {
108 | CModule.free(mChunkPtr);
109 | }
110 | mChunkPtr = CModule.malloc(size);
111 | }
112 |
113 | var ptr: uint = mChunkPtr;
114 |
115 | mPassPtr = ptr;
116 | ptr += maxPassLen;
117 |
118 | mSaltPtr = ptr;
119 | ptr += maxSaltLen;
120 |
121 | mDkPtr = ptr;
122 | ptr += maxDkLen;
123 |
124 | mBlksPtr = ptr;
125 |
126 |
127 | for (var i: uint = 0; i < thread; i++) {
128 | var worker: WebWorker = mWorkerPool[i];
129 | if (!worker) {
130 | var buf: ByteArray = new ByteArray();
131 | buf['shareable'] = true;
132 |
133 | worker = new WebWorker(mWorkerBytes);
134 | worker.setSharedObject('buf', buf);
135 | worker.addEventListener('message', msgHander);
136 | worker.tag = {
137 | id: 0,
138 | buf: buf
139 | };
140 | mWorkerPool[i] = worker;
141 | }
142 | worker.postMessage({
143 | cmd: 'config',
144 | N: N,
145 | r: r
146 | });
147 | }
148 | }
149 |
150 |
151 | private function hash(passHex: String, saltHex: String, dkLen: Number) : void {
152 | mPassLen = hexToByte(passHex, CModule.ram, mPassPtr);
153 | mSaltLen = hexToByte(saltHex, CModule.ram, mSaltPtr);
154 | mDkLen = dkLen;
155 |
156 | mRunning = true;
157 | mIterCur = 0;
158 | mDoingCounter = 0;
159 | mDoneCounter = 0;
160 |
161 |
162 | // [B0, B1, ..., Bp] <- PBKDF2(pass, salt)
163 | C_PBKDF2_OneIter(
164 | mPassPtr, mPassLen,
165 | mSaltPtr, mSaltLen,
166 | mBlksPtr, mBlkLen * mArgP
167 | );
168 |
169 | // log('smix pre:' +
170 | // bytesToHex(CModule.ram, mBlksPtr, mBlkLen * mArgP)
171 | // );
172 |
173 | for (var i: uint = 0; i < mThreads; i++) {
174 | task(mWorkerPool[i]);
175 | }
176 | }
177 |
178 |
179 | private function complete() : void {
180 | // log('smix post:' +
181 | // bytesToHex(CModule.ram, mBlksPtr, mBlkLen * mArgP)
182 | // );
183 |
184 | // final hash
185 | C_PBKDF2_OneIter(
186 | mPassPtr, mPassLen,
187 | mBlksPtr, mBlkLen * mArgP,
188 | mDkPtr, mDkLen > 32 ? mDkLen : 32
189 | );
190 |
191 | var dkHex: String = bytesToHex(CModule.ram, mDkPtr, mDkLen);
192 |
193 | mRunning = false;
194 | callback('oncomplete', dkHex);
195 | }
196 |
197 |
198 | private function msgHander(e: WebWorkerEvent) : void {
199 | var worker: WebWorker = e.target as WebWorker;
200 | var msg: * = e.data;
201 |
202 | // fast case
203 | if (typeof msg == 'number') {
204 | if (!mRunning) {
205 | return;
206 | }
207 | worker.postMessage(true);
208 |
209 | // progress
210 | mIterCur += msg;
211 |
212 | var now: uint = getTimer();
213 | if (now - mLastNotify > 100) {
214 | callback('onprogress', mIterCur / mIterMax);
215 | }
216 | mLastNotify = now;
217 | return;
218 | }
219 |
220 |
221 | switch (msg.state) {
222 | case 'done':
223 | if (!mRunning) {
224 | return;
225 | }
226 | // Bi -> B'i
227 | var ptr: uint = mBlksPtr + mBlkLen * worker.tag.id;
228 | var buf: ByteArray = worker.tag.buf;
229 | buf.position = 0;
230 | CModule.writeBytes(ptr, mBlkLen, buf);
231 |
232 | mIterCur += msg.step;
233 |
234 | if (++mDoneCounter == mArgP) {
235 | complete();
236 | } else if (mDoingCounter < mArgP) {
237 | task(worker);
238 | }
239 | break;
240 |
241 | case 'ready':
242 | if (++mReadyCounter == mThreads) {
243 | callback('onready');
244 | }
245 | break;
246 | }
247 | }
248 |
249 |
250 | private function task(worker: WebWorker) : void {
251 | var ptrBi: uint = mBlksPtr + mDoingCounter * mBlkLen;
252 | var bufBi: ByteArray = worker.tag.buf;
253 |
254 | bufBi.position = 0;
255 | CModule.readBytes(ptrBi, mBlkLen, bufBi);
256 |
257 | worker.tag.id = mDoingCounter++;
258 | worker.postMessage({
259 | cmd: 'task'
260 | });
261 | }
262 |
263 |
264 | private function bytesToHex(inBuf: ByteArray, inPtr: uint, inLen: uint) : String {
265 | var outStr: String = '';
266 |
267 | for(var i: uint = 0; i < inLen; i++) {
268 | var byte: uint = inBuf[inPtr + i];
269 | var hex: String = byte.toString(16);
270 | if (byte < 16) {
271 | hex = '0' + hex;
272 | }
273 | outStr += hex;
274 | }
275 | return outStr;
276 | }
277 |
278 |
279 | private function hexToByte(inStr: String, outBuf: ByteArray, outPtr: uint) : uint {
280 | var outLen: uint = inStr.length / 2;
281 |
282 | for (var i: uint = 0; i < outLen; i++) {
283 | outBuf[outPtr++] = parseInt(inStr.substr(i * 2, 2), 16);
284 | }
285 | return outLen;
286 | }
287 | }
288 | }
289 |
--------------------------------------------------------------------------------
/src/mod_flash/src/Scrypt.as:
--------------------------------------------------------------------------------
1 | package {
2 | import flash.display.Sprite;
3 | import com.etherdream.webworker.WebWorker;
4 |
5 |
6 | public class Scrypt extends Sprite {
7 |
8 | public function Scrypt() {
9 | if (WebWorker.isMainThread()) {
10 | new Main(loaderInfo.bytes);
11 | } else {
12 | new Child();
13 | }
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------