auditchain

package
v0.49.0 Latest Latest
Warning

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

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

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

View Source
const GenesisHash = "0000000000000000000000000000000000000000000000000000000000000000"

GenesisHash 创世链锚:首行(无前链可衔接)的 PrevHash 取值——64 个 '0' (sha256 hex 长度)。空表追加首行时用它,链走查缺省要求首行衔接它。

View Source
const Separator = "\x1f"

Separator canonical 字段分隔符(ASCII \x1f Unit Separator):业务可打印 字段里几乎不出现,天然降低拼接歧义。字段内可能出现 \x1f 时由宿主 canonical 先消毒——转义规则本身也是 canonical 的一部分,必须写入方/校验方一致。

Variables

This section is empty.

Functions

func Hash

func Hash(canonical string) string

Hash canonical 串 → 链哈希(sha256 hex,64 字符小写)。与行类型无关—— 校验侧对 Record.Canonical 重算哈希也走本函数。

func NormalizeTs

func NormalizeTs(t time.Time) time.Time

NormalizeTs 入链时间归一:UTC + 微秒截断。PG timestamptz 精度上限微秒—— 纳秒入哈希,任何方言读回(微秒精度)重算必假断。写入方入链前必须归一; 这是双方言读回一致的前提,不是可选优化。

Types

type CanonicalFunc

type CanonicalFunc[T any] func(row T) []string

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

func (c *Chain[T]) Canonicalize(row T) string

Canonicalize 行 → canonical 串(\x1f 连接宿主字段序)。写入方(算哈希前) 与校验方(重算哈希前)都必须经由本方法产出——canonical 单源纪律的执行点。

func (*Chain[T]) HashRow

func (c *Chain[T]) HashRow(row T) string

HashRow 行 → 链哈希(Canonicalize 后 sha256 hex)。追加侧:选定新行 PrevHash(链尾哈希或 GenesisHash)后,对本行调用本函数落库。

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 当作通过。

Jump to

Keyboard shortcuts

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