Skip to content

feat(js): 在 Goja 环境中实现 Web Crypto API 支持 - #1605

Open
lyjjl wants to merge 4 commits into
sealdice:masterfrom
lyjjl:master
Open

lyjjl wants to merge 4 commits into
sealdice:masterfrom
lyjjl:master

Conversation

@lyjjl

@lyjjl lyjjl commented Feb 23, 2026

Copy link
Copy Markdown
Contributor

Closes #1603

Summary by Sourcery

在 Goja JS 运行时中新增一个兼容 Web Crypto API 的 crypto 模块,并将其集成到 Dice JS 环境中。

新功能:

  • 在 Goja 环境中暴露全局 crypto 对象,提供 getRandomValuesrandomUUID,以及符合 Web Crypto API 的 subtle 接口。
  • 支持多种加密算法(AES 各种模式、HMAC、RSA、ECDSA/ECDH、Ed25519、X25519、PBKDF2、HKDF、DES/3DES、传统 RSA 变体),涵盖密钥生成、导入/导出、加解密、签名/验签、密钥派生以及密钥包裹/解包裹。
  • 在 JS 运行时中提供 TextEncoder/TextDecoder polyfill,用于 UTF-8 字符串的编解码。

文档:

  • crypto 模块撰写文档,说明暴露的 JS Web Crypto API 接口、所支持的算法、密钥格式以及使用示例。

测试:

  • 添加基于 Goja 的全面测试,覆盖随机数生成、摘要、对称/非对称加密、密钥派生、密钥导入/导出格式、包裹/解包裹流程、Ed25519/X25519 流程以及模块暴露等场景。
  • 在 JS 环境中验证 TextEncoder/TextDecoder 全局对象以及 UUID 格式行为。
Original summary in English

Summary by Sourcery

Add a Web Crypto API-compatible crypto module to the Goja JS runtime and integrate it into the Dice JS environment.

New Features:

  • Expose a global crypto object with getRandomValues, randomUUID, and a subtle Web Crypto API surface in the Goja environment.
  • Support a wide range of cryptographic algorithms (AES modes, HMAC, RSA, ECDSA/ECDH, Ed25519, X25519, PBKDF2, HKDF, DES/3DES, legacy RSA variants) including key generation, import/export, encryption/decryption, signing/verifying, key derivation, and key wrap/unwrap.
  • Provide TextEncoder/TextDecoder polyfills in the JS runtime for encoding/decoding UTF-8 strings.

Documentation:

  • Document the exposed JS Web Crypto API surface, supported algorithms, key formats, and usage examples for the crypto module.

Tests:

  • Add extensive Goja-based tests covering random generation, digests, symmetric/asymmetric crypto, key derivation, key import/export formats, wrap/unwrap flows, Ed25519/X25519 flows, and module exposure.
  • Verify TextEncoder/TextDecoder globals and UUID format behavior in the JS environment.

@sourcery-ai

sourcery-ai Bot commented Feb 23, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

为 Goja VM 引入一个完整兼容 Web Crypto API 的 crypto 实现,将其接入 JS 运行环境(既作为全局对象,又作为 require() 模块),并配套完善的测试和面向用户的文档。

crypto.subtle.encrypt 调用流程时序图

sequenceDiagram
  participant JS as JS_Script
  participant VM as Goja_Runtime
  participant Crypto as CryptoModule
  participant KeyHelpers as CryptoKeyHelpers

  JS->>VM: call crypto.subtle.encrypt(algorithm, key, data)
  VM->>Crypto: subtleEncrypt(rt, call)
  Crypto->>Crypto: parseAlgorithmIdentifier(rt, algorithm)
  Crypto->>KeyHelpers: extractCryptoKeyHandle(rt, key)
  KeyHelpers-->>Crypto: *CryptoKeyHandle
  Crypto->>Crypto: bufferSourceBytes(rt, data)
  Crypto->>Crypto: encryptData(rt, algorithmName, algObj, handle, bytes)
  Crypto-->>VM: Promise_resolved(ArrayBuffer(cipherText))
  VM-->>JS: resolved Promise with cipherText
Loading

File-Level Changes

Change Details Files
将一个新的、兼容 Web Crypto 的 crypto 模块接入基于 Goja 的 JS VM。
  • 在 JS VM 初始化代码中为新的 crypto 插件模块导入设置别名。
  • 在 require() 机制中,与 console 一起注册 crypto 原生模块。
  • 在每个 Goja runtime 上启用 crypto 模块,使 globalThis.crypto 可用。
dice/dice_jsvm.go
为 Goja 实现兼容 Web Crypto API 的功能(crypto/subtle、密钥管理、算法)。
  • 提供 EnableRequire 辅助方法,在 Goja runtime 中暴露一个包含 getRandomValuesrandomUUIDsubtle 命名空间的 crypto 对象。
  • 通过内部的 cryptoKeyHandle 实现 Web Crypto 风格的密钥处理(类似 CryptoKey 的对象),支持对称、RSA、EC、Ed25519 和 X25519 算法的对称密钥、公钥和私钥。
  • 支持 crypto.subtle 方法:digestgenerateKeyimportKeyexportKeysignverifyencryptdecryptderiveBitsderiveKeywrapKeyunwrapKey,包括基于 Promise 的返回结果以及 Web Crypto 风格的算法规范化。
  • 增加丰富的算法支持,包括 AES-CBC/GCM/CTR/KW、DES/3DES CBC、HMAC、PBKDF2、HKDF、ECDSA/ECDH、Ed25519、X25519、RSASSA-PKCS1-v1_5、RSA-PSS、RSA-OAEP、RSAES-PKCS1-v1_5,以及摘要算法(SHA-2 家族、SHA-1、MD5)和必要的辅助工具(填充、AES-KW、支持自定义指数的 RSA 密钥生成、EC/X25519 派生、JWK/PKCS/SPKI/SEC1 编解码)。
  • 添加用于 ArrayBuffer/TypedArray 处理、算法解析/规范化、JWK 处理,以及 Go crypto 密钥与内部句柄之间转换的辅助工具。
utils/plugin/crypto/crypto.go
暴露用于 crypto 模块编码支持的最小 TextEncoder/TextDecoder 全局对象。
  • 在启用 crypto 模块时,确保 Goja runtime 中存在 TextEncoderTextDecoder 全局对象。
  • 实现仅支持 UTF-8 的编码/解码行为,分别映射到 Uint8Array 和字符串,并在缺失 Uint8Array 构造函数时进行优雅处理。
utils/plugin/crypto/textencoding.go
为 Web Crypto 实现和 require() 集成添加全面测试。
  • 引入辅助工具,用于在测试中执行 JS 并解包已完成的 Promise。
  • 添加对随机性(getRandomValuesrandomUUID)、摘要(包括 MD5)、对称加密模式(AES-CBC/GCM/CTR、DES/3DES、AES-KW)、非对称操作(RSAES-PKCS1-v1_5、RSASSA-PKCS1-v1_5、RSA-PSS、RSA-OAEP)以及 HKDF/PBKDF2 deriveBits 的测试。
  • 测试跨格式(rawjwkpkcs1pkcs8sec1spki)和算法(AES、HMAC、RSA、ECDSA/ECDH、Ed25519、X25519)的密钥生成/导入/导出,包括 RSA JWK 缺少 p/q 以及使用非默认公钥指数等边界情况。
  • 验证 ECDSA 原始签名格式、EC/ECDH/X25519 deriveBits 互操作性、Ed25519/X25519 公钥/私钥原始密钥导入路径,以及 crypto 模块能够正确以全局对象和 require("crypto") 两种方式暴露。
  • 添加测试,确保 TextEncoder/TextDecoder 全局对象在 Goja 中按预期行为工作。
utils/plugin/crypto/crypto_test.go
为插件作者/用户编写文档,说明暴露的 JS Web Crypto API。
  • 添加一份 markdown 文档,描述在 Goja 中可用的 crypto / crypto.subtle API、支持的算法、密钥格式,以及针对传统/不安全算法的弃用说明。
  • 包含常见摘要、HMAC 和 AES 用法的简短示例,并链接到一个外部 JS 测试脚本作为参考。
docs/js-webcrypto-api.md

Assessment against linked issues

Issue Objective Addressed Explanation
#1603 在 Goja JS 环境中暴露原生 crypto 模块和全局对象,通过 RegisterNativeModule("crypto", loader) 注册,提供 Web Crypto 风格的 API(例如 crypto.getRandomValuescrypto.randomUUIDcrypto.subtle.*)。
#1603 在 Go 侧实现支撑 Web Crypto 风格 API 的底层密码学功能,包括哈希、对称/非对称加解密、签名、密钥派生,以及针对常用算法的密钥导入/导出/封装/解封。
#1603 添加文档,描述环境中暴露的 JS Web Crypto API,便于插件开发者了解如何使用新的 crypto/crypto.subtle API。

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • 触发新的代码审查: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 在审查评论下回复,请求 Sourcery 从该评论创建 issue。你也可以回复 @sourcery-ai issue 来从该评论创建 issue。
  • 生成 pull request 标题: 在 pull request 标题任意位置写上 @sourcery-ai,即可随时生成标题。也可以在 pull request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 正文任意位置写上 @sourcery-ai summary,即可在对应位置生成 PR 摘要。也可以在 pull request 中评论 @sourcery-ai summary 来随时(重新)生成摘要。
  • 生成 Reviewer's Guide: 在 pull request 中评论 @sourcery-ai guide,可随时(重新)生成审查指南。
  • 一次性解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,可将所有 Sourcery 评论标记为已解决。如果你已经处理完所有评论且不希望再看到它们,这会很有用。
  • 忽略所有 Sourcery 审查: 在 pull request 中评论 @sourcery-ai dismiss,可忽略所有现有的 Sourcery 审查。尤其适合你想从一次全新的审查开始时 —— 记得再评论 @sourcery-ai review 触发新的审查!

Customizing Your Experience

访问你的 dashboard 以:

  • 启用或禁用审查特性,例如 Sourcery 自动生成的 pull request 摘要、审查指南等。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查指令。
  • 调整其他审查设置。

Getting Help

Original review guide in English

Reviewer's Guide

Introduce a full Web Crypto API-compatible crypto implementation for the Goja VM, wire it into the JS environment (both as a global and as a require() module), and cover it with extensive tests and user-facing documentation.

Sequence diagram for crypto.subtle.encrypt call flow

sequenceDiagram
  participant JS as JS_Script
  participant VM as Goja_Runtime
  participant Crypto as CryptoModule
  participant KeyHelpers as CryptoKeyHelpers

  JS->>VM: call crypto.subtle.encrypt(algorithm, key, data)
  VM->>Crypto: subtleEncrypt(rt, call)
  Crypto->>Crypto: parseAlgorithmIdentifier(rt, algorithm)
  Crypto->>KeyHelpers: extractCryptoKeyHandle(rt, key)
  KeyHelpers-->>Crypto: *CryptoKeyHandle
  Crypto->>Crypto: bufferSourceBytes(rt, data)
  Crypto->>Crypto: encryptData(rt, algorithmName, algObj, handle, bytes)
  Crypto-->>VM: Promise_resolved(ArrayBuffer(cipherText))
  VM-->>JS: resolved Promise with cipherText
Loading

File-Level Changes

Change Details Files
Wire a new Web Crypto-compatible crypto module into the Goja-based JS VM.
  • Alias the new crypto plugin module import in the JS VM initialization code.
  • Register the crypto native module alongside console for require().
  • Enable the crypto module on each Goja runtime so globalThis.crypto is available.
dice/dice_jsvm.go
Implement Web Crypto API-compatible functionality (crypto/subtle, key management, algorithms) for Goja.
  • Provide Enable and Require helpers that expose a crypto object with getRandomValues, randomUUID, and a subtle namespace to a Goja runtime.
  • Implement Web Crypto-like key handling (CryptoKey-style objects) via an internal cryptoKeyHandle with support for secret, public, and private keys across symmetric, RSA, EC, Ed25519, and X25519 algorithms.
  • Support crypto.subtle methods: digest, generateKey, importKey, exportKey, sign, verify, encrypt, decrypt, deriveBits, deriveKey, wrapKey, unwrapKey, including promise-based results and Web Crypto-style algorithm normalization.
  • Add extensive algorithm support including AES-CBC/GCM/CTR/KW, DES/3DES CBC, HMAC, PBKDF2, HKDF, ECDSA/ECDH, Ed25519, X25519, RSASSA-PKCS1-v1_5, RSA-PSS, RSA-OAEP, RSAES-PKCS1-v1_5, plus digest algorithms (SHA-2 family, SHA-1, MD5) and the necessary helper utilities (padding, AES-KW, RSA key gen with custom exponent, EC/X25519 derive, JWK/PKCS/SPKI/SEC1 encode/decode).
  • Add helpers for ArrayBuffer/TypedArray handling, algorithm parsing/normalization, JWK handling, and conversion between Go crypto keys and the internal handles.
utils/plugin/crypto/crypto.go
Expose minimal TextEncoder/TextDecoder globals for encoding support in the crypto module.
  • Ensure TextEncoder and TextDecoder globals exist in the Goja runtime when the crypto module is enabled.
  • Implement UTF-8-only encode/decode behavior mapping to Uint8Array and string, with graceful handling of missing Uint8Array constructor.
utils/plugin/crypto/textencoding.go
Add comprehensive tests for the Web Crypto implementation and require() integration.
  • Introduce helper utilities to evaluate JS and unwrap fulfilled Promises in tests.
  • Add tests for randomness (getRandomValues, randomUUID), digests (including MD5), symmetric encryption modes (AES-CBC/GCM/CTR, DES/3DES, AES-KW), asymmetric operations (RSAES-PKCS1-v1_5, RSASSA-PKCS1-v1_5, RSA-PSS, RSA-OAEP), and HKDF/PBKDF2 deriveBits.
  • Test key generation/import/export across formats (raw, jwk, pkcs1, pkcs8, sec1, spki) and algorithms (AES, HMAC, RSA, ECDSA/ECDH, Ed25519, X25519), including edge cases like RSA JWK without p/q and non-default public exponents.
  • Verify ECDSA raw signature format, EC/ECDH/X25519 deriveBits interoperability, Ed25519/X25519 raw key import paths (public/private), and that the crypto module is correctly exposed both globally and via require("crypto").
  • Add tests ensuring TextEncoder/TextDecoder globals behave as expected in Goja.
utils/plugin/crypto/crypto_test.go
Document the exposed JS Web Crypto API surface for plugin authors/users.
  • Add a markdown document that describes available crypto / crypto.subtle APIs in Goja, the supported algorithms, key formats, and deprecation notes for legacy/unsafe algorithms.
  • Include short usage examples for common digest, HMAC, and AES usage and link to an external JS test script as a reference.
docs/js-webcrypto-api.md

Assessment against linked issues

Issue Objective Addressed Explanation
#1603 Expose a native crypto module and global in the Goja JS environment, registered via RegisterNativeModule("crypto", loader), providing a Web Crypto–style API (e.g., crypto.getRandomValues, crypto.randomUUID, and crypto.subtle.*).
#1603 Implement underlying cryptographic functionality on the Go side that backs the Web Crypto–style API, including hashing, symmetric/asymmetric encryption, signatures, key derivation, and key import/export/wrap/unwrap for common algorithms.
#1603 Add documentation describing the JS Web Crypto API exposed in the environment so plugin developers know how to use the new crypto/crypto.subtle APIs.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我在这里给出一些总体反馈:

  • crypto.go 的实现已经变得非常庞大(大约 3.7k 行),同时混合了算法解析、JWK 处理、RSA 数学运算、AES 封装等内容;建议将其拆分成多个更小的文件(例如:digest.goaes.gorsa.gojwk.goderive.go),以便未来的修改和代码审查更容易管理。
  • 像 MD5、SHA-1、DES/3DES 和 RSAES-PKCS1-v1_5 这样的弱/传统算法目前通过 API 完全暴露;即使文档中已经标注为已弃用,最好还是通过显式的选择加入标志或运行时选项来加以限制,这样就不会在普通脚本中被意外使用。
面向 AI 代理的提示词
Please address the comments from this code review:

## Overall Comments
- The `crypto.go` implementation has grown very large (~3.7k lines) and mixes algorithm parsing, JWK handling, RSA math, AES wrappers, etc.; consider splitting it into smaller files (e.g., `digest.go`, `aes.go`, `rsa.go`, `jwk.go`, `derive.go`) to make future changes and reviews more manageable.
- Weak/legacy algorithms like MD5, SHA-1, DES/3DES, and `RSAES-PKCS1-v1_5` are fully exposed through the API; even though they are documented as deprecated, it might be safer to gate them behind an explicit opt-in flag or runtime option so they cannot be used accidentally in normal scripts.

Sourcery 对开源项目是免费的——如果你觉得我们的审查有帮助,请考虑分享它 ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据你的反馈改进后续的代码审查。
Original comment in English

Hey - I've left some high level feedback:

  • The crypto.go implementation has grown very large (~3.7k lines) and mixes algorithm parsing, JWK handling, RSA math, AES wrappers, etc.; consider splitting it into smaller files (e.g., digest.go, aes.go, rsa.go, jwk.go, derive.go) to make future changes and reviews more manageable.
  • Weak/legacy algorithms like MD5, SHA-1, DES/3DES, and RSAES-PKCS1-v1_5 are fully exposed through the API; even though they are documented as deprecated, it might be safer to gate them behind an explicit opt-in flag or runtime option so they cannot be used accidentally in normal scripts.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `crypto.go` implementation has grown very large (~3.7k lines) and mixes algorithm parsing, JWK handling, RSA math, AES wrappers, etc.; consider splitting it into smaller files (e.g., `digest.go`, `aes.go`, `rsa.go`, `jwk.go`, `derive.go`) to make future changes and reviews more manageable.
- Weak/legacy algorithms like MD5, SHA-1, DES/3DES, and `RSAES-PKCS1-v1_5` are fully exposed through the API; even though they are documented as deprecated, it might be safer to gate them behind an explicit opt-in flag or runtime option so they cannot be used accidentally in normal scripts.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@kenichiLyon

Copy link
Copy Markdown
Contributor

既然做了 crypto 相关的 package,或许把文件拆得小一些,做成 package 内不同的文件或许会更好一些

@Szzrain Szzrain left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

关于此pr我思索了许久,我认为这部分功能不太适合放在海豹之中,而更合适塞进 goja_nodejs 或仿照其实现方式另起一个 repo

@kenichiLyon

Copy link
Copy Markdown
Contributor

关于此pr我思索了许久,我认为这部分功能不太适合放在海豹之中,而更合适塞进 goja_nodejs 或仿照其实现方式另起一个 repo

认同。

另外,木落老师有透露未来 2.0 版本不排除去除 goja 继而引入其他库与架构对插件系统进行重构的可能,虽然确认即将到来的 1.6.0 版本已经距离 1.5.1 版本很久,但私以为此修改的未来可迁移性有些太低,可能在那时会有隐性的技术债务。

@lyjjl

lyjjl commented Feb 24, 2026

Copy link
Copy Markdown
Contributor Author

为什么我觉得不影响未来可能的迁移呢。
脑子混乱,还是请 @fy0 判断吧

@kenichiLyon

Copy link
Copy Markdown
Contributor

为什么我觉得不影响未来可能的迁移呢。

目前的 crypto helper 是基于 goja 做的 bridge 实现,但是未来我们如果希望引入例如 wazero 库作为 2.0 可能的插件系统底层,那么这一套就行不通了,届时,如果要重构,肯定要考虑 像这样子既非 goja 亦非海豹原生实现的插件功能是否要搬运出去作为一个 repo 或是直接 remove再或是重写为符合对应底层实现的文件。无论哪一条,迁移难度都不低。

@fy0

fy0 commented Mar 15, 2026

Copy link
Copy Markdown
Member

为什么我觉得不影响未来可能的迁移呢。 脑子混乱,还是请 @fy0 判断吧

我了解了一下这个方向上的大致用途,感觉还是有用的,不过考虑到兼容性问题,需要确认api与标准api一致,未来可以无缝迁移。简单说就是看看js测试脚本在quickjs中能否运行

我认为可以这样:如果无法做到完全支持或者大部分支持,可以缩小一点接触面,也就是把常用的那几个算法支持掉

@PaienNate

Copy link
Copy Markdown
Contributor

为什么我觉得不影响未来可能的迁移呢。 脑子混乱,还是请 @fy0 判断吧

我想将该实现挪动到sealdice/goja_ext中,请问您的意见?

@fy0

fy0 commented Jul 26, 2026

Copy link
Copy Markdown
Member

暂时无法完成验证,等1.6.0发布后考虑合并,核心验证点是和常规webapi的一致性

@PaienNate

Copy link
Copy Markdown
Contributor

已将对应代码拷贝到sealdice_ext内。本PR若考虑修改,则修改对应引入sealdice_ext代码;否则将考虑关闭。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

为JS环境提供Crypto相关API

5 participants