jedisct1/libsodium.js

GitHub: jedisct1/libsodium.js

将成熟的 libsodium 加密库通过 Emscripten 编译为 WebAssembly 和纯 JavaScript,为 Web 和 Node.js 应用提供高性能、全面的现代密码学原语。

Stars: 1132 | Forks: 162

# libsodium.js ## 概述 [sodium](https://github.com/jedisct1/libsodium) 加密库 使用 [Emscripten](https://github.com/emscripten-core/emscripten) 编译为 WebAssembly 和纯 JavaScript, 并带有自动生成的封装,使其易于在 Web 应用程序中使用。 标准版本大约 290 KB,sumo 版本大约 375 KB (经压缩和 gzip 处理,包含纯 JS + WebAssembly 版本)。两者都可以在 Web 浏览器以及服务端运行。 ### 兼容性 支持的浏览器/JS 引擎: * Chrome >= 16 * Edge >= 0.11 * Firefox >= 21 * iOS >= 8.0 上的 Mobile Safari(较旧版本会产生不正确的结果) * NodeJS * Bun * Opera >= 15 * Safari >= 6(较旧版本会产生不正确的结果) 这与 WebCrypto API 的兼容性相当,后者也兼容相似数量的浏览器。 签名和其他基于 Edwards25519 的操作与 [WasmCrypto](https://github.com/jedisct1/wasm-crypto) 兼容。 ## 安装 [dist](https://github.com/jedisct1/libsodium.js/tree/master/dist) 目录包含预构建的脚本。将其子目录中的文件复制到您的应用程序中: - [browsers](https://github.com/jedisct1/libsodium.js/tree/master/dist/browsers) 包含一个可以包含在网页中的单文件脚本。 它包含了常用函数的代码。 - [browsers-sumo](https://github.com/jedisct1/libsodium.js/tree/master/dist/browsers-sumo) 是前一个脚本的超集,包含所有函数, 包括极少使用的和未记录的函数。 - [modules](https://github.com/jedisct1/libsodium.js/tree/master/dist/modules) 包含常用函数,设计为作为 CommonJS 模块加载。 `libsodium-wrappers` 是您的应用程序应该加载的模块,它 随后会自动加载 `libsodium` 作为依赖项。 - [modules-sumo](https://github.com/jedisct1/libsodium.js/tree/master/dist/modules-sumo) 包含上述模块的 sumo 变体。 - [modules-esm](https://github.com/jedisct1/libsodium.js/tree/master/dist/modules-esm) 包含带有 `.mjs` 扩展名的 ESM(ES modules)版本。 - [modules-sumo-esm](https://github.com/jedisct1/libsodium.js/tree/master/dist/modules-sumo-esm) 包含 sumo 的 ESM 变体。 这些模块也可以在 npm 上获取: - [libsodium-wrappers](https://www.npmjs.com/package/libsodium-wrappers) - [libsodium-wrappers-sumo](https://www.npmjs.com/package/libsodium-wrappers-sumo) ### 使用方法(作为模块) 加载 `libsodium-wrappers` 模块。返回的对象包含一个 `.ready` 属性:这是一个 Promise,必须在 sodium 函数可用之前被 resolve。 示例: ``` import sodium from 'libsodium-wrappers'; await sodium.ready; let key = sodium.crypto_secretstream_xchacha20poly1305_keygen(); let { state: state_out, header } = sodium.crypto_secretstream_xchacha20poly1305_init_push(key); let c1 = sodium.crypto_secretstream_xchacha20poly1305_push(state_out, sodium.from_string('message 1'), null, sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE); let c2 = sodium.crypto_secretstream_xchacha20poly1305_push(state_out, sodium.from_string('message 2'), null, sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL); let state_in = sodium.crypto_secretstream_xchacha20poly1305_init_pull(header, key); let r1 = sodium.crypto_secretstream_xchacha20poly1305_pull(state_in, c1); let { message: m1_bytes, tag: tag1 } = r1; let m1 = sodium.to_string(m1_bytes); let r2 = sodium.crypto_secretstream_xchacha20poly1305_pull(state_in, c2); let { message: m2_bytes, tag: tag2 } = r2; let m2 = sodium.to_string(m2_bytes); console.log(m1); console.log(m2); ``` **命名导出:** ESM 模块也提供了辅助函数的命名导出: ``` import { ready, from_hex, to_hex, from_string, to_string } from 'libsodium-wrappers'; await ready; const bytes = from_hex('deadbeef'); console.log(to_hex(bytes)); ``` **注意:** 加密函数(如 `crypto_secretbox_easy`)和常量(如 `crypto_secretbox_KEYBYTES`)是在 `ready` 解决后,在运行时动态添加到模块中的。它们不能作为命名导出被导入,必须通过默认导出进行访问: ``` import sodium from 'libsodium-wrappers'; await sodium.ready; // Now crypto functions and constants are available on the sodium object const key = sodium.crypto_secretbox_keygen(); const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES); ``` ### 使用方法(在 Web 浏览器中,通过回调) `sodium.js` 文件包含核心的 libsodium 函数以及更高级的 JavaScript 封装。它可以被异步加载。 一个 `sodium` 对象应该在全局命名空间中定义,并包含以下属性: - `onload`:封装初始化完成后调用的函数。 示例: ``` ``` **重要提示:** 如果您直接将库内联到 HTML 中(而不是从单独的文件加载),请确保您的页面声明了 UTF-8 编码: ``` ``` 如果不这样做,浏览器可能会在 HTML 解析期间损坏嵌入的 WebAssembly 二进制数据,导致出现诸如 "failed to match magic number" 或 "HEAPU8 is undefined" 等错误。同时请确保您的服务器发送了 `charset=utf-8` 响应头。 ## 其他辅助函数 * `from_base64()`、`to_base64()`,带有可选的第二个参数, 其值为以下之一:`base64_variants.ORIGINAL`、`base64_variants.ORIGINAL_NO_PADDING`、 `base64_variants.URLSAFE` 或 `base64_variants.URLSAFE_NO_PADDING`。默认值为 `base64_variants.URLSAFE_NO_PADDING`。 * `from_hex()`、`to_hex()` * `from_string()`、`to_string()` * `pad(, )`、`unpad(, )` * `memcmp()`(常数时间相等性检查,返回 `true` 或 `false`) * `compare()`(常数时间比较。值必须具有相同的大小。返回 `-1`、`0` 或 `1`) * `memzero()`(适用于 `Uint8Array` 对象) * `increment()`(递增一个以 little-endian `Uint8Array` 存储的任意长度数字 - 通常用于递增 nonces) * `add()`(将两个以 little-endian `Uint8Array` 向量存储的任意长度数字相加) * `is_zero()`(常数时间检查 `Uint8Array` 对象是否全为零) ## API 封装器暴露的 API 与 C 库的 API 相同,只是缓冲区长度永远不需要显式给出。 二进制输入缓冲区应为 `Uint8Array` 对象。但是,如果改为传入字符串,封装器将自动把字符串转换为包含该字符串 UTF-8 表示形式的数组。 示例: ``` var key = sodium.randombytes_buf(sodium.crypto_shorthash_KEYBYTES), hash1 = sodium.crypto_shorthash(new Uint8Array([1, 2, 3, 4]), key), hash2 = sodium.crypto_shorthash('test', key); ``` 如果输出是唯一的二进制缓冲区,它将作为 `Uint8Array` 对象返回。 示例 (secretbox): ``` let key = sodium.from_hex('724b092810ec86d7e35c9d067702b31ef90bc43a7b598626749914d6a3e033ed'); function encrypt_and_prepend_nonce(message) { let nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES); let ciphertext = sodium.crypto_secretbox_easy(message, nonce, key); let result = new Uint8Array(nonce.length + ciphertext.length); result.set(nonce); result.set(ciphertext, nonce.length); return result; } function decrypt_after_extracting_nonce(nonce_and_ciphertext) { if (nonce_and_ciphertext.length < sodium.crypto_secretbox_NONCEBYTES + sodium.crypto_secretbox_MACBYTES) { throw "Short message"; } let nonce = nonce_and_ciphertext.slice(0, sodium.crypto_secretbox_NONCEBYTES), ciphertext = nonce_and_ciphertext.slice(sodium.crypto_secretbox_NONCEBYTES); return sodium.crypto_secretbox_open_easy(ciphertext, nonce, key); } ``` 此外,提供了 `from_hex`、`to_hex`、`from_string` 和 `to_string` 函数,用于在十六进制和任意字符串表示形式与 `Uint8Array` 对象之间进行显式转换。 返回多个输出缓冲区的函数将把它们作为一个对象返回。例如,`sodium.crypto_box_keypair()` 函数返回以下对象: ``` { publicKey: (Uint8Array), privateKey: (Uint8Array), keyType: 'x25519' } ``` 这也适用于较新的 API,例如: ``` { state: (StateAddress), header: (Uint8Array) } { ciphertext: (Uint8Array), sharedSecret: (Uint8Array) } ``` 用于 `crypto_secretstream_xchacha20poly1305_init_push()` 和 `crypto_kem_enc()`。 ### 标准版与 Sumo 版本对比 标准版本(位于 `dist/browsers`、`dist/modules` 和 `dist/modules-esm` 目录中)包含高级函数,并且是大多数项目推荐使用的版本。 或者,在 `dist/browsers-sumo`、`dist/modules-sumo` 和 `dist/modules-sumo-esm` 目录中可用的“sumo”版本包含了原始库中的所有符号。这包括未记录的、未经测试的、已弃用的、底层的以及容易误用的函数。 `crypto_pwhash_*` 函数集仅包含在 sumo 版本中。 sumo 版本比标准版本略大,占用更多内存,应仅在您确实需要它提供的额外符号时才使用。 ### 编译 如果您想自行编译这些文件,您的系统上需要安装以下依赖项: * Emscripten * binaryen * git * bun * make 运行 `make` 将在需要时安装开发依赖项,构建标准版和 sumo 发行版,重新生成 API 文档和 TypeScript 绑定,打包生成的输出,并重新构建浏览器测试构件。 ### 基准测试 运行 `make benchmark` 来测量加密函数的性能。 您也可以使用 `bun benchmark/index.ts --only ` 运行特定的基准测试 (使用 `--list` 查看可用的基准测试)。 ## 相关项目 * [react-native-libsodium](https://github.com/serenity-kit/react-native-libsodium):与 libsodium-wrappers 包 API 匹配的 Libsodium React Native 绑定 ## 作者 由 Ahmad Ben Mrad、Frank Denis 和 Ryan Lester 构建。 ## 许可证 此封装器在 [ISC 许可证](https://en.wikipedia.org/wiki/ISC_license) 下分发。
标签:AI工具, CMS安全, CommonJS, CVE, Ed25519, Emscripten, GNU通用公共许可证, JavaScript, libsodium, MITM代理, Node.js, WebAssembly, 前端加密, 加密库, 后端开发, 密码学, 手动系统调用, 数字签名, 数据可视化, 浏览器兼容, 网络安全, 隐私保护