Documentation
¶
Overview ¶
Package auditchain 提供 append-only 审计哈希链的纯计算内核——防篡改审计 留痕(合规类产品经典需求)的可复用单源:canonical 串构造(\x1f 分隔、字段序 由宿主注入)、sha256 链哈希、创世锚、逐行重算 + 前后链接核对的 verify 走查 (首个断点即停)、时间归一(UTC + 微秒截断)。纯 stdlib,零存储假设—— gorm 咨询锁 / busyRetry / 追加事务等方言件留在宿主,本包不做任何 IO。
canonical 单源纪律 ¶
写入方(追加落库)与校验方(链走查)必须共享同一个 Chain——即同一个 CanonicalFunc 字段序定义:任何一方私改字段序,从改动点起的每一行哈希重算 都对不上,全链假断。这是包契约而不是文档倡议:Canonicalize 是唯一出口, 落库的行哈希与走查时的 Record.Canonical 都必须由同一实例产出。字段内若 可能出现 \x1f(业务文本不可信时),由宿主 canonical 先消毒——内核不做 猜测性转义,因为转义规则本身也是 canonical 的一部分,双方不一致即分叉。
时间归一是读回一致的前提 ¶
NormalizeTs = UTC + 微秒截断。PG timestamptz 精度上限微秒:纳秒入哈希, 任何方言读回(微秒精度)重算必假断。写入方入链前必须归一——它是双方言 读回一致的前提,不是可选优化。
追加(宿主侧示意,方言件留宿主) ¶
chain, err := auditchain.NewChain(func(row auditRow) []string {
return []string{row.ID, row.Ts, row.Actor, row.Action, row.PrevHash}
})
// 追加事务内(宿主方言件:咨询锁串行化 → 读链尾 → INSERT → 回填哈希):
row.PrevHash = lastHash // 空表取 auditchain.GenesisHash
row.Ts = auditchain.NormalizeTs(now)
row.Hash = chain.HashRow(row)
走查 ¶
res, err := auditchain.Verify(src) // src 为 RecordSource 行迭代器(DB 分批扫描适配) // 锚定语义三档: // 缺省:首行必须衔接创世锚 GenesisHash(未清扫链头的部署 = 完整证明); // WithAnchor(h):首行衔接锚行哈希(从链中段起查); // WithUnanchoredHead():保留清扫截断链头的部署,首行只查自洽——头行 // 截断本身不可由本地链证明(防截断靠外发 SIEM 侧对账)。 // 首个断点即停:OK=false + FirstBrokenID + Reason(后续行 PrevHash 全部 // 对不上,多报无益)。
Index ¶
Constants ¶
const GenesisHash = "0000000000000000000000000000000000000000000000000000000000000000"
GenesisHash 创世链锚:首行(无前链可衔接)的 PrevHash 取值——64 个 '0' (sha256 hex 长度)。空表追加首行时用它,链走查缺省要求首行衔接它。
const Separator = "\x1f"
Separator canonical 字段分隔符(ASCII \x1f Unit Separator):业务可打印 字段里几乎不出现,天然降低拼接歧义。字段内可能出现 \x1f 时由宿主 canonical 先消毒——转义规则本身也是 canonical 的一部分,必须写入方/校验方一致。
Variables ¶
This section is empty.
Functions ¶
Types ¶
type CanonicalFunc ¶
CanonicalFunc 行 → canonical 字段序(宿主单源)。字段序由宿主定义——本包 不落成任何具体业务行结构专用;写入方与校验方必须共享同一函数值,任何一方 私改字段序即全链假断。
type Chain ¶
type Chain[T any] struct { // contains filtered or unexported fields }
Chain 审计哈希链内核(泛型):把「字段序定义」钉成一个值,写入方与校验方 共享同一 *Chain 实例即共享同一 canonical。零可变状态,可并发使用。
func NewChain ¶
func NewChain[T any](canonical CanonicalFunc[T]) (*Chain[T], error)
NewChain 构造链内核。canonical 为 nil 是构造期非法值 → fail-fast 报错 (没有字段序定义的链没有语义,不能静默兜底)。
func (*Chain[T]) Canonicalize ¶
Canonicalize 行 → canonical 串(\x1f 连接宿主字段序)。写入方(算哈希前) 与校验方(重算哈希前)都必须经由本方法产出——canonical 单源纪律的执行点。
type Record ¶
type Record struct {
// ID 行标识(宿主主键字符串化;断点报告原样透出)。
ID string
// Canonical 行 canonical 串(共享 Chain.Canonicalize 的产物)。
Canonical string
// PrevHash 落库的前链哈希(首行 = GenesisHash 或锚行哈希)。
PrevHash string
// Hash 落库的本行链哈希。
Hash string
}
Record 链校验记录面:宿主把存储行适配为内核所需的最小事实四元组。 Canonical 必须由与写入方共享的同一 *Chain.Canonicalize 产出(单源纪律)。
type RecordSource ¶
type RecordSource interface {
// Next 返回下一行;遍历结束返回 io.EOF。非 EOF 错误视为扫描故障,
// Verify 原样上抛(与「链断」严格区分:前者走查不了,后者是数据事实)。
Next() (Record, error)
}
RecordSource 链校验行源(行迭代器接口):宿主把 DB 分批扫描 / 内存切片 / SIEM 流适配成逐行拉取,行序必须按链序(写入序)。大表分批由宿主在 Next 内部完成,内核不做一次性载入。
func SliceSource ¶
func SliceSource(records []Record) RecordSource
SliceSource 内存切片行源(测试 / 小型链 / SIEM 已收批数据用),按序逐行 吐出,结束返回 io.EOF。
type VerifyOption ¶
type VerifyOption func(*verifyConfig)
VerifyOption 校验走查选项(命名循仓规:XxxOption,禁裸 Option)。
func WithAnchor ¶
func WithAnchor(anchorHash string) VerifyOption
WithAnchor 从锚行起走查:首行 PrevHash 必须衔接 anchorHash(锚行哈希)。 用于从链中段起查——锚行即起点前一行,锚行自身有效性由其所在链段的走查 负责。锚行为空串等非法锚没有静默兜底(调用方应显式改用缺省或 WithUnanchoredHead 表达意图)。
func WithUnanchoredHead ¶
func WithUnanchoredHead() VerifyOption
WithUnanchoredHead 无锚头走查:首行只查自洽不查前链。保留期清扫截断链头的 部署用——头行截断本身不可由本地链证明(防截断靠外发 SIEM 侧对账),从最早 存活行起查时首行只查自洽,其后行仍逐一核对前后链接。
type VerifyResult ¶
type VerifyResult struct {
// OK 全部已核行哈希与前后链接均通过。
OK bool `json:"ok"`
// Checked 已核对行数(断点即停时 = 断点所在位置)。
Checked int64 `json:"checked"`
// FirstBrokenID 首个断点行标识(宿主主键字符串化;OK=true 时为空)。
FirstBrokenID string `json:"first_broken_id,omitempty"`
// Reason 断点原因(行哈希不匹配 / 链衔接断裂,带期望与实际值短哈希)。
Reason string `json:"reason,omitempty"`
}
VerifyResult 链校验结论(首个断点即停——后续行 PrevHash 全部对不上,多报 无益)。json 标签供宿主管理面直接透出。
func Verify ¶
func Verify(src RecordSource, opts ...VerifyOption) (VerifyResult, error)
Verify 逐行重算哈希并核对前后链接,首个断点即停。锚定语义三档:
- 缺省(无选项):首行必须衔接创世锚 GenesisHash——未清扫链头的部署 即完整证明;
- WithAnchor(h):首行衔接锚行哈希(链中段起查);
- WithUnanchoredHead():首行只查自洽(保留清扫截断链头的部署)。
返回值语义:(res, nil) 表示走查正常完成——链断是数据事实,走 res.OK / FirstBrokenID / Reason 报告,不作 error;(res, err) 仅表示行源扫描故障 (非 io.EOF),res.Checked 带已核行数,链完整性此时未知,调用方不得把 res.OK 当作通过。