Compare commits
No commits in common. "d2ba99cb9d728f3e566862f479315e927d64ca4b" and "d3962a08ac990fee8b472e371b0bc7eef1d38ab3" have entirely different histories.
d2ba99cb9d
...
d3962a08ac
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
go.sum
|
|
||||||
20
AI.md
20
AI.md
@ -1,4 +1,4 @@
|
|||||||
# AI Coding Context: @go/safe (v1.0.4)
|
# AI Coding Context: @go/safe
|
||||||
|
|
||||||
本索引供 AI 模型理解 `@go/safe` 的设计规范,以生成符合本项目“安全闭环、防御优先”哲学的代码。
|
本索引供 AI 模型理解 `@go/safe` 的设计规范,以生成符合本项目“安全闭环、防御优先”哲学的代码。
|
||||||
|
|
||||||
@ -7,7 +7,7 @@
|
|||||||
1. **优先使用 (Tier 1)**:处理敏感数据,首选 `NewSafeBufAndErase` 或 `NewSafeBuf`,配合 `Open()/Close()` 闭环操作。
|
1. **优先使用 (Tier 1)**:处理敏感数据,首选 `NewSafeBufAndErase` 或 `NewSafeBuf`,配合 `Open()/Close()` 闭环操作。
|
||||||
2. **安全约束**:
|
2. **安全约束**:
|
||||||
- `SafeBuf` 内部已自动处理 `LockMemory` 和 `ZeroMemory`,**严禁**对已托管的 `SafeBuf` 或其内部数据再次手动调用 `LockMemory`。
|
- `SafeBuf` 内部已自动处理 `LockMemory` 和 `ZeroMemory`,**严禁**对已托管的 `SafeBuf` 或其内部数据再次手动调用 `LockMemory`。
|
||||||
- 任何 `Open()` 获得的 `SecretPlaintext`,**必须**在同一作用域内 `defer secret.Close()`。
|
- 任何 `Open()` 获得的 `SecretPlaintext`,**必须**在同一作用域内 `defer sp.Close()`。
|
||||||
3. **系统与高级工具 (Tier 2)**:系统级防御(如 `DisableCoreDump`)应在 `init()` 调用。`ZeroMemory` 等底层 API 仅限高性能 buffer 复用场景。
|
3. **系统与高级工具 (Tier 2)**:系统级防御(如 `DisableCoreDump`)应在 `init()` 调用。`ZeroMemory` 等底层 API 仅限高性能 buffer 复用场景。
|
||||||
|
|
||||||
## 🛠 API Reference
|
## 🛠 API Reference
|
||||||
@ -17,8 +17,8 @@
|
|||||||
- `func NewSafeBufAndErase(raw []byte) *SafeBuf`:创建实例并自动擦除原始明文,**首选 API**。
|
- `func NewSafeBufAndErase(raw []byte) *SafeBuf`:创建实例并自动擦除原始明文,**首选 API**。
|
||||||
- `func NewSafeBufFromEncrypted(cipher, salt []byte) *SafeBuf`:从加密源重建保护实例。
|
- `func NewSafeBufFromEncrypted(cipher, salt []byte) *SafeBuf`:从加密源重建保护实例。
|
||||||
- `func NewSafeString(raw []byte) (*SecretPlaintext, string)`:创建临时的敏感字符串。
|
- `func NewSafeString(raw []byte) (*SecretPlaintext, string)`:创建临时的敏感字符串。
|
||||||
- `func (safeBuf *SafeBuf) Open() *SecretPlaintext`:解密 SafeBuf。**调用后必须 `defer secret.Close()`**。
|
- `func (sb *SafeBuf) Open() *SecretPlaintext`:解密 SafeBuf。**调用后必须 `defer sp.Close()`**。
|
||||||
- `func (safeBuf *SafeBuf) Close()`:销毁加密实例并擦除加密内容。
|
- `func (sb *SafeBuf) Close()`:销毁加密实例并擦除加密内容。
|
||||||
|
|
||||||
### 内存管理与防御
|
### 内存管理与防御
|
||||||
- `func LockMemory(buf []byte) error`:锁定内存页,防止 Swap。
|
- `func LockMemory(buf []byte) error`:锁定内存页,防止 Swap。
|
||||||
@ -37,18 +37,18 @@
|
|||||||
* **✅ 场景:标准敏感数据处理**:
|
* **✅ 场景:标准敏感数据处理**:
|
||||||
```go
|
```go
|
||||||
// 数据使用后立即销毁,无需二次操作
|
// 数据使用后立即销毁,无需二次操作
|
||||||
safeBuf := safe.NewSafeBufAndErase(secretData)
|
sb := safe.NewSafeBufAndErase(secretData)
|
||||||
defer safeBuf.Close()
|
defer sb.Close()
|
||||||
|
|
||||||
secret := safeBuf.Open()
|
sp := sb.Open()
|
||||||
defer secret.Close() // 闭环清理明文副本
|
defer sp.Close() // 闭环清理明文副本
|
||||||
process(secret.String())
|
process(sp.String())
|
||||||
```
|
```
|
||||||
|
|
||||||
* **✅ 系统级防御**:
|
* **✅ 系统级防御**:
|
||||||
```go
|
```go
|
||||||
func init() {
|
func init() {
|
||||||
_ = safe.DisableCoreDump()
|
safe.DisableCoreDump()
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,16 +1,5 @@
|
|||||||
# Changelog: @go/safe
|
# Changelog: @go/safe
|
||||||
|
|
||||||
## [v1.0.4] - 2026-05-01
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- **Windows 兼容性**:修复 `mem_windows.go` 缺失 `unsafe` 导入导致的编译失败。
|
|
||||||
|
|
||||||
### Improved
|
|
||||||
- **命名规范**:重构短变量名(如 `sb`, `sp`)为更具描述性的名称,提升代码可读性。
|
|
||||||
- **鲁棒性增强**:为 `ChaCha20` 加解密增加长度校验与错误处理。
|
|
||||||
- **安全性提升**:改进 `ZeroMemory` 的随机种子生成机制,引入 `crypto/rand` 熵池。
|
|
||||||
- **内存锁定处理**:显式处理 `LockMemory`/`UnlockMemory` 的潜在错误。
|
|
||||||
|
|
||||||
## [v1.0.0] - 2026-04-22
|
## [v1.0.0] - 2026-04-22
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
30
README.md
30
README.md
@ -13,27 +13,17 @@
|
|||||||
|
|
||||||
## 🛠 API Reference
|
## 🛠 API Reference
|
||||||
|
|
||||||
### 内存保护与擦除
|
### 内存保护
|
||||||
- `func LockMemory(buf []byte) error`: 锁定内存页,防止交换。
|
- `func LockMemory(buf []byte) error`
|
||||||
- `func UnlockMemory(buf []byte) error`: 解锁内存页。
|
- `func UnlockMemory(buf []byte) error`
|
||||||
- `func DisableCoreDump() error`: 禁止进程核心转储。
|
- `func DisableCoreDump() error`
|
||||||
- `func ZeroMemory(buf []byte)`: 使用随机种子物理覆盖内存。
|
|
||||||
|
|
||||||
### 安全存储 (SafeBuf)
|
### 安全存储
|
||||||
- `func NewSafeBuf(data []byte) *SafeBuf`: 创建加密存储的缓冲。
|
- `func NewSafeBuf(data []byte) *SafeBuf`
|
||||||
- `func NewSafeBufAndErase(data []byte) *SafeBuf`: 创建并擦除原始明文。
|
- `func NewSafeBufAndErase(data []byte) *SafeBuf`
|
||||||
- `func NewSafeBufFromEncrypted(cipher, salt []byte) *SafeBuf`: 从密文恢复。
|
- `func NewSafeBufFromEncrypted(cipher, salt []byte) *SafeBuf`
|
||||||
- `func (s *SafeBuf) Open() *SecretPlaintext`: 解密并返回受保护的明文副本。
|
- `func MakeSafeToken(size int) []byte`
|
||||||
- `func (s *SafeBuf) Close()`: 擦除并释放 SafeBuf。
|
- `func SetSafeBufObfuscator(enc, dec)`
|
||||||
|
|
||||||
### 敏感字符串与令牌
|
|
||||||
- `func NewSafeString(data []byte) (*SecretPlaintext, string)`: 创建临时的安全字符串。
|
|
||||||
- `func MakeSafeToken(size int) []byte`: 生成带随机偏移的安全令牌。
|
|
||||||
|
|
||||||
### 混淆器与算法
|
|
||||||
- `func SetSafeBufObfuscator(enc, dec)`: 自定义 SafeBuf 的底层加密逻辑。
|
|
||||||
- `func EncryptChaCha20(raw, key, salt []byte) []byte`
|
|
||||||
- `func DecryptChaCha20(cipher, key, salt []byte) []byte`
|
|
||||||
|
|
||||||
## 📦 安装
|
## 📦 安装
|
||||||
|
|
||||||
|
|||||||
49
TEST.md
49
TEST.md
@ -1,37 +1,24 @@
|
|||||||
# Test Report: @go/safe
|
# Test Report: @go/safe
|
||||||
|
|
||||||
## 测试环境
|
## 📋 测试概览
|
||||||
- **操作系统**: darwin
|
- **测试时间**: 2026-04-22
|
||||||
- **架构**: amd64
|
- **测试环境**: darwin/amd64
|
||||||
- **Go 版本**: 1.25.0
|
- **Go 版本**: 1.25.0
|
||||||
|
|
||||||
## 覆盖场景
|
## ✅ 功能测试 (Functional Tests)
|
||||||
1. **SafeBuf 基础功能**: 验证 `NewSafeBuf`, `Open`, `Close` 的闭环逻辑。
|
| 场景 | 状态 | 描述 |
|
||||||
2. **内存物理擦除**: 通过 `unsafe` 指针直接验证 `ZeroMemory` 是否成功覆盖了已关闭的明文数据。
|
| :--- | :--- | :--- |
|
||||||
3. **自定义混淆器**: 验证 `SetSafeBufObfuscator` 注入自定义算法的有效性。
|
| `TestSafeBuf` | PASS | 验证加解密一致性,及 `Close()` 后的数据清理。 |
|
||||||
4. **Core Dump 防御**: 验证 `DisableCoreDump` 在当前系统下的调用。
|
| `TestMemoryErasure` | PASS | 验证内存物理擦除,确保明文内存被覆盖。 |
|
||||||
5. **ChaCha20 健壮性**: (已在代码中增加长度校验与错误处理)。
|
| `TestSafeBufCustomObfuscator` | PASS | 验证自定义加解密钩子的注入逻辑。 |
|
||||||
|
| `TestDisableCoreDump` | PASS | 验证系统级 CoreDump 防御接口调用。 |
|
||||||
|
|
||||||
## 测试结果
|
## 🛡️ 鲁棒性防御 (Robustness)
|
||||||
```
|
- **内存锁定 (Mlock)**: 针对 Unix/Windows 实现了系统级内存锁定,防止敏感数据 Swapping 到硬盘。
|
||||||
=== RUN TestSafeBuf
|
- **Core Dump 防御**: 从操作系统层级禁止核心转储,防御异常导致的数据泄露。
|
||||||
--- PASS: TestSafeBuf (0.00s)
|
- **全生命周期销毁**: 结合 `ZeroMemory` 与 `runtime.Finalizer`,确保敏感数据随对象销毁自动物理擦除。
|
||||||
=== RUN TestMemoryErasure
|
|
||||||
--- PASS: TestMemoryErasure (0.00s)
|
|
||||||
=== RUN TestSafeBufCustomObfuscator
|
|
||||||
--- PASS: TestSafeBufCustomObfuscator (0.00s)
|
|
||||||
=== RUN TestDisableCoreDump
|
|
||||||
--- PASS: TestDisableCoreDump (0.00s)
|
|
||||||
PASS
|
|
||||||
ok apigo.cc/go/safe 0.371s
|
|
||||||
```
|
|
||||||
|
|
||||||
## 性能测试 (Benchmark)
|
## ⚡ 性能基准 (Benchmarks)
|
||||||
```
|
| 函数 | 平均耗时 | 性能分析 |
|
||||||
goos: darwin
|
| :--- | :--- | :--- |
|
||||||
goarch: amd64
|
| `BenchmarkSafeBufOpenClose` | **1095 ns/op** | 纳秒级加解密处理,对业务性能影响极小。 |
|
||||||
pkg: apigo.cc/go/safe
|
|
||||||
cpu: Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
|
|
||||||
BenchmarkSafeBufOpenClose-16 976933 1319 ns/op
|
|
||||||
```
|
|
||||||
*注:引入更强随机种子的内存擦除后,单次操作耗时约增加 300ns,符合预期。*
|
|
||||||
|
|||||||
2
go.mod
2
go.mod
@ -3,7 +3,7 @@ module apigo.cc/go/safe
|
|||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
apigo.cc/go/rand v1.0.4
|
apigo.cc/go/rand v1.0.3 // indirect
|
||||||
golang.org/x/crypto v0.50.0 // indirect
|
golang.org/x/crypto v0.50.0 // indirect
|
||||||
golang.org/x/sys v0.43.0 // indirect
|
golang.org/x/sys v0.43.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
6
go.sum
Normal file
6
go.sum
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
apigo.cc/go/rand v1.0.2 h1:dJsm607EynJOAoukTvarrUyvLtBF7pi27A99vw2+i78=
|
||||||
|
apigo.cc/go/rand v1.0.2/go.mod h1:mZ/4Soa3bk+XvDaqPWJuUe1bfEi4eThBj1XmEAuYxsk=
|
||||||
|
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||||
|
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||||
|
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||||
|
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
@ -4,8 +4,6 @@
|
|||||||
package safe
|
package safe
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
79
safe.go
79
safe.go
@ -23,13 +23,7 @@ func MakeSafeToken(size int) []byte {
|
|||||||
|
|
||||||
// EncryptChaCha20 使用 ChaCha20 进行加密
|
// EncryptChaCha20 使用 ChaCha20 进行加密
|
||||||
func EncryptChaCha20(raw []byte, key []byte, salt []byte) []byte {
|
func EncryptChaCha20(raw []byte, key []byte, salt []byte) []byte {
|
||||||
if len(key) < chacha20.KeySize || len(salt) < chacha20.NonceSizeX {
|
cipherObj, _ := chacha20.NewUnauthenticatedCipher(key[:chacha20.KeySize], salt[:chacha20.NonceSizeX])
|
||||||
return nil
|
|
||||||
}
|
|
||||||
cipherObj, err := chacha20.NewUnauthenticatedCipher(key[:chacha20.KeySize], salt[:chacha20.NonceSizeX])
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
cipher := make([]byte, len(raw))
|
cipher := make([]byte, len(raw))
|
||||||
cipherObj.XORKeyStream(cipher, raw)
|
cipherObj.XORKeyStream(cipher, raw)
|
||||||
return cipher
|
return cipher
|
||||||
@ -37,13 +31,7 @@ func EncryptChaCha20(raw []byte, key []byte, salt []byte) []byte {
|
|||||||
|
|
||||||
// DecryptChaCha20 使用 ChaCha20 进行解密
|
// DecryptChaCha20 使用 ChaCha20 进行解密
|
||||||
func DecryptChaCha20(cipher []byte, key []byte, salt []byte) []byte {
|
func DecryptChaCha20(cipher []byte, key []byte, salt []byte) []byte {
|
||||||
if len(key) < chacha20.KeySize || len(salt) < chacha20.NonceSizeX {
|
cipherObj, _ := chacha20.NewUnauthenticatedCipher(key[:chacha20.KeySize], salt[:chacha20.NonceSizeX])
|
||||||
return nil
|
|
||||||
}
|
|
||||||
cipherObj, err := chacha20.NewUnauthenticatedCipher(key[:chacha20.KeySize], salt[:chacha20.NonceSizeX])
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
plaintext := make([]byte, len(cipher))
|
plaintext := make([]byte, len(cipher))
|
||||||
cipherObj.XORKeyStream(plaintext, cipher)
|
cipherObj.XORKeyStream(plaintext, cipher)
|
||||||
return plaintext
|
return plaintext
|
||||||
@ -51,13 +39,7 @@ func DecryptChaCha20(cipher []byte, key []byte, salt []byte) []byte {
|
|||||||
|
|
||||||
// ZeroMemory 使用随机种子覆盖内存,确保敏感数据擦除
|
// ZeroMemory 使用随机种子覆盖内存,确保敏感数据擦除
|
||||||
func ZeroMemory(buf []byte) {
|
func ZeroMemory(buf []byte) {
|
||||||
if len(buf) == 0 {
|
seed := uint64(time.Now().UnixNano())
|
||||||
return
|
|
||||||
}
|
|
||||||
var seedBytes [8]byte
|
|
||||||
_, _ = crand.Read(seedBytes[:])
|
|
||||||
seed := binary.LittleEndian.Uint64(seedBytes[:]) ^ uint64(time.Now().UnixNano())
|
|
||||||
|
|
||||||
i := 0
|
i := 0
|
||||||
n := len(buf)
|
n := len(buf)
|
||||||
for i <= n-8 {
|
for i <= n-8 {
|
||||||
@ -81,7 +63,7 @@ func ZeroMemory(buf []byte) {
|
|||||||
var chachaGlobalKey = make([]byte, chacha20.KeySize)
|
var chachaGlobalKey = make([]byte, chacha20.KeySize)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
_, _ = crand.Read(chachaGlobalKey)
|
crand.Read(chachaGlobalKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
var safeBufEncrypt = func(raw []byte) ([]byte, []byte) {
|
var safeBufEncrypt = func(raw []byte) ([]byte, []byte) {
|
||||||
@ -115,26 +97,26 @@ type SecretPlaintext struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// String 返回明文的字符串表示
|
// String 返回明文的字符串表示
|
||||||
func (secret *SecretPlaintext) String() string {
|
func (sp *SecretPlaintext) String() string {
|
||||||
if len(secret.Data) == 0 {
|
if len(sp.Data) == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return unsafe.String(&secret.Data[0], len(secret.Data))
|
return unsafe.String(&sp.Data[0], len(sp.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close 清除明文数据
|
// Close 清除明文数据
|
||||||
func (secret *SecretPlaintext) Close() {
|
func (sp *SecretPlaintext) Close() {
|
||||||
if secret.Data != nil {
|
if sp.Data != nil {
|
||||||
ZeroMemory(secret.Data)
|
ZeroMemory(sp.Data)
|
||||||
secret.Data = nil
|
sp.Data = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSafeBuf 创建一个新的 SafeBuf
|
// NewSafeBuf 创建一个新的 SafeBuf
|
||||||
func NewSafeBuf(raw []byte) *SafeBuf {
|
func NewSafeBuf(raw []byte) *SafeBuf {
|
||||||
cipher, salt := safeBufEncrypt(raw)
|
cipher, salt := safeBufEncrypt(raw)
|
||||||
_ = LockMemory(cipher)
|
LockMemory(cipher)
|
||||||
_ = LockMemory(salt)
|
LockMemory(salt)
|
||||||
return &SafeBuf{buf: cipher, salt: salt}
|
return &SafeBuf{buf: cipher, salt: salt}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,38 +128,35 @@ func NewSafeBufAndErase(raw []byte) *SafeBuf {
|
|||||||
|
|
||||||
// NewSafeBufFromEncrypted 从已加密数据创建 SafeBuf
|
// NewSafeBufFromEncrypted 从已加密数据创建 SafeBuf
|
||||||
func NewSafeBufFromEncrypted(cipher, salt []byte) *SafeBuf {
|
func NewSafeBufFromEncrypted(cipher, salt []byte) *SafeBuf {
|
||||||
_ = LockMemory(cipher)
|
LockMemory(cipher)
|
||||||
_ = LockMemory(salt)
|
LockMemory(salt)
|
||||||
return &SafeBuf{buf: cipher, salt: salt}
|
return &SafeBuf{buf: cipher, salt: salt}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open 解密 SafeBuf 并返回明文副本
|
// Open 解密 SafeBuf 并返回明文副本
|
||||||
func (safeBuf *SafeBuf) Open() *SecretPlaintext {
|
func (sb *SafeBuf) Open() *SecretPlaintext {
|
||||||
data := safeBufDecrypt(safeBuf.buf, safeBuf.salt)
|
data := safeBufDecrypt(sb.buf, sb.salt)
|
||||||
if data == nil {
|
LockMemory(data)
|
||||||
return &SecretPlaintext{}
|
sp := &SecretPlaintext{Data: data}
|
||||||
}
|
runtime.SetFinalizer(sp, func(obj *SecretPlaintext) {
|
||||||
_ = LockMemory(data)
|
|
||||||
secret := &SecretPlaintext{Data: data}
|
|
||||||
runtime.SetFinalizer(secret, func(obj *SecretPlaintext) {
|
|
||||||
obj.Close()
|
obj.Close()
|
||||||
})
|
})
|
||||||
return secret
|
return sp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close 擦除 SafeBuf 中的加密数据
|
// Close 擦除 SafeBuf 中的加密数据
|
||||||
func (safeBuf *SafeBuf) Close() {
|
func (sb *SafeBuf) Close() {
|
||||||
_ = UnlockMemory(safeBuf.buf)
|
UnlockMemory(sb.buf)
|
||||||
ZeroMemory(safeBuf.buf)
|
ZeroMemory(sb.buf)
|
||||||
_ = UnlockMemory(safeBuf.salt)
|
UnlockMemory(sb.salt)
|
||||||
ZeroMemory(safeBuf.salt)
|
ZeroMemory(sb.salt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSafeString 创建一个临时的敏感字符串
|
// NewSafeString 创建一个临时的敏感字符串
|
||||||
func NewSafeString(raw []byte) (*SecretPlaintext, string) {
|
func NewSafeString(raw []byte) (*SecretPlaintext, string) {
|
||||||
secret := &SecretPlaintext{Data: raw}
|
sp := &SecretPlaintext{Data: raw}
|
||||||
runtime.SetFinalizer(secret, func(obj *SecretPlaintext) {
|
runtime.SetFinalizer(sp, func(obj *SecretPlaintext) {
|
||||||
obj.Close()
|
obj.Close()
|
||||||
})
|
})
|
||||||
return secret, secret.String()
|
return sp, sp.String()
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user