cea9b941cf
Build and Push / build (push) Failing after 13m20s
Go 项目,包含: - 服务端 updater:两阶段协议,ECDSA 签名验证,AES-GCM 加密 - 发送端 dcu-send:Gitea Action CLI - internal/auth:加解密/签名/会话管理 - internal/docker:Docker CLI 容器查找/拉取/重建 - action/:Gitea Action 定义 - deploy/Dockerfile:多阶段构建 - .gitea/workflows/build.yaml:CI/CD
137 lines
2.8 KiB
Go
137 lines
2.8 KiB
Go
package auth
|
||
|
||
import (
|
||
"crypto/rand"
|
||
"fmt"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
// sessionEntry 存储一次 Phase 1 协商的会话密钥。
|
||
type sessionEntry struct {
|
||
key []byte // AES-256 会话密钥
|
||
expiry time.Time
|
||
}
|
||
|
||
// SessionManager 管理内存中的会话密钥(Nonce → AES Key)。
|
||
// 每个 Phase 1 请求生成一个 session,Phase 2 消费后删除。
|
||
type SessionManager struct {
|
||
mu sync.Mutex
|
||
store map[string]*sessionEntry
|
||
ttl time.Duration
|
||
}
|
||
|
||
// NewSessionManager 创建会话密钥管理器。
|
||
// ttl: 密钥过期时间(如 30 秒)
|
||
func NewSessionManager(ttl time.Duration) *SessionManager {
|
||
sm := &SessionManager{
|
||
store: make(map[string]*sessionEntry),
|
||
ttl: ttl,
|
||
}
|
||
// 后台协程定期清理过期密钥
|
||
go sm.cleanupLoop()
|
||
return sm
|
||
}
|
||
|
||
// GenerateKey 生成 AES-256 会话密钥,用指定的 keyID 存储(通常用 nonce)。
|
||
func (sm *SessionManager) GenerateKey(keyID string) ([]byte, error) {
|
||
key := make([]byte, 32)
|
||
if _, err := rand.Read(key); err != nil {
|
||
return nil, fmt.Errorf("generate session key: %w", err)
|
||
}
|
||
|
||
sm.mu.Lock()
|
||
sm.store[keyID] = &sessionEntry{
|
||
key: key,
|
||
expiry: time.Now().Add(sm.ttl),
|
||
}
|
||
sm.mu.Unlock()
|
||
|
||
return key, nil
|
||
}
|
||
|
||
// GetKey 获取并删除会话密钥(一次性使用)。
|
||
// 返回 nil 表示 keyID 不存在或已过期。
|
||
func (sm *SessionManager) GetKey(keyID string) []byte {
|
||
sm.mu.Lock()
|
||
defer sm.mu.Unlock()
|
||
|
||
entry, ok := sm.store[keyID]
|
||
if !ok {
|
||
return nil
|
||
}
|
||
|
||
delete(sm.store, keyID)
|
||
|
||
if time.Now().After(entry.expiry) {
|
||
return nil
|
||
}
|
||
|
||
return entry.key
|
||
}
|
||
|
||
// cleanupLoop 定期清理过期密钥。
|
||
func (sm *SessionManager) cleanupLoop() {
|
||
ticker := time.NewTicker(sm.ttl)
|
||
defer ticker.Stop()
|
||
|
||
for range ticker.C {
|
||
sm.mu.Lock()
|
||
now := time.Now()
|
||
for id, entry := range sm.store {
|
||
if now.After(entry.expiry) {
|
||
delete(sm.store, id)
|
||
}
|
||
}
|
||
sm.mu.Unlock()
|
||
}
|
||
}
|
||
|
||
// NonceCache 用于防重放攻击的 Nonce 缓存。
|
||
type NonceCache struct {
|
||
mu sync.Mutex
|
||
store map[string]time.Time
|
||
ttl time.Duration
|
||
}
|
||
|
||
// NewNonceCache 创建 Nonce 缓存。
|
||
// ttl: Nonce 有效时间(如 60 秒)
|
||
func NewNonceCache(ttl time.Duration) *NonceCache {
|
||
nc := &NonceCache{
|
||
store: make(map[string]time.Time),
|
||
ttl: ttl,
|
||
}
|
||
go nc.cleanupLoop()
|
||
return nc
|
||
}
|
||
|
||
// Check 检查并记录 Nonce。返回 true 表示 Nonce 有效(未使用过)。
|
||
// 返回 false 表示 Nonce 已存在(重放攻击)。
|
||
func (nc *NonceCache) Check(nonce string) bool {
|
||
nc.mu.Lock()
|
||
defer nc.mu.Unlock()
|
||
|
||
if _, exists := nc.store[nonce]; exists {
|
||
return false
|
||
}
|
||
|
||
nc.store[nonce] = time.Now().Add(nc.ttl)
|
||
return true
|
||
}
|
||
|
||
func (nc *NonceCache) cleanupLoop() {
|
||
ticker := time.NewTicker(nc.ttl)
|
||
defer ticker.Stop()
|
||
|
||
for range ticker.C {
|
||
nc.mu.Lock()
|
||
now := time.Now()
|
||
for n, expiry := range nc.store {
|
||
if now.After(expiry) {
|
||
delete(nc.store, n)
|
||
}
|
||
}
|
||
nc.mu.Unlock()
|
||
}
|
||
}
|