Compare commits

..

No commits in common. "main" and "v1.0.6" have entirely different histories.
main ... v1.0.6

9 changed files with 22 additions and 141 deletions

4
.gitignore vendored
View File

@ -1,4 +0,0 @@
.ai/
.geminiignore
.gemini
/CODE-FULL.md

View File

@ -1,13 +1,5 @@
# Changelog: @go/crypto-sm # Changelog: @go/crypto-sm
## [v1.1.0] - 2026-05-07
### Added
- **国密算法强化**:同步引入基于 Argon2id 的密码派生密钥能力。
- **便捷构造器**:新增 `NewSM4CBCByPassword`, `NewSM4GCMByPassword``NewSM2ByPassword` API其中 SM2 支持基于密码的确定性密钥对生成。
- **内存安全**:所有密码构造器均强制执行 `EraseKey` 策略,派生后物理擦除密码与盐。
- **依赖更新**:升级 `apigo.cc/go/crypto` 至 v1.1.0。
## [v1.0.6] - 2026-05-06 ## [v1.0.6] - 2026-05-06
### Changed ### Changed

View File

@ -16,7 +16,6 @@
### 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 GenerateSM2KeyPair() (priv, pub []byte, err error)` - `func GenerateSM2KeyPair() (priv, pub []byte, err error)`
### SM3 (国密哈希) ### SM3 (国密哈希)
@ -25,9 +24,7 @@
### 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)`
## 📦 安装 ## 📦 安装

View File

@ -1,7 +1,7 @@
# Test Report: @go/crypto-sm # Test Report: @go/crypto-sm
## 📋 测试概览 ## 📋 测试概览
- **测试时间**: 2026-05-07 - **测试时间**: 2026-05-06
- **测试环境**: darwin/amd64 - **测试环境**: darwin/amd64
- **Go 版本**: 1.25.0 - **Go 版本**: 1.25.0
@ -11,8 +11,6 @@
| `TestSM2` | PASS | SM2 签名、验签与加解密测试。 | | `TestSM2` | PASS | SM2 签名、验签与加解密测试。 |
| `TestSM3` | PASS | SM3 哈希确定性验证。 | | `TestSM3` | PASS | SM3 哈希确定性验证。 |
| `TestSM4` | PASS | SM4 CBC/GCM 模式加解密测试。 | | `TestSM4` | PASS | SM4 CBC/GCM 模式加解密测试。 |
| `TestSMPassword` | PASS | 基于密码的 SM4 与 SM2 构造器测试。 |
| `TestSM2Deterministic` | PASS | SM2 密钥基于密码的确定性生成验证。 |
## 🛡️ 鲁棒性防御 (Robustness) ## 🛡️ 鲁棒性防御 (Robustness)
- **摩擦消除**:移除 `Must` 冗余 API完全对齐 `go/crypto` 规范。 - **摩擦消除**:移除 `Must` 冗余 API完全对齐 `go/crypto` 规范。

22
go.mod
View File

@ -3,15 +3,23 @@ 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.6
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/cast v1.2.8 // indirect
golang.org/x/crypto v0.51.0 // indirect apigo.cc/go/rand v1.0.5 // indirect
golang.org/x/sys v0.44.0 // indirect golang.org/x/crypto v0.50.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

20
go.sum
View File

@ -1,16 +1,8 @@
apigo.cc/go/cast v1.3.3 h1:aln5eDR5DZVWVzZ/y5SJh1gQNgWv2sT82I25NaO9g34= apigo.cc/go/cast v1.2.8 h1:plb676DH2TjYljzf8OEMGT6lIhmZ/xaxEFfs0kDOiSI=
apigo.cc/go/cast v1.3.3/go.mod h1:lGlwImiOvHxG7buyMWhFzcdvQzmSaoKbmr7bcDfUpHk= apigo.cc/go/cast v1.2.8/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
View File

@ -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
View File

@ -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

View File

@ -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")
}
}