Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7f13b5de6 | ||
|
|
d88d8bdcd7 | ||
|
|
7d17b20a19 |
57
AI.md
57
AI.md
@ -3,11 +3,54 @@
|
||||
本索引供 AI 模型理解 `@go/crypto-sm` 的逻辑,以确保代码与 `@go/crypto` 行为一致。
|
||||
|
||||
## 🤖 AI 行为准则
|
||||
1. **接口对齐**:SM2/SM4 必须实现 `@go/crypto` 定义的非对称与对称加密接口。
|
||||
2. **内存闭环**:所有算法构造必须默认支持 `AndEraseKey` 范式。
|
||||
3. **静默原则**:解密函数推荐使用 `DecryptBytesN`(静默模式)。
|
||||
|
||||
## 🛠 关键算法约定
|
||||
- SM2 签名强制使用 `sm2` 特有的签名接口。
|
||||
- SM4 CBC/GCM 使用 `gmsm` 底层包,但 API 必须完全遵循 `Symmetric` 的设计。
|
||||
- 所有输出不得包含调试日志。
|
||||
1. **内存安全优先**:**首选 `SafeBuf` 接口**。在处理敏感数据(如密钥、明文)时,强制优先使用 `SafeBuf` 避免明文在内存中暴露。
|
||||
2. **接口对齐**:必须实现 `@go/crypto` 定义的非对称与对称加密接口。
|
||||
3. **闭环义务**:任何构造的 `Asymmetric` 或 `Symmetric` 对象必须由 AI 生成 `defer obj.Close()` 代码。
|
||||
4. **鲁棒性优先**:处理配置或业务数据解密时,优先使用 `Must` 或 `Try` 系列 API。
|
||||
|
||||
## 🛠 API Reference
|
||||
|
||||
### SM2 (国密非对称)
|
||||
- `func NewSM2(priv, pub *safe.SafeBuf) (*crypto.Asymmetric, error)`
|
||||
- `func NewSM2AndEraseKey(priv, pub []byte) (*crypto.Asymmetric, error)`
|
||||
- `func NewSM2WithOutEraseKey(priv, pub []byte) (*crypto.Asymmetric, error)`
|
||||
- `func GenerateSM2KeyPair() ([]byte, []byte, error)`
|
||||
- `func (a *SM2Algorithm) ParsePrivateKey(der []byte) (any, error)`
|
||||
- `func (a *SM2Algorithm) ParsePublicKey(der []byte) (any, error)`
|
||||
- `func (a *SM2Algorithm) Sign(priv any, data []byte, hash ...stdcrypto.Hash) ([]byte, error)`
|
||||
- `func (a *SM2Algorithm) Verify(pub any, data, sig []byte, hash ...stdcrypto.Hash) (bool, error)`
|
||||
- `func (a *SM2Algorithm) Encrypt(pub any, data []byte) ([]byte, error)`
|
||||
- `func (a *SM2Algorithm) Decrypt(priv any, data []byte) ([]byte, error)`
|
||||
|
||||
### SM3 (国密摘要)
|
||||
- `func Sm3(data ...[]byte) []byte`
|
||||
- `func Sm3ToHex(data []byte) string`
|
||||
- `func Sm3ToBase64(data []byte) string`
|
||||
- `func Sm3ToUrlBase64(data []byte) string`
|
||||
|
||||
### SM4 (国密对称)
|
||||
- `func NewSM4CBC(key, iv *safe.SafeBuf) (*crypto.Symmetric, error)`
|
||||
- `func NewSM4CBCAndEraseKey(key, iv []byte) (*crypto.Symmetric, error)`
|
||||
- `func NewSM4CBCWithOutEraseKey(key, iv []byte) (*crypto.Symmetric, error)`
|
||||
- `func NewSM4GCM(key, iv *safe.SafeBuf) (*crypto.Symmetric, error)`
|
||||
- `func NewSM4GCMAndEraseKey(key, iv []byte) (*crypto.Symmetric, error)`
|
||||
- `func NewSM4GCMWithOutEraseKey(key, iv []byte) (*crypto.Symmetric, error)`
|
||||
- `func (s *SM4Cipher) Encrypt(data, key, iv []byte) ([]byte, error)`
|
||||
- `func (s *SM4Cipher) Decrypt(data, key, iv []byte) ([]byte, error)`
|
||||
|
||||
## 🧩 典型模式 (Best Practices)
|
||||
|
||||
* **✅ 安全处理 (SafeBuf 优先)**:
|
||||
```go
|
||||
sb := safe.NewSafeBuf(sensitiveData)
|
||||
encrypted, _ := sm4.Encrypt(sb)
|
||||
decSb, _ := sm4.Decrypt(encrypted)
|
||||
defer decSb.Close()
|
||||
```
|
||||
|
||||
* **✅ 鲁棒性业务 (Must/Try 模式)**:
|
||||
```go
|
||||
config := sm4.TryDecrypt(configData)
|
||||
sig := sm2.MustSign(data)
|
||||
```
|
||||
|
||||
@ -17,17 +17,18 @@
|
||||
- `func NewSM2AndEraseKey(priv, pub []byte) (*crypto.Asymmetric, error)`
|
||||
- `func NewSM2WithOutEraseKey(priv, pub []byte) (*crypto.Asymmetric, error)`
|
||||
- `func GenerateSM2KeyPair() ([]byte, []byte, error)`
|
||||
- *注:SM2 继承 `Asymmetric` 接口,支持 `Sign`/`Verify`/`Encrypt`/`Decrypt`。*
|
||||
- *注:SM2 继承 `Asymmetric` 接口,支持所有 `crypto.Asymmetric` 方法 (含 `Must` 和 `Try` 系列)。*
|
||||
|
||||
### SM3 (国密摘要)
|
||||
- `func Sm3(data ...[]byte) []byte`
|
||||
- `func Sm3ToHex(data []byte) string`
|
||||
- `func Sm3ToBase64(data []byte) string`
|
||||
- `func Sm3ToUrlBase64(data []byte) string`
|
||||
|
||||
### SM4 (国密对称)
|
||||
- `func NewSM4CBCAndEraseKey(key, iv []byte) (*crypto.Symmetric, error)`
|
||||
- `func NewSM4GCMAndEraseKey(key, iv []byte) (*crypto.Symmetric, error)`
|
||||
- *注:SM4 继承 `Symmetric` 接口,支持 `EncryptBytes`/`DecryptBytes`。*
|
||||
- *注:SM4 继承 `Symmetric` 接口,支持所有 `crypto.Symmetric` 方法 (含 `Must` 和 `Try` 系列)。*
|
||||
|
||||
## 📦 安装
|
||||
```bash
|
||||
|
||||
8
go.mod
8
go.mod
@ -3,14 +3,14 @@ module apigo.cc/go/crypto-sm
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
apigo.cc/go/crypto v1.0.0
|
||||
apigo.cc/go/encoding v1.0.0
|
||||
apigo.cc/go/safe v1.0.0
|
||||
apigo.cc/go/crypto v1.0.3
|
||||
apigo.cc/go/encoding v1.0.3
|
||||
apigo.cc/go/safe v1.0.3
|
||||
github.com/emmansun/gmsm v0.28.0
|
||||
)
|
||||
|
||||
require (
|
||||
apigo.cc/go/rand v1.0.2 // indirect
|
||||
apigo.cc/go/rand v1.0.3 // indirect
|
||||
golang.org/x/crypto v0.50.0 // indirect
|
||||
golang.org/x/sys v0.43.0 // indirect
|
||||
)
|
||||
|
||||
10
sm4.go
10
sm4.go
@ -16,8 +16,6 @@ type SM4Cipher struct {
|
||||
var SM4CBC = &SM4Cipher{useGCM: false}
|
||||
var SM4GCM = &SM4Cipher{useGCM: true}
|
||||
|
||||
// --- Factory functions matching your style ---
|
||||
|
||||
func NewSM4CBC(safeKeyBuf, safeIvBuf *safe.SafeBuf) (*crypto.Symmetric, error) {
|
||||
return crypto.NewSymmetric(SM4CBC, safeKeyBuf, safeIvBuf)
|
||||
}
|
||||
@ -49,10 +47,8 @@ func (s *SM4Cipher) Encrypt(data []byte, key []byte, iv []byte) ([]byte, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// SM4-GCM nonce 推荐 12 字节
|
||||
return sm4gcm.Seal(nil, iv[:sm4gcm.NonceSize()], data, nil), nil
|
||||
} else {
|
||||
// SM4 块大小固定为 16
|
||||
blockSize := block.BlockSize()
|
||||
paddedData := crypto.Pkcs5Padding(data, blockSize)
|
||||
blockMode := cipher.NewCBCEncrypter(block, iv[:blockSize])
|
||||
@ -82,6 +78,10 @@ func (s *SM4Cipher) Decrypt(data []byte, key []byte, iv []byte) ([]byte, error)
|
||||
blockMode := cipher.NewCBCDecrypter(block, iv[:blockSize])
|
||||
plainText := make([]byte, len(data))
|
||||
blockMode.CryptBlocks(plainText, data)
|
||||
return crypto.Pkcs5UnPadding(plainText), nil
|
||||
unpadded := crypto.Pkcs5UnPadding(plainText)
|
||||
if unpadded == nil {
|
||||
return nil, errors.New("padding error")
|
||||
}
|
||||
return unpadded, nil
|
||||
}
|
||||
}
|
||||
|
||||
77
sm_test.go
77
sm_test.go
@ -13,13 +13,21 @@ func TestSM2_AllModes(t *testing.T) {
|
||||
data := []byte("sm2 comprehensive test")
|
||||
|
||||
a, _ := sm.NewSM2AndEraseKey(priv, pub)
|
||||
sig, err := a.Sign(data)
|
||||
if err != nil { t.Fatal(err) }
|
||||
if ok, _ := a.Verify(data, sig); !ok { t.Error("SM2 Sign/Verify failed") }
|
||||
|
||||
enc, _ := a.Encrypt(data)
|
||||
dec, _ := a.Decrypt(enc)
|
||||
if !bytes.Equal(data, dec) { t.Error("SM2 Encrypt/Decrypt failed") }
|
||||
// MustSign
|
||||
sig := a.MustSign(data)
|
||||
if len(sig) == 0 { t.Error("MustSign failed") }
|
||||
|
||||
// MustVerify
|
||||
if !a.MustVerify(data, sig) { t.Error("MustVerify failed") }
|
||||
|
||||
// MustEncrypt
|
||||
enc := a.MustEncrypt(data)
|
||||
if len(enc) == 0 { t.Error("MustEncrypt failed") }
|
||||
|
||||
// MustDecrypt
|
||||
dec := a.MustDecrypt(enc)
|
||||
if !bytes.Equal(data, dec) { t.Error("MustDecrypt failed") }
|
||||
}
|
||||
|
||||
func TestSM3_Compatibility(t *testing.T) {
|
||||
@ -45,58 +53,25 @@ func TestSM4_Exhaustive(t *testing.T) {
|
||||
cipher, _ := sm.NewSM4CBCWithOutEraseKey(key, iv)
|
||||
|
||||
// 1. CBC
|
||||
enc, _ := cipher.EncryptBytes(data)
|
||||
dec, _ := cipher.DecryptBytes(enc)
|
||||
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") }
|
||||
|
||||
// 2. GCM
|
||||
gcm, _ := sm.NewSM4GCMWithOutEraseKey(key, iv[:12])
|
||||
encG, _ := gcm.EncryptBytes(data)
|
||||
decG, _ := gcm.DecryptBytes(encG)
|
||||
encG := gcm.MustEncrypt(data)
|
||||
decG := gcm.MustDecrypt(encG)
|
||||
if !bytes.Equal(data, decG) { t.Error("SM4 GCM roundtrip failed") }
|
||||
|
||||
// 3. Negative Padding - Expect error for CBC but GCM should behave differently
|
||||
// 3. TryDecrypt
|
||||
damaged := append([]byte(nil), enc...)
|
||||
damaged[len(damaged)-1] ^= 0xFF
|
||||
// For CBC, expect padding error
|
||||
if _, err := cipher.DecryptBytes(damaged); err == nil {
|
||||
t.Log("Padding error not detected in damaged CBC ciphertext (acceptable depending on implementation)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSM4_Concurrency(t *testing.T) {
|
||||
key := bytes.Repeat([]byte{0x01}, 16)
|
||||
iv := bytes.Repeat([]byte{0x02}, 16)
|
||||
cipher, _ := sm.NewSM4CBCWithOutEraseKey(key, iv)
|
||||
data := []byte("concurrent")
|
||||
|
||||
for i := 0; i < 50; i++ {
|
||||
t.Run("Concurrent", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
enc, _ := cipher.EncryptBytes(data)
|
||||
dec, _ := cipher.DecryptBytes(enc)
|
||||
if !bytes.Equal(data, dec) { t.Error("Data race detected") }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSM2_Sign(b *testing.B) {
|
||||
priv, pub, _ := sm.GenerateSM2KeyPair()
|
||||
a, _ := sm.NewSM2WithOutEraseKey(priv, pub)
|
||||
data := []byte("benchmark data")
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = a.Sign(data)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSM4_GCM(b *testing.B) {
|
||||
key := make([]byte, 16)
|
||||
iv := make([]byte, 12)
|
||||
data := make([]byte, 1024)
|
||||
cipher, _ := sm.NewSM4GCMWithOutEraseKey(key, iv)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = cipher.EncryptBytes(data)
|
||||
|
||||
// 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