docs: support ByPassword series and release v1.1.0 (by AI)
This commit is contained in:
parent
cff84084bc
commit
8f0a4d2939
@ -1,5 +1,13 @@
|
|||||||
# 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
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
### 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 (国密哈希)
|
||||||
@ -24,7 +25,9 @@
|
|||||||
|
|
||||||
### 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)`
|
||||||
|
|
||||||
## 📦 安装
|
## 📦 安装
|
||||||
|
|
||||||
|
|||||||
4
TEST.md
4
TEST.md
@ -1,7 +1,7 @@
|
|||||||
# Test Report: @go/crypto-sm
|
# Test Report: @go/crypto-sm
|
||||||
|
|
||||||
## 📋 测试概览
|
## 📋 测试概览
|
||||||
- **测试时间**: 2026-05-06
|
- **测试时间**: 2026-05-07
|
||||||
- **测试环境**: darwin/amd64
|
- **测试环境**: darwin/amd64
|
||||||
- **Go 版本**: 1.25.0
|
- **Go 版本**: 1.25.0
|
||||||
|
|
||||||
@ -11,6 +11,8 @@
|
|||||||
| `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` 规范。
|
||||||
|
|||||||
2
go.mod
2
go.mod
@ -3,7 +3,7 @@ module apigo.cc/go/crypto-sm
|
|||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
apigo.cc/go/crypto v1.0.6
|
apigo.cc/go/crypto v1.1.0
|
||||||
apigo.cc/go/encoding v1.0.5
|
apigo.cc/go/encoding v1.0.5
|
||||||
apigo.cc/go/safe v1.0.5
|
apigo.cc/go/safe v1.0.5
|
||||||
github.com/emmansun/gmsm v0.28.0
|
github.com/emmansun/gmsm v0.28.0
|
||||||
|
|||||||
30
sm2.go
30
sm2.go
@ -28,6 +28,36 @@ 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,7 +36,20 @@ 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
|
||||||
|
|||||||
59
sm_password_test.go
Normal file
59
sm_password_test.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user