├── 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 | ![](mode1.png) 17 | 18 | 假如我们的登录过程被嗅探,那么前端提交的 dk 就会泄露。而 dk 是身份凭据,泄露即意味着 **攻击者用它也能登上该账号**。所以账号被盗用,显然是无法避免的。 19 | 20 | 不过,相比传统提交,风险其实已降低了不少 —— 传统提交,口令大多是毫不避讳,直接明文发送的。(当然有些开发者会用公钥加密口令,这确实能有效对抗嗅探攻击) 21 | 22 | ![](sniffer.png) 23 | 24 | 而现在,攻击者嗅探到却是的 dk,这是口令经过 **高成本 Hash** 的计算结果。攻击者若想通过 dk 还原口令,得花费巨大的算力。 25 | 26 | 因此最终:账号被盗,口令拿不到。那些使用类似口令的其他账号,就幸免于难了。 27 | 28 | > 注意,这里的「被盗」是指能被攻击者使用,但未必就能改掉口令。后续文章会讨论这个问题。 29 | 30 | 31 | ### 劫持 32 | 33 | 嗅探是静默的,通常不篡改或注入流量。因此流量只是失去了隐蔽性,并没有破坏完整性。 34 | 35 | 然而现实中,攻击者大多有主动出击的能力。例如中间人攻击,或是数据包注入,能对流量实施篡改。 36 | 37 | ![](mode2.png) 38 | 39 | 既然能修改流量内容,攻击者即可往登录页面中植入一段 JS 恶意代码,这样就能从应用层发起攻击,直接读取表单元素的口令值。 40 | 41 | ![](hijack.png) 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 | ![](1.png) 23 | 24 | 从系统攻防的角度来看,它是管用的。恶意程序想通过按键窃听口令,并不十分容易。至于想靠页面中的 JS 来捕获键盘事件,那更是天方夜谭。 25 | 26 | 然而事实上,用 JS 攻击才是最容易、最可行的。并不是因为这个插件存在什么接口、暴露了什么隐私,而是,这里根本就用不着插件! 27 | 28 | 如果能运行 JS,我们只需简单地删除插件 DOM,然后**通过 HTML 画个相似的输入框**,就能钓到用户口令了:) 29 | 30 | ```javascript 31 | $('.alieditContainer').html( 32 | '' 33 | ); 34 | ``` 35 | 36 | 至于用户无法正常登录,我们并不用关心,反正我们已经拿到明文口令了 —— 虽然未必正确,但正确的可能性是很大的。另外,也不必每次都劫持,让用户仍能正常登陆。 37 | 38 | ![](2.png) 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 |
18 | Input 19 |
20 |
21 | Pass: 22 | 23 |
24 |
25 | Salt: 26 | 27 |
28 |
29 | dkLen: 30 |
31 |
32 |
33 |
34 | N: 2^ 35 |
36 |
37 | r: 38 |
39 |
40 | P: 41 |
42 |
43 | Thread: 44 |
45 |
46 |
47 |
48 | API: 49 | 54 |
55 |
56 | 65 |
66 | 67 | 68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | Output 77 |
78 |
79 | 80 |
81 |
82 | 83 | 84 | 85 | 86 | 87 | Fork me on GitHub 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 |
11 | 登录框 12 |
13 | 账号: 14 |
15 |
16 | 密码: 17 |
18 | 19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
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 | --------------------------------------------------------------------------------