secretx

package
v0.34.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 4, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package secretx 提供静态敏感凭证的信封加密原语(AES-GCM + 密文版本前缀 + 主密钥解析):把一段敏感字符串(云账号 SecretKey、LDAP bind 密码、渠道 token、git provider token 等)用主密钥封起来存库——此前每个服务各自手搓 AES,密钥/算法语义漂移出四个形态(CFB 无认证、缺钥明文透传、密文无版本位), 本包把它们收成一份正确默认(#177)。

密文格式(与 domain-manage 手搓形态兼容,存量数据零迁移):

"v1:" + base64( nonce(12B) ‖ ciphertext+tag )

设计立场:

  • **必须带认证**(GCM),CFB 这类无完整性保护的原语不在可选项里;
  • **缺钥 fail-fast**:构造期校验,不提供「缺钥明文透传」降级——那是 octopus 手搓版的真实事故形态(漏到生产即裸奔);本地开发想绕过请 自行用恒等实现,不该由库默认承担;
  • **密文版本前缀**是主密钥轮换的识别位:Open 遇到未识别前缀返回哨兵 ErrUnsupportedVersion,下游可按前缀选钥解密重封(v2 落地时零破坏);
  • **错误语义统一**:解密失败(密钥不匹配 vs 密文损坏)一律 ErrBadCiphertext ——不向日志/调用方泄露「哪条密文是用哪把钥封的」这类信息。

典型用法:

key, err := secretx.ParseKey(os.Getenv("APP_MASTER_KEY")) // base64(16/24/32B)
if err != nil { panic(err) }                             // 构造期 fail-fast
box, err := secretx.NewBox(key)
if err != nil { panic(err) }

sealed, err := box.Seal([]byte(plaintext))     // "v1:xxxx" 直接入库
pt, err := box.Open(sealed)                    // 读出解封

// 可选:绑定行 id/用途做 AAD,防密文在行间挪用(换行 AAD 解封必失败)
sealed, err := box.SealWithAAD(pt, []byte("credential:"+rowID))
pt, err := box.OpenWithAAD(sealed, []byte("credential:"+rowID))

Index

Constants

View Source
const VersionV1 = "v1"

VersionV1 当前密文版本前缀("v1:")。与 domain-manage 手搓形态逐字节兼容, 存量数据零迁移。

Variables

View Source
var (
	// ErrNoKey 主密钥缺失(空串/空切片)。构造期 fail-fast,不存在「缺钥
	// 明文透传」的降级路径——本地开发需要绕过时请自行用恒等实现。
	ErrNoKey = errors.New("secretx: master key missing")
	// ErrBadKeySize 主密钥长度不在 16/24/32 字节(AES-128/192/256)范围内。
	ErrBadKeySize = errors.New("secretx: master key size must be 16, 24 or 32 bytes")
	// ErrBadCiphertext 密文损坏或主密钥不匹配(GCM 认证失败)。两种情形
	// 统一表述,不泄露细节——「哪条密文是用哪把钥封的」不该可被日志反推。
	ErrBadCiphertext = errors.New("secretx: bad ciphertext")
	// ErrUnsupportedVersion 密文版本前缀未识别(主密钥轮换识别位:v2 密钥
	// 落地后,下游可按前缀选钥解密重封)。
	ErrUnsupportedVersion = errors.New("secretx: unsupported ciphertext version")
)

哨兵错误(errors.Is 判定)。

Functions

func ParseKey

func ParseKey(encoded string) ([]byte, error)

ParseKey 解析 base64 编码的主密钥:解码 + 16/24/32 字节校验,错误带实际 长度上下文。供「主密钥从环境变量/配置文件进来」的消费方一行完成入口校验。

Types

type Box

type Box struct {
	// contains filtered or unexported fields
}

Box 信封盒:主密钥不可变,可并发使用(aead 无状态)。

func NewBox

func NewBox(masterKey []byte) (*Box, error)

NewBox 以主密钥创建信封盒。缺钥返回 ErrNoKey、长度非法返回 ErrBadKeySize (构造期 fail-fast——加密原语没有「带病运行」的合法形态)。

func (*Box) Open

func (b *Box) Open(ciphertext string) ([]byte, error)

Open 解封 Seal 的产物。

func (*Box) OpenWithAAD

func (b *Box) OpenWithAAD(ciphertext string, aad []byte) ([]byte, error)

OpenWithAAD 解封带 AAD 的密文(AAD 必须与 SealWithAAD 时逐字节一致)。

错误语义:版本前缀缺失/未识别 → ErrUnsupportedVersion(轮换识别位); base64 非法、负载过短、GCM 认证失败 → 一律 ErrBadCiphertext(统一表述)。

func (*Box) Seal

func (b *Box) Seal(plaintext []byte) (string, error)

Seal 封装明文:返回 "v1:" + base64(nonce‖ciphertext+tag)。随机 nonce 由 crypto/rand 生成(读取失败返回错误,绝不降级复用 nonce——GCM 复用 nonce 等于交出明文)。

func (*Box) SealWithAAD

func (b *Box) SealWithAAD(plaintext, aad []byte) (string, error)

SealWithAAD 同 Seal,额外绑定附加认证数据(行 id、用途标签等):AAD 不入 密文但参与认证——密文挪到别的行/别的用途上解封必失败。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL