AI Gateway Go 0.10.0 源码快照 + 旗舰版需求规划报告
M0-M7 已完成:核心网关(身份/RBAC/TOTP/OIDC/SAML/Provider/配额/路由/内容策略/审计/定价)+ 资源市场(MCP/Skills/数字员工)。 含 22 个 PostgreSQL 迁移、管理端/门户端前端源码、OpenAPI 契约、部署 compose。 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
package cryptox
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Cipher interface {
|
||||
Encrypt([]byte) ([]byte, int, error)
|
||||
Decrypt([]byte, int) ([]byte, error)
|
||||
}
|
||||
|
||||
type Keyring struct {
|
||||
activeVersion int
|
||||
ciphers map[int]*AESGCM
|
||||
}
|
||||
|
||||
func NewKeyring(activeKey string, activeVersion int, encodedKeyring, purpose string) (*Keyring, error) {
|
||||
if activeVersion < 1 || purpose == "" {
|
||||
return nil, errors.New("encryption version and purpose are required")
|
||||
}
|
||||
encoded := make(map[int]string)
|
||||
if strings.TrimSpace(encodedKeyring) != "" {
|
||||
var values map[string]string
|
||||
if err := json.Unmarshal([]byte(encodedKeyring), &values); err != nil {
|
||||
return nil, fmt.Errorf("encryption keyring must be a JSON object: %w", err)
|
||||
}
|
||||
for rawVersion, key := range values {
|
||||
version, err := strconv.Atoi(rawVersion)
|
||||
if err != nil || version < 1 || strings.TrimSpace(key) == "" {
|
||||
return nil, fmt.Errorf("invalid encryption keyring version %q", rawVersion)
|
||||
}
|
||||
encoded[version] = strings.TrimSpace(key)
|
||||
}
|
||||
}
|
||||
if strings.TrimSpace(activeKey) != "" {
|
||||
if existing, ok := encoded[activeVersion]; ok && existing != strings.TrimSpace(activeKey) {
|
||||
return nil, fmt.Errorf("active encryption key version %d is configured twice with different values", activeVersion)
|
||||
}
|
||||
encoded[activeVersion] = strings.TrimSpace(activeKey)
|
||||
}
|
||||
if len(encoded) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if _, ok := encoded[activeVersion]; !ok {
|
||||
return nil, fmt.Errorf("active encryption key version %d is not loaded", activeVersion)
|
||||
}
|
||||
keyring := &Keyring{activeVersion: activeVersion, ciphers: make(map[int]*AESGCM, len(encoded))}
|
||||
for version, key := range encoded {
|
||||
cipher, err := NewAESGCM(key, version, purpose)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("encryption key version %d: %w", version, err)
|
||||
}
|
||||
keyring.ciphers[version] = cipher
|
||||
}
|
||||
return keyring, nil
|
||||
}
|
||||
|
||||
func (k *Keyring) Encrypt(plaintext []byte) ([]byte, int, error) {
|
||||
if k == nil {
|
||||
return nil, 0, ErrKeyUnavailable
|
||||
}
|
||||
return k.ciphers[k.activeVersion].Encrypt(plaintext)
|
||||
}
|
||||
|
||||
func (k *Keyring) Decrypt(encrypted []byte, version int) ([]byte, error) {
|
||||
if k == nil {
|
||||
return nil, ErrKeyUnavailable
|
||||
}
|
||||
cipher, ok := k.ciphers[version]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("encryption key version %d is not loaded", version)
|
||||
}
|
||||
return cipher.Decrypt(encrypted, version)
|
||||
}
|
||||
|
||||
func (k *Keyring) ActiveVersion() int {
|
||||
if k == nil {
|
||||
return 0
|
||||
}
|
||||
return k.activeVersion
|
||||
}
|
||||
|
||||
func (k *Keyring) Versions() []int {
|
||||
if k == nil {
|
||||
return nil
|
||||
}
|
||||
versions := make([]int, 0, len(k.ciphers))
|
||||
for version := range k.ciphers {
|
||||
versions = append(versions, version)
|
||||
}
|
||||
sort.Ints(versions)
|
||||
return versions
|
||||
}
|
||||
|
||||
var _ Cipher = (*Keyring)(nil)
|
||||
Reference in New Issue
Block a user