feat: implement unified DefaultAES interface and align infrastructure

This commit is contained in:
AI Engineer 2026-05-12 23:10:29 +08:00
parent eb90ce303c
commit d977f8a727
6 changed files with 144 additions and 2 deletions

3
.gitignore vendored
View File

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

5
CHANGELOG-LATEST.md Normal file
View File

@ -0,0 +1,5 @@
## [v1.1.2] - 2026-05-12
### Added
- **统一配置解密接口 (加固版)**:新增 `OnSetDefaultAES``SetDefaultAES` 注入接口,支持自动擦除密钥与自动锁定通道。
- **基础设施对齐**:同步更新 `redis`, `api`, `db`, `mail` 等项目接入 `crypto` 安全解密体系。

View File

@ -1,6 +1,13 @@
# Changelog: @go/crypto # Changelog: @go/crypto
## [v1.1.0] - 2026-05-07 ## [v1.1.2] - 2026-05-12
### Added
- **统一配置解密接口 (加固版)**:新增 `OnSetDefaultAES` 回调与 `SetDefaultAES` 注入接口。
- **极致内存安全**`SetDefaultAES` 注入生产密钥后将**自动擦除** (ZeroMemory) 传入的密钥源字节,并**自动锁定**注册通道。
- **基础设施对齐**:同步更新 `redis`, `api`, `db`, `mail` 等项目,移除本地 `confAes`,全面接入安全加固后的 `crypto.confAES` 体系。
## [v1.1.1] - 2026-05-12
### Added ### Added
- **密码学强化**:新增基于 Argon2id 的密码派生密钥 (KDF) 支持。 - **密码学强化**:新增基于 Argon2id 的密码派生密钥 (KDF) 支持。

View File

@ -41,6 +41,12 @@
- `func GenerateEd25519KeyPair() (priv, pub []byte, err error)` - `func GenerateEd25519KeyPair() (priv, pub []byte, err error)`
- `func GenerateX25519KeyPair() (priv, pub []byte, err error)` - `func GenerateX25519KeyPair() (priv, pub []byte, err error)`
### 全局配置解密 (Default AES)
为实现全项目配置解密的统一管理与生产环境密钥注入,提供以下全局接口:
- `var DefaultAES *Symmetric`:全局默认 AES 实例(初始使用内置硬编码密钥)。
- `func OnSetDefaultAES(h func(*Symmetric))`:注册 `DefaultAES` 变更回调。
- `func SetDefaultAES(key, iv []byte)`:注入生产环境密钥并触发所有回调更新。
### 哈希算法 (Hash) ### 哈希算法 (Hash)
- `func MD5(data []byte) []byte` - `func MD5(data []byte) []byte`
- `func Sha256(data []byte) []byte` - `func Sha256(data []byte) []byte`

69
default.go Normal file
View File

@ -0,0 +1,69 @@
package crypto
import (
"sync"
)
var (
// defaultAES 用于配置解密的默认 AES 实例 (私有)
defaultAES *Symmetric
defaultAESHandlers []func(*Symmetric)
defaultAESLock sync.RWMutex
defaultAESOnce sync.Once
isDefaultAESSet bool
isLocked bool
)
func init() {
// 默认硬编码密钥,仅用于基础防护。
defaultAES, _ = NewAESGCMWithoutEraseKey(
[]byte("?GQ$0K0GgLdO=f+~L68PLm$uhKr4'=tV"),
[]byte("VFs7@sK61cj^f?HZ"),
)
}
// OnSetDefaultAES 注册配置解密 AES 实例变更的回调。
// 即使在 SetDefaultAES 调用之后,此方法仍然有效,直到调用 LockDefaultAES。
// 注册时会立即以当前实例(硬编码或已注入的生产密钥)调用一次回调。
func OnSetDefaultAES(h func(*Symmetric)) {
defaultAESLock.Lock()
defer defaultAESLock.Unlock()
if isLocked {
// 如果已经锁定,不再允许注册新的回调
return
}
defaultAESHandlers = append(defaultAESHandlers, h)
if defaultAES != nil {
h(defaultAES)
}
}
// SetDefaultAES 设置全局默认 AES 密钥与 IV。
// 仅允许调用一次。调用后会立即锁定注册通道,后续 OnSetDefaultAES 将不再起作用。
// 注意:此方法会自动擦除 (ZeroMemory) 传入的 key 和 iv 字节切片。
func SetDefaultAES(key, iv []byte) {
defaultAESOnce.Do(func() {
// 创建新实例并擦除原始密钥
newAES, err := NewAESGCMAndEraseKey(key, iv)
if err != nil {
return
}
defaultAESLock.Lock()
defer defaultAESLock.Unlock()
defaultAES = newAES
isLocked = true
// 通知所有在初始化阶段注册的观察者
for _, h := range defaultAESHandlers {
h(defaultAES)
}
// 彻底清理并释放引用
defaultAESHandlers = nil
})
}

54
default_test.go Normal file
View File

@ -0,0 +1,54 @@
package crypto
import (
"bytes"
"testing"
)
func TestDefaultAES(t *testing.T) {
// 1. 测试初始默认值
var confAES *Symmetric
OnSetDefaultAES(func(aes *Symmetric) {
confAES = aes
})
if confAES == nil {
t.Fatal("confAES should be initialized by OnSetDefaultAES")
}
// 2. 测试 SetDefaultAES 触发更新与锁定
rawKey := []byte("12345678901234567890123456789012")
newKey := bytes.Clone(rawKey)
newIv := []byte("123456789012")
SetDefaultAES(newKey, newIv)
// 验证密钥已被擦除 (ZeroMemory 会用随机 junk 覆盖,所以检查是否不再等于原始值)
if bytes.Equal(newKey, rawKey) {
t.Error("newKey should be overwritten after SetDefaultAES")
}
// 此时 confAES 应该已经被回调更新了
data := []byte("hello world")
encrypted, err := confAES.EncryptAndErase(bytes.Clone(data))
if err != nil {
t.Fatalf("Encrypt failed: %v", err)
}
// 3. 测试安全性SetDefaultAES 之后不再允许 OnSetDefaultAES
var blockedAES *Symmetric
OnSetDefaultAES(func(aes *Symmetric) {
blockedAES = aes
})
if blockedAES != nil {
t.Error("OnSetDefaultAES should be blocked after SetDefaultAES (auto-lock)")
}
// 4. 测试 SetDefaultAES 仅允许一次
anotherKey := []byte("another key 32 bytes long.......")
SetDefaultAES(anotherKey, newIv)
// 验证密钥没有改变(通过解密验证)
_, err = confAES.DecryptBytes(encrypted)
if err != nil {
t.Errorf("Decryption should still work with the first injected key: %v", err)
}
}