likes
comments
collection

crypto-js-wasm: 当crypto-js遇上WebAssembly

作者站长头像
站长
· 阅读数 2

当提到WebAssembly(简称Wasm)技术的时候,大家回想到什么?“新技术”、“新闻里看到过”、“需要用非JS语言写”,或者“压根就没听说过”?那么,我们今天就给大家介绍一次Wasm技术实践: crypto-js-wasm

什么是WebAssembly (Wasm)?

Wasm在Wiki上的定义是:

WebAssembly或称wasm是一个实验性的低级编程语言,应用于浏览器内的客户端。WebAssembly是便携式的抽象语法树[1],被设计来提供比JavaScript更快速的编译及执行。WebAssembly将让开发者能运用自己熟悉的编程语言(最初以C/C++作为实现目标)编译,再藉虚拟机引擎在浏览器内执行。

简单来说,Wasm 能让开发着使用除Javacript外的其他语言,开发出可以在浏览器或 Node.js 中运行的二进制应用。

Wasm 技术的优点

那么,我们为什么放着好好的 JS 不用,要用其他编程语言,通过Wasm来开发前段应用呢?Wasm 官网上列出了它的几个优点,其中比较重要的2个是:

  • 性能:JS作为一种解释型语言,性能自然比不上以二进制形式提前编译好的 Wasm 应用
  • 安全:Wasm 提供一个内存安全的沙箱运行时环境。与 JS 不同,Wasm 代码的运行时过程是不可见的

另外,从 Wasm 官网提供的Roadmap可以看出,绝大部分Wasm标准的API已经被当前主流浏览器(Chrome, Firefox, Safari)和 Node.js 所支持了,也就是说,现在主流的JavaScript运行时环境都已支持Wasm。

当前支持编译为Wasm二进制的编程语言有:C/C++, Rust, AssemblyScript(一种类 TypeScript 的、专门编写 Wasm 的语言)、C#、Go等(具体名单)。

Wasm 技术的缺点

这么说,Wasm 技术完全超越了 JavaScript 吗?当然不是,目前Wasm也有以下这些缺点:

  • Wasm 应用的运行时内存与JS隔离,因此开始运行前,需要将内存从JS运行时拷贝到 Wasm 运行时,运行结束后再从 Wasm 中拷贝到JS中,这中间的工作主要是通过“浇水代码”(glue code)实现的。Wasm 的运行时速度虽然优于 JavaScript,但内存拷贝却十分耗时
  • Wasm 应用再开始使用前,需要县对应用进行二进制转化,再通过JS运行时(浏览器或 Node.js)提供的API加载到JS运行时中
  • Wasm 技术当前还不成熟,各种生态尚需完善,开发体验有待提升

也就是说,Wasm 虽然运行速度比 JavaScript 更快,但如果开发着反复调用 Wasm 一个用,频繁出发耗时的内存拷贝操作的话,Wasm应用的运行速度可能比 JavaScript 还要慢。

小结

总结一下,Wasm 的特点是:

  • 安全。Wasm 相比于 JavaScript,运行时内存不可见,执行更安全
  • 内存拷贝操作慢。因此为了获得比JS更好的性能,应该尽量减少调用 Wasm 应用的次数

结合这些特点,我们认为加密算法非常适合 Wasm 应用:加密算法的计算量较大,对性能要求较高;并且加密算法可以通过一次输入,进行复杂计算后获得输出,调用次数较少;通过加密算法对运行时安全也有一定要求。

而在加密算法方面,npm 上已经又一款下载量非常大、基于JS开发的加密算法开源软件 crypto-js。crypto-js 支持主流的哈希算法(如 SHA-256, SHA-3, MD5等)、加密算法(AES, RC4等)以及一些编码和填充算法。因此,我们基于功能相对齐全的crypto-js,再结合Wasm技术,开发了 crypto-js-wasm

crypto-js-wasm介绍

除了使用 Wasm 技术外,我们还按照 ES6 标准重写了 crypto-js,并加上了 jest 测试、大宝等内容。当前,crypto-js-wasm 再 npm 上已经发布了 1.0.0版本,支持crypto-js 已有的全部API接口。

安装

通过npm命令便可以安装crypto-js-wasm:

npm install @originjs/crypto-js-wasm

使用

由于 Wasm 技术的限制(前文有介绍),与 crypto-js 不同,在使用 crypto-js-wasm 某个算法前,需要异步加载一次对应的 Wasm 二进制(一次即可),或者也可以直接调用一次我们提供的 loadAllWasm 加载所有算法的 Wasm 二进制:

import CryptoJSW from 'crypto-js-wasm';

// (可选) 加载所有 wasm 文件
await CryptoJSW.loadAllWasm();

// 通过 Async/Await 语法调用
await CryptoJSW.MD5.loadWasm();
const rstMD5 = CryptoJSW.MD5('message').toString();
console.log(rstMD5);

// 通过 Promise 语法调用
CryptoJSW.SHA256.loadWasm().then(() => {
    const rstSHA256 = CryptoJSW.SHA256('message').toString();
    console.log(rstSHA256);
})

Benchmark

为了测试我们所写的 crypto-js-wasm 的性能,我们还专门开发了一个 Benchmark,方便在浏览器和 Node.js 中进行性能测试(下在源代码后在本地运行测试即可);此外,也可以通过我们提供的 在线 Benchmark 直接在浏览器中进行性能测试。

在我们的测试设备上(台式机,i5-4590, 16 GB RAM, Windows 10 Version 21H2 (OSBuild 19044, 1466)),crypto-js-wasm 与 crypto-js 的性能表现对比如下:

Chrome

crypto-js-wasm: 当crypto-js遇上WebAssembly

Firefox

crypto-js-wasm: 当crypto-js遇上WebAssembly

Node.js

crypto-js-wasm: 当crypto-js遇上WebAssembly

可以看到,crypto-js-wasm 在绝大部分场景下都有性能提升,并且在某些较复杂的算法上甚至可达到16倍以上的性能提升。

同时,获益于 Wasm 技术,crypto-js-wasm 的运行时内存不可见,一定程度上提升了加密算法的安全性(当然,目前内存还需要通过 JavaScript 编写的胶水代码进行交换,因此只是加密过程内存不可见,并非全过程内存不可见)。

总结

各位使用了 crypto-js,或者需要用到 JavaScript 加密算法的开发着们,欢迎尝试使用 crypto-js-wasm

后续,我们还计划在 crypto-js-wasm 中,提供 RSA 等更复杂的加密算法,欢迎大家积极提交issue和PR!