Compare commits
No commits in common. "main" and "v1.3.0" have entirely different histories.
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,3 @@
|
||||
.ai/
|
||||
|
||||
.geminiignore
|
||||
.gemini
|
||||
/CODE-FULL.md
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
## [v1.1.2] - 2026-05-12
|
||||
|
||||
### Added
|
||||
- **统一配置解密接口 (加固版)**:新增 `OnSetDefaultAES` 与 `SetDefaultAES` 注入接口,支持自动擦除密钥与自动锁定通道。
|
||||
- **基础设施对齐**:同步更新 `redis`, `api`, `db`, `mail` 等项目接入 `crypto` 安全解密体系。
|
||||
@ -1,13 +1,6 @@
|
||||
# Changelog: @go/crypto
|
||||
|
||||
## [v1.1.2] - 2026-05-12
|
||||
|
||||
### Added
|
||||
- **统一配置解密接口 (加固版)**:新增 `OnSetDefaultAES` 回调与 `SetDefaultAES` 注入接口。
|
||||
- **极致内存安全**:`SetDefaultAES` 注入生产密钥后将**自动擦除** (ZeroMemory) 传入的密钥源字节,并**自动锁定**注册通道。
|
||||
- **基础设施对齐**:同步更新 `redis`, `api`, `db`, `mail` 等项目,移除本地 `confAes`,全面接入安全加固后的 `crypto.confAES` 体系。
|
||||
|
||||
## [v1.1.1] - 2026-05-12
|
||||
## [v1.1.0] - 2026-05-07
|
||||
|
||||
### Added
|
||||
- **密码学强化**:新增基于 Argon2id 的密码派生密钥 (KDF) 支持。
|
||||
|
||||
@ -41,12 +41,6 @@
|
||||
- `func GenerateEd25519KeyPair() (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)
|
||||
- `func MD5(data []byte) []byte`
|
||||
- `func Sha256(data []byte) []byte`
|
||||
|
||||
69
default.go
69
default.go
@ -1,69 +0,0 @@
|
||||
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
|
||||
})
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
8
go.mod
8
go.mod
@ -3,13 +3,13 @@ module apigo.cc/go/crypto
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
apigo.cc/go/cast v1.3.3
|
||||
apigo.cc/go/encoding v1.3.1
|
||||
apigo.cc/go/safe v1.3.1
|
||||
apigo.cc/go/cast v1.3.0
|
||||
apigo.cc/go/encoding v1.3.0
|
||||
apigo.cc/go/safe v1.3.0
|
||||
golang.org/x/crypto v0.51.0
|
||||
)
|
||||
|
||||
require (
|
||||
apigo.cc/go/rand v1.3.1 // indirect
|
||||
apigo.cc/go/rand v1.3.0 // indirect
|
||||
golang.org/x/sys v0.44.0 // indirect
|
||||
)
|
||||
|
||||
16
go.sum
16
go.sum
@ -1,11 +1,11 @@
|
||||
apigo.cc/go/cast v1.3.3 h1:aln5eDR5DZVWVzZ/y5SJh1gQNgWv2sT82I25NaO9g34=
|
||||
apigo.cc/go/cast v1.3.3/go.mod h1:lGlwImiOvHxG7buyMWhFzcdvQzmSaoKbmr7bcDfUpHk=
|
||||
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=
|
||||
apigo.cc/go/cast v1.2.10 h1:wa9/hz6GW6Z+5co6l7LftMn2Eo06WpVHHDCCQphnmH8=
|
||||
apigo.cc/go/cast v1.2.10/go.mod h1:lGlwImiOvHxG7buyMWhFzcdvQzmSaoKbmr7bcDfUpHk=
|
||||
apigo.cc/go/encoding v1.1.2 h1:reSrLkyYrtZsf4S91XPdyBY2AQpvA43n9q0Q9wz5uJA=
|
||||
apigo.cc/go/encoding v1.1.2/go.mod h1:iLuvrYHEK8mLnk8jijx5Sv1tInFreny0yGNBouA1d20=
|
||||
apigo.cc/go/rand v1.0.6 h1:p51rkaDrYUdZPIRbQAujZmQelWg2ipAMts33A/tG7QE=
|
||||
apigo.cc/go/rand v1.0.6/go.mod h1:mZ/4Soa3bk+XvDaqPWJuUe1bfEi4eThBj1XmEAuYxsk=
|
||||
apigo.cc/go/safe v1.0.7 h1:f0d+v9K2dHPyG5DNqhyddCmAmSiIqIfkPi/AMED/iQI=
|
||||
apigo.cc/go/safe v1.0.7/go.mod h1:Hu7TVDWPe/I+nBZfYJH4mt+ROzG+rwk2D1zHTXj/2eE=
|
||||
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
|
||||
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
|
||||
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user