From dc155a9245e360fe2f4dcf5ec8c99c261b7183b6 Mon Sep 17 00:00:00 2001 From: AI Engineer Date: Sun, 21 Jun 2026 10:16:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(crypto-sm):=20=E5=85=B7=E5=90=8D=E5=8C=96?= =?UTF-8?q?=20JS=20=E5=AF=BC=E5=87=BA=E5=B9=B6=E5=8A=A8=E6=80=81=E5=8C=85?= =?UTF-8?q?=E8=A3=B9=E9=94=99=E8=AF=AF=EF=BC=88by=20AI=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++ TEST.md | 5 +- go.mod | 12 ++-- js_export.go | 194 +++++++++++++++++++++++++++++++++++---------------- 4 files changed, 146 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e92d6e0..93a352e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog: @go/crypto-sm +## v1.5.4 (2026-06-21) +- **JS 对齐**: 重构 JS 导出为具名函数,并引入 `jsmod.MakeError` 动态包装错误以获取调用栈。 +- **依赖更新**: 升级依赖 `jsmod` 至 `v1.5.3`,`cast` 至 `v1.5.3`,`rand` 至 `v1.5.3`,`encoding` 至 `v1.5.4`,`safe` 至 `v1.5.2`,`crypto` 至 `v1.5.3`。 + ## v1.5.3 (2026-06-11) - **版本对齐**: 基础设施全局对齐 v1.5.3。 diff --git a/TEST.md b/TEST.md index 70daaec..f286c53 100644 --- a/TEST.md +++ b/TEST.md @@ -1,14 +1,14 @@ # Test Report: @go/crypto-sm ## 📋 测试概览 -- **测试时间**: 2026-05-07 +- **测试时间**: 2026-06-21 - **测试环境**: darwin/amd64 - **Go 版本**: 1.25.0 ## ✅ 功能测试 (Functional Tests) | 场景 | 状态 | 描述 | | :--- | :--- | :--- | -| `TestSM2` | PASS | SM2 签名、验签与加解密测试。 | +| `TestSM2` | PASS | SM2 签名、验签 with SM3 与加解密测试。 | | `TestSM3` | PASS | SM3 哈希确定性验证。 | | `TestSM4` | PASS | SM4 CBC/GCM 模式加解密测试。 | | `TestSMPassword` | PASS | 基于密码的 SM4 与 SM2 构造器测试。 | @@ -17,6 +17,7 @@ ## 🛡️ 鲁棒性防御 (Robustness) - **摩擦消除**:移除 `Must` 冗余 API,完全对齐 `go/crypto` 规范。 - **内存安全**:继承 `go/crypto` 的安全缓冲区机制。 +- **JS 错误调用栈**:JS 桥接层改用具名导出并使用 `jsmod.MakeError` 包裹错误,确保 JS 抛出异常时携带准确的 Go 运行时堆栈。 ## ⚡ 性能基准 (Benchmarks) *SM 系列算法性能已在常规测试中验证,符合业务预期。* diff --git a/go.mod b/go.mod index 2a4b696..223c110 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,16 @@ module apigo.cc/go/crypto-sm go 1.25.0 require ( - apigo.cc/go/cast v1.5.2 - apigo.cc/go/crypto v1.5.2 - apigo.cc/go/encoding v1.5.3 - apigo.cc/go/jsmod v1.5.2 - apigo.cc/go/safe v1.5.1 + apigo.cc/go/cast v1.5.3 + apigo.cc/go/crypto v1.5.3 + apigo.cc/go/encoding v1.5.4 + apigo.cc/go/jsmod v1.5.3 + apigo.cc/go/safe v1.5.2 github.com/emmansun/gmsm v0.28.0 ) require ( - apigo.cc/go/rand v1.5.2 + apigo.cc/go/rand v1.5.3 golang.org/x/crypto v0.52.0 // indirect golang.org/x/sys v0.45.0 // indirect ) diff --git a/js_export.go b/js_export.go index 5b622a2..959242e 100644 --- a/js_export.go +++ b/js_export.go @@ -7,72 +7,144 @@ import ( func init() { jsmod.Register("sm", map[string]any{ // SM3 Hash - "SM3": SM3, - "SM3ToHex": SM3ToHex, - "SM3ToBase64": SM3ToBase64, - "SM3ToURLBase64": SM3ToURLBase64, + "SM3": jsSM3, + "SM3ToHex": jsSM3ToHex, + "SM3ToBase64": jsSM3ToBase64, + "SM3ToURLBase64": jsSM3ToURLBase64, // SM4 Symmetric (Stateless) - "SM4EncryptCBC": func(data, key, iv []byte) ([]byte, error) { - s, err := NewSM4CBCAndEraseKey(key, iv) - if err != nil { - return nil, err - } - return s.EncryptBytes(data) - }, - "SM4DecryptCBC": func(data, key, iv []byte) ([]byte, error) { - s, err := NewSM4CBCAndEraseKey(key, iv) - if err != nil { - return nil, err - } - return s.DecryptBytes(data) - }, - "SM4EncryptGCM": func(data, key, iv []byte) ([]byte, error) { - s, err := NewSM4GCMAndEraseKey(key, iv) - if err != nil { - return nil, err - } - return s.EncryptBytes(data) - }, - "SM4DecryptGCM": func(data, key, iv []byte) ([]byte, error) { - s, err := NewSM4GCMAndEraseKey(key, iv) - if err != nil { - return nil, err - } - return s.DecryptBytes(data) - }, + "SM4EncryptCBC": jsSM4EncryptCBC, + "SM4DecryptCBC": jsSM4DecryptCBC, + "SM4EncryptGCM": jsSM4EncryptGCM, + "SM4DecryptGCM": jsSM4DecryptGCM, // SM2 Asymmetric (Stateless) - "SM2Encrypt": func(data, pubKey []byte) ([]byte, error) { - a, err := NewSM2AndEraseKey(nil, pubKey) - if err != nil { - return nil, err - } - return a.EncryptBytes(data) - }, - "SM2Decrypt": func(data, privKey []byte) ([]byte, error) { - a, err := NewSM2AndEraseKey(privKey, nil) - if err != nil { - return nil, err - } - return a.DecryptBytes(data) - }, - "SM2Sign": func(data, privKey []byte) ([]byte, error) { - a, err := NewSM2AndEraseKey(privKey, nil) - if err != nil { - return nil, err - } - return a.Sign(data) - }, - "SM2Verify": func(data, sig, pubKey []byte) (bool, error) { - a, err := NewSM2AndEraseKey(nil, pubKey) - if err != nil { - return false, err - } - return a.Verify(data, sig) - }, + "SM2Encrypt": jsSM2Encrypt, + "SM2Decrypt": jsSM2Decrypt, + "SM2Sign": jsSM2Sign, + "SM2Verify": jsSM2Verify, // Key Generation - "GenerateSM2KeyPair": GenerateSM2KeyPair, + "GenerateSM2KeyPair": jsGenerateSM2KeyPair, }) } + +func jsSM3(data ...[]byte) []byte { + return SM3(data...) +} + +func jsSM3ToHex(data []byte) string { + return SM3ToHex(data) +} + +func jsSM3ToBase64(data []byte) string { + return SM3ToBase64(data) +} + +func jsSM3ToURLBase64(data []byte) string { + return SM3ToURLBase64(data) +} + +func jsSM4EncryptCBC(data, key, iv []byte) ([]byte, error) { + s, err := NewSM4CBCAndEraseKey(key, iv) + if err != nil { + return nil, jsmod.MakeError(err) + } + res, err := s.EncryptBytes(data) + if err != nil { + return nil, jsmod.MakeError(err) + } + return res, nil +} + +func jsSM4DecryptCBC(data, key, iv []byte) ([]byte, error) { + s, err := NewSM4CBCAndEraseKey(key, iv) + if err != nil { + return nil, jsmod.MakeError(err) + } + res, err := s.DecryptBytes(data) + if err != nil { + return nil, jsmod.MakeError(err) + } + return res, nil +} + +func jsSM4EncryptGCM(data, key, iv []byte) ([]byte, error) { + s, err := NewSM4GCMAndEraseKey(key, iv) + if err != nil { + return nil, jsmod.MakeError(err) + } + res, err := s.EncryptBytes(data) + if err != nil { + return nil, jsmod.MakeError(err) + } + return res, nil +} + +func jsSM4DecryptGCM(data, key, iv []byte) ([]byte, error) { + s, err := NewSM4GCMAndEraseKey(key, iv) + if err != nil { + return nil, jsmod.MakeError(err) + } + res, err := s.DecryptBytes(data) + if err != nil { + return nil, jsmod.MakeError(err) + } + return res, nil +} + +func jsSM2Encrypt(data, pubKey []byte) ([]byte, error) { + a, err := NewSM2AndEraseKey(nil, pubKey) + if err != nil { + return nil, jsmod.MakeError(err) + } + res, err := a.EncryptBytes(data) + if err != nil { + return nil, jsmod.MakeError(err) + } + return res, nil +} + +func jsSM2Decrypt(data, privKey []byte) ([]byte, error) { + a, err := NewSM2AndEraseKey(privKey, nil) + if err != nil { + return nil, jsmod.MakeError(err) + } + res, err := a.DecryptBytes(data) + if err != nil { + return nil, jsmod.MakeError(err) + } + return res, nil +} + +func jsSM2Sign(data, privKey []byte) ([]byte, error) { + a, err := NewSM2AndEraseKey(privKey, nil) + if err != nil { + return nil, jsmod.MakeError(err) + } + res, err := a.Sign(data) + if err != nil { + return nil, jsmod.MakeError(err) + } + return res, nil +} + +func jsSM2Verify(data, sig, pubKey []byte) (bool, error) { + a, err := NewSM2AndEraseKey(nil, pubKey) + if err != nil { + return false, jsmod.MakeError(err) + } + res, err := a.Verify(data, sig) + if err != nil { + return false, jsmod.MakeError(err) + } + return res, nil +} + +func jsGenerateSM2KeyPair() ([]byte, []byte, error) { + priv, pub, err := GenerateSM2KeyPair() + if err != nil { + return nil, nil, jsmod.MakeError(err) + } + return priv, pub, nil +}