Compare commits
No commits in common. "main" and "v1.0.5" have entirely different histories.
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,4 +0,0 @@
|
|||||||
.ai/
|
|
||||||
.geminiignore
|
|
||||||
.gemini
|
|
||||||
/CODE-FULL.md
|
|
||||||
22
CHANGELOG.md
22
CHANGELOG.md
@ -1,26 +1,6 @@
|
|||||||
# Changelog: @go/crypto-sm
|
# Changelog: @go/crypto-sm
|
||||||
|
|
||||||
## [v1.1.0] - 2026-05-07
|
## [v1.0.0] - 2026-04-23
|
||||||
|
|
||||||
### Added
|
|
||||||
- **国密算法强化**:同步引入基于 Argon2id 的密码派生密钥能力。
|
|
||||||
- **便捷构造器**:新增 `NewSM4CBCByPassword`, `NewSM4GCMByPassword` 及 `NewSM2ByPassword` API,其中 SM2 支持基于密码的确定性密钥对生成。
|
|
||||||
- **内存安全**:所有密码构造器均强制执行 `EraseKey` 策略,派生后物理擦除密码与盐。
|
|
||||||
- **依赖更新**:升级 `apigo.cc/go/crypto` 至 v1.1.0。
|
|
||||||
|
|
||||||
## [v1.0.6] - 2026-05-06
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- **设计哲学对齐**:全面废除 `Must` 前缀函数支持,完全对齐 `go/crypto` 的 frictionless 设计。
|
|
||||||
- **文档更新**:更新 `README.md`,明确配合 `go/cast` 的使用方式。
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- **依赖对齐**:更新 `apigo.cc/go/crypto` 依赖至 v1.0.6,引入 `apigo.cc/go/cast` 依赖。
|
|
||||||
|
|
||||||
## [v1.0.5] - 2026-05-01
|
|
||||||
- (同步版本号)
|
|
||||||
|
|
||||||
## [v1.0.4] - 2026-05-01
|
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- **国产密码算法支持**:完全实现国密 SM2 (签名/加密)、SM3 (哈希)、SM4 (CBC/GCM) 算法。
|
- **国产密码算法支持**:完全实现国密 SM2 (签名/加密)、SM3 (哈希)、SM4 (CBC/GCM) 算法。
|
||||||
|
|||||||
37
README.md
37
README.md
@ -1,49 +1,36 @@
|
|||||||
# 关于本项目
|
# 关于本项目
|
||||||
|
|
||||||
本项目完全由 AI 维护。代码源自 github.com/ssgo/u 的重构。
|
本项目完全由 AI 维护。代码源自 github.com/ssgo/u 的重构。
|
||||||
|
|
||||||
# @go/crypto-sm
|
# @go/crypto-sm
|
||||||
|
|
||||||
`@go/crypto-sm` 是 `go/crypto` 的国密(SM2/SM3/SM4)扩展包,完全兼容 `go/crypto` 的设计哲学与接口规范,强制内存安全并消除摩擦。
|
`@go/crypto-sm` 是国产密码算法的 Go 语言实现工具库。本项目兼容 `@go/crypto` 的核心接口设计,提供极致的内存安全与性能表现,特别适用于对安全性与国产化合规有要求的金融及服务端环境。
|
||||||
|
|
||||||
## 🎯 设计哲学
|
## 🎯 设计哲学
|
||||||
|
|
||||||
* **国密合规**:实现 SM2(非对称)、SM3(哈希)、SM4(对称)标准。
|
* **防御优先**:全面支持 `AndEraseKey` 模式,敏感密钥在构造后即进行物理擦除,防止内存残留。
|
||||||
* **接口对齐**:SM2 继承 `crypto.Asymmetric`,SM4 继承 `crypto.Symmetric`,开发者无需学习新 API。
|
* **兼容设计**:完全对齐 `@go/crypto` 的接口规范,替换算法实现即可平滑迁移业务。
|
||||||
* **消除摩擦**:移除 `Must` 系列函数,推荐结合 `go/cast` 的 `As` 函数。
|
* **性能优化**:针对服务端高并发场景,提供对象缓存与 FastMode 模式。
|
||||||
|
|
||||||
## 🛠 API Reference
|
## 🛠 API Reference
|
||||||
|
|
||||||
### SM2 (国密非对称)
|
### SM2 (国密非对称)
|
||||||
- `func NewSM2AndEraseKey(priv, pub []byte) (*crypto.Asymmetric, error)`
|
- `func NewSM2AndEraseKey(priv, pub []byte) (*crypto.Asymmetric, error)`
|
||||||
- `func NewSM2ByPassword(password, salt []byte) (*crypto.Asymmetric, error)` (确定性生成)
|
- `func NewSM2WithoutEraseKey(priv, pub []byte) (*crypto.Asymmetric, error)`
|
||||||
- `func GenerateSM2KeyPair() (priv, pub []byte, err error)`
|
- `func GenerateSM2KeyPair() ([]byte, []byte, error)`
|
||||||
|
- *注:SM2 继承 `Asymmetric` 接口,支持所有 `crypto.Asymmetric` 方法 (含 `Must` 和 `Try` 系列)。*
|
||||||
|
|
||||||
### SM3 (国密哈希)
|
### SM3 (国密摘要)
|
||||||
- `func Sm3(data []byte) []byte`
|
- `func Sm3(data ...[]byte) []byte`
|
||||||
- `func Sm3ToHex(data []byte) string`
|
- `func Sm3ToHex(data []byte) string`
|
||||||
|
- `func Sm3ToBase64(data []byte) string`
|
||||||
|
- `func Sm3ToUrlBase64(data []byte) string`
|
||||||
|
|
||||||
### SM4 (国密对称)
|
### SM4 (国密对称)
|
||||||
- `func NewSM4CBCAndEraseKey(key, iv []byte) (*crypto.Symmetric, error)`
|
- `func NewSM4CBCAndEraseKey(key, iv []byte) (*crypto.Symmetric, error)`
|
||||||
- `func NewSM4CBCByPassword(password, salt []byte) (*crypto.Symmetric, error)`
|
|
||||||
- `func NewSM4GCMAndEraseKey(key, iv []byte) (*crypto.Symmetric, error)`
|
- `func NewSM4GCMAndEraseKey(key, iv []byte) (*crypto.Symmetric, error)`
|
||||||
- `func NewSM4GCMByPassword(password, salt []byte) (*crypto.Symmetric, error)`
|
- *注:SM4 继承 `Symmetric` 接口,支持所有 `crypto.Symmetric` 方法 (含 `Must` 和 `Try` 系列)。*
|
||||||
|
|
||||||
## 📦 安装
|
## 📦 安装
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go get apigo.cc/go/crypto-sm
|
go get apigo.cc/go/crypto-sm
|
||||||
```
|
```
|
||||||
|
|
||||||
## 💡 示例
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
"apigo.cc/go/crypto-sm"
|
|
||||||
"apigo.cc/go/cast"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SM2 签名示例
|
|
||||||
a, _ := sm.NewSM2AndEraseKey(priv, pub)
|
|
||||||
sig := cast.As(a.Sign(data))
|
|
||||||
```
|
|
||||||
|
|||||||
23
TEST.md
23
TEST.md
@ -1,22 +1,17 @@
|
|||||||
# Test Report: @go/crypto-sm
|
# Test Report: @go/crypto-sm
|
||||||
|
|
||||||
## 📋 测试概览
|
## 📋 测试概览
|
||||||
- **测试时间**: 2026-05-07
|
- **测试时间**: 2026-04-23
|
||||||
- **测试环境**: darwin/amd64
|
- **测试环境**: darwin/amd64
|
||||||
- **Go 版本**: 1.25.0
|
|
||||||
|
|
||||||
## ✅ 功能测试 (Functional Tests)
|
## ✅ 功能测试
|
||||||
| 场景 | 状态 | 描述 |
|
| 场景 | 状态 | 描述 |
|
||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- |
|
||||||
| `TestSM2` | PASS | SM2 签名、验签与加解密测试。 |
|
| `TestSM2_AllModes` | PASS | SM2 生成、签名/验签、加解密链路验证。 |
|
||||||
| `TestSM3` | PASS | SM3 哈希确定性验证。 |
|
| `TestSM3_Compatibility` | PASS | SM3 哈希计算与编码兼容性验证。 |
|
||||||
| `TestSM4` | PASS | SM4 CBC/GCM 模式加解密测试。 |
|
| `TestSM4_Exhaustive` | PASS | SM4 CBC/GCM 多模式与填充错误检测验证。 |
|
||||||
| `TestSMPassword` | PASS | 基于密码的 SM4 与 SM2 构造器测试。 |
|
| `TestSM4_Concurrency` | PASS | SM4 并发安全性验证。 |
|
||||||
| `TestSM2Deterministic` | PASS | SM2 密钥基于密码的确定性生成验证。 |
|
|
||||||
|
|
||||||
## 🛡️ 鲁棒性防御 (Robustness)
|
## ⚡ 性能基准 (Benchmark)
|
||||||
- **摩擦消除**:移除 `Must` 冗余 API,完全对齐 `go/crypto` 规范。
|
- `BenchmarkSM2_Sign`: 性能卓越,适合国密合规场景。
|
||||||
- **内存安全**:继承 `go/crypto` 的安全缓冲区机制。
|
- `BenchmarkSM4_GCM`: 吞吐量优异。
|
||||||
|
|
||||||
## ⚡ 性能基准 (Benchmarks)
|
|
||||||
*SM 系列算法性能已在常规测试中验证,符合业务预期。*
|
|
||||||
|
|||||||
21
go.mod
21
go.mod
@ -3,15 +3,22 @@ module apigo.cc/go/crypto-sm
|
|||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
apigo.cc/go/cast v1.3.3
|
apigo.cc/go/crypto v1.0.5
|
||||||
apigo.cc/go/crypto v1.3.1
|
apigo.cc/go/encoding v1.0.5
|
||||||
apigo.cc/go/encoding v1.3.1
|
apigo.cc/go/safe v1.0.5
|
||||||
apigo.cc/go/safe v1.3.1
|
|
||||||
github.com/emmansun/gmsm v0.28.0
|
github.com/emmansun/gmsm v0.28.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
apigo.cc/go/rand v1.3.1 // indirect
|
apigo.cc/go/rand v1.0.5 // indirect
|
||||||
golang.org/x/crypto v0.51.0 // indirect
|
golang.org/x/crypto v0.50.0 // indirect
|
||||||
golang.org/x/sys v0.44.0 // indirect
|
golang.org/x/sys v0.43.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace apigo.cc/go/crypto => ../crypto
|
||||||
|
|
||||||
|
replace apigo.cc/go/encoding => ../encoding
|
||||||
|
|
||||||
|
replace apigo.cc/go/safe => ../safe
|
||||||
|
|
||||||
|
replace apigo.cc/go/rand => ../rand
|
||||||
|
|||||||
18
go.sum
18
go.sum
@ -1,16 +1,6 @@
|
|||||||
apigo.cc/go/cast v1.3.3 h1:aln5eDR5DZVWVzZ/y5SJh1gQNgWv2sT82I25NaO9g34=
|
|
||||||
apigo.cc/go/cast v1.3.3/go.mod h1:lGlwImiOvHxG7buyMWhFzcdvQzmSaoKbmr7bcDfUpHk=
|
|
||||||
apigo.cc/go/crypto v1.3.1 h1:ulQ2zX9bUWirk0sEacx1Srsjs2Jow7HlZq7ED7msNcg=
|
|
||||||
apigo.cc/go/crypto v1.3.1/go.mod h1:SwHlBFDPddttWgFFtzsEMla8CM/rcFy9nvdsJjW4CIs=
|
|
||||||
apigo.cc/go/encoding v1.3.1 h1:y8O58KYAyulkThg1O2ji2BqjnFoSvk42sit9I3z+K7Y=
|
|
||||||
apigo.cc/go/encoding v1.3.1/go.mod h1:xAJk5b83VZ31mXMTnyp0dfMoBKfT/AHDn0u+cQfojgY=
|
|
||||||
apigo.cc/go/rand v1.3.1 h1:7FvsI6PtQ5XrWER0dTiLVo0p7GIxRidT/TBKhVy93j8=
|
|
||||||
apigo.cc/go/rand v1.3.1/go.mod h1:mZ/4Soa3bk+XvDaqPWJuUe1bfEi4eThBj1XmEAuYxsk=
|
|
||||||
apigo.cc/go/safe v1.3.1 h1:irTCqPAC97gGsX/Lw5AzLelDt1xXLEZIAaVhLELWe9Q=
|
|
||||||
apigo.cc/go/safe v1.3.1/go.mod h1:XdOpBhN2vkImalaykYXXmEpczqWa1y3ah6/Q72cdRqE=
|
|
||||||
github.com/emmansun/gmsm v0.28.0 h1:0WyTHmQgaAfM8IwMnNMJCfEiK999cZ2J8csfcZ2Ooco=
|
github.com/emmansun/gmsm v0.28.0 h1:0WyTHmQgaAfM8IwMnNMJCfEiK999cZ2J8csfcZ2Ooco=
|
||||||
github.com/emmansun/gmsm v0.28.0/go.mod h1:9lKtK8f3c7wh2z0g6fsqRbay69V1jWYDcBaytyuR95M=
|
github.com/emmansun/gmsm v0.28.0/go.mod h1:9lKtK8f3c7wh2z0g6fsqRbay69V1jWYDcBaytyuR95M=
|
||||||
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
|
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||||
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
|
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||||
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||||
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
|
|||||||
30
sm2.go
30
sm2.go
@ -28,36 +28,6 @@ func NewSM2WithoutEraseKey(privateKey, publicKey []byte) (*crypto.Asymmetric, er
|
|||||||
return crypto.NewAsymmetricWithoutEraseKey(SM2, privateKey, publicKey, false)
|
return crypto.NewAsymmetricWithoutEraseKey(SM2, privateKey, publicKey, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSM2ByPassword(password, salt []byte) (*crypto.Asymmetric, error) {
|
|
||||||
seed := crypto.DeriveKey(password, salt, 32)
|
|
||||||
defer safe.ZeroMemory(seed)
|
|
||||||
|
|
||||||
curve := sm2.P256()
|
|
||||||
params := curve.Params()
|
|
||||||
|
|
||||||
// Derive D: seed % (n-1) + 1
|
|
||||||
// SM2 private key d should be in [1, n-1]
|
|
||||||
d := new(big.Int).SetBytes(seed)
|
|
||||||
nMinusOne := new(big.Int).Sub(params.N, big.NewInt(1))
|
|
||||||
d.Mod(d, nMinusOne)
|
|
||||||
d.Add(d, big.NewInt(1))
|
|
||||||
|
|
||||||
priv := new(sm2.PrivateKey)
|
|
||||||
priv.Curve = curve
|
|
||||||
priv.D = d
|
|
||||||
priv.PublicKey.X, priv.PublicKey.Y = curve.ScalarBaseMult(d.Bytes())
|
|
||||||
|
|
||||||
privateKey, err := smx509.MarshalPKCS8PrivateKey(priv)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
publicKey, err := smx509.MarshalPKIXPublicKey(&priv.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return NewSM2AndEraseKey(privateKey, publicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GenerateSM2KeyPair() ([]byte, []byte, error) {
|
func GenerateSM2KeyPair() ([]byte, []byte, error) {
|
||||||
privKey, err := sm2.GenerateKey(rand.Reader)
|
privKey, err := sm2.GenerateKey(rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
13
sm4.go
13
sm4.go
@ -36,20 +36,7 @@ func NewSM4GCMWithoutEraseKey(key, iv []byte) (*crypto.Symmetric, error) {
|
|||||||
return crypto.NewSymmetricWithoutEraseKey(SM4GCM, key, iv)
|
return crypto.NewSymmetricWithoutEraseKey(SM4GCM, key, iv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSM4CBCByPassword(password, salt []byte) (*crypto.Symmetric, error) {
|
|
||||||
derived := crypto.DeriveKey(password, salt, 16+16)
|
|
||||||
defer safe.ZeroMemory(derived)
|
|
||||||
return NewSM4CBCAndEraseKey(derived[:16], derived[16:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSM4GCMByPassword(password, salt []byte) (*crypto.Symmetric, error) {
|
|
||||||
derived := crypto.DeriveKey(password, salt, 16+12)
|
|
||||||
defer safe.ZeroMemory(derived)
|
|
||||||
return NewSM4GCMAndEraseKey(derived[:16], derived[16:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SM4Cipher) Encrypt(data []byte, key []byte, iv []byte) ([]byte, error) {
|
func (s *SM4Cipher) Encrypt(data []byte, key []byte, iv []byte) ([]byte, error) {
|
||||||
|
|
||||||
block, err := sm4.NewCipher(key)
|
block, err := sm4.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@ -1,59 +0,0 @@
|
|||||||
package sm_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"apigo.cc/go/cast"
|
|
||||||
"apigo.cc/go/crypto-sm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSMPassword(t *testing.T) {
|
|
||||||
password := []byte("sm-secret-password")
|
|
||||||
salt := []byte("sm-salt")
|
|
||||||
data := []byte("hello sm password")
|
|
||||||
|
|
||||||
// 1. SM4-GCM
|
|
||||||
p1 := append([]byte(nil), password...)
|
|
||||||
s1 := append([]byte(nil), salt...)
|
|
||||||
sm4, err := sm.NewSM4GCMByPassword(p1, s1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
enc := cast.As(sm4.EncryptBytes(data))
|
|
||||||
dec := cast.As(sm4.DecryptBytes(enc))
|
|
||||||
if !bytes.Equal(data, dec) {
|
|
||||||
t.Error("SM4-GCM password roundtrip failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. SM2
|
|
||||||
p2 := append([]byte(nil), password...)
|
|
||||||
s2 := append([]byte(nil), salt...)
|
|
||||||
sm2a, err := sm.NewSM2ByPassword(p2, s2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
sig := cast.As(sm2a.Sign(data))
|
|
||||||
if ok, _ := sm2a.Verify(data, sig); !ok {
|
|
||||||
t.Error("SM2 password sign/verify failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSM2Deterministic(t *testing.T) {
|
|
||||||
password := []byte("sm2-determ-pass")
|
|
||||||
salt := []byte("sm2-determ-salt")
|
|
||||||
|
|
||||||
p1 := append([]byte(nil), password...)
|
|
||||||
s1 := append([]byte(nil), salt...)
|
|
||||||
a1, _ := sm.NewSM2ByPassword(p1, s1)
|
|
||||||
|
|
||||||
p2 := append([]byte(nil), password...)
|
|
||||||
s2 := append([]byte(nil), salt...)
|
|
||||||
a2, _ := sm.NewSM2ByPassword(p2, s2)
|
|
||||||
|
|
||||||
data := []byte("test")
|
|
||||||
sig, _ := a1.Sign(data)
|
|
||||||
if ok, _ := a2.Verify(data, sig); !ok {
|
|
||||||
t.Error("SM2 deterministic generation failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
99
sm_test.go
99
sm_test.go
@ -4,59 +4,74 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"apigo.cc/go/cast"
|
|
||||||
"apigo.cc/go/crypto-sm"
|
"apigo.cc/go/crypto-sm"
|
||||||
|
"github.com/emmansun/gmsm/sm3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSM2(t *testing.T) {
|
func TestSM2_AllModes(t *testing.T) {
|
||||||
priv, pub, err := sm.GenerateSM2KeyPair()
|
priv, pub, _ := sm.GenerateSM2KeyPair()
|
||||||
if err != nil { t.Fatal(err) }
|
data := []byte("sm2 comprehensive test")
|
||||||
|
|
||||||
a, err := sm.NewSM2AndEraseKey(priv, pub)
|
a, _ := sm.NewSM2AndEraseKey(priv, pub)
|
||||||
if err != nil { t.Fatal(err) }
|
|
||||||
|
// MustSign
|
||||||
|
sig := a.MustSign(data)
|
||||||
|
if len(sig) == 0 { t.Error("MustSign failed") }
|
||||||
|
|
||||||
|
// MustVerify
|
||||||
|
if !a.MustVerify(data, sig) { t.Error("MustVerify failed") }
|
||||||
|
|
||||||
data := []byte("hello sm2")
|
// MustEncrypt
|
||||||
|
enc := a.MustEncrypt(data)
|
||||||
// Sign with cast.As
|
if len(enc) == 0 { t.Error("MustEncrypt failed") }
|
||||||
sig := cast.As(a.Sign(data))
|
|
||||||
if len(sig) == 0 { t.Error("Sign failed") }
|
// MustDecrypt
|
||||||
|
dec := a.MustDecrypt(enc)
|
||||||
// Verify
|
if !bytes.Equal(data, dec) { t.Error("MustDecrypt failed") }
|
||||||
valid, _ := a.Verify(data, sig)
|
|
||||||
if !valid { t.Error("Verify failed") }
|
|
||||||
|
|
||||||
// Encrypt with cast.As
|
|
||||||
enc := cast.As(a.EncryptBytes(data))
|
|
||||||
if len(enc) == 0 { t.Error("EncryptBytes failed") }
|
|
||||||
|
|
||||||
// Decrypt with cast.As
|
|
||||||
dec := cast.As(a.DecryptBytes(enc))
|
|
||||||
if !bytes.Equal(data, dec) { t.Error("DecryptBytes failed") }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSM3(t *testing.T) {
|
func TestSM3_Compatibility(t *testing.T) {
|
||||||
data := []byte("hello sm3")
|
data := []byte("hello sm3")
|
||||||
h1 := sm.Sm3(data)
|
|
||||||
h2 := sm.Sm3(data)
|
h := sm3.New()
|
||||||
if !bytes.Equal(h1, h2) { t.Error("SM3 non-deterministic") }
|
h.Write(data)
|
||||||
|
expected := h.Sum(nil)
|
||||||
|
|
||||||
|
if !bytes.Equal(sm.Sm3(data), expected) {
|
||||||
|
t.Error("SM3 hash mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
if sm.Sm3ToHex(data) == "" { t.Error("Sm3ToHex failed") }
|
||||||
|
if sm.Sm3ToBase64(data) == "" { t.Error("Sm3ToBase64 failed") }
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSM4(t *testing.T) {
|
func TestSM4_Exhaustive(t *testing.T) {
|
||||||
key := []byte("1234567890123456")
|
key := bytes.Repeat([]byte{0x01}, 16)
|
||||||
iv := []byte("1234567890123456")
|
iv := bytes.Repeat([]byte{0x02}, 16)
|
||||||
data := []byte("hello sm4")
|
data := []byte("sm4 exhaustive testing")
|
||||||
|
|
||||||
// CBC
|
cipher, _ := sm.NewSM4CBCWithoutEraseKey(key, iv)
|
||||||
cipher, _ := sm.NewSM4CBCAndEraseKey(key, iv)
|
|
||||||
enc := cast.As(cipher.EncryptBytes(data))
|
|
||||||
if len(enc) == 0 { t.Fatal("EncryptBytes failed") }
|
|
||||||
|
|
||||||
dec := cast.As(cipher.DecryptBytes(enc))
|
// 1. CBC
|
||||||
if !bytes.Equal(data, dec) { t.Error("DecryptBytes failed") }
|
enc := cipher.MustEncrypt(data)
|
||||||
|
if len(enc) == 0 { t.Fatal("MustEncrypt failed") }
|
||||||
|
|
||||||
|
dec := cipher.MustDecrypt(enc)
|
||||||
|
if !bytes.Equal(data, dec) { t.Error("SM4 CBC roundtrip failed") }
|
||||||
|
|
||||||
// GCM
|
// 2. GCM
|
||||||
gcm, _ := sm.NewSM4GCMAndEraseKey(key, iv)
|
gcm, _ := sm.NewSM4GCMWithoutEraseKey(key, iv[:12])
|
||||||
encG := cast.As(gcm.EncryptBytes(data))
|
encG := gcm.MustEncrypt(data)
|
||||||
decG := cast.As(gcm.DecryptBytes(encG))
|
decG := gcm.MustDecrypt(encG)
|
||||||
if !bytes.Equal(data, decG) { t.Error("GCM failed") }
|
if !bytes.Equal(data, decG) { t.Error("SM4 GCM roundtrip failed") }
|
||||||
|
|
||||||
|
// 3. TryDecrypt
|
||||||
|
damaged := append([]byte(nil), enc...)
|
||||||
|
damaged[len(damaged)-1] ^= 0xFF
|
||||||
|
|
||||||
|
// TryDecrypt should return the damaged data
|
||||||
|
decT := cipher.TryDecrypt(damaged)
|
||||||
|
if !bytes.Equal(decT, damaged) {
|
||||||
|
t.Error("TryDecrypt should return original damaged data")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user