首页 / 文章

V Vanity
先玩一下
这篇讲标识与台账的设计;想亲手把一条 AIGC 元数据写进 PNG、再把它改坏,打开 AIGC 标识实验。浏览器本地计算,不上传文件。

1问题:重编码之后靠什么证明

AI 生成内容越来越多,但“这张图是不是 AI 生成的”不能只靠平台自报。隐式标识要解决的是:文件离开生成方之后,还能不能读出谁生成的、哪一条内容、完整性有没有被动过。

难点在承载位。图片上传到社交平台会被重编码,音频会被转码,PDF 可能被重新导出;写进文件里的标识不保证存活。所以这个项目做两层:

2七字段与完整性码

标识的字段名与取值来自 GB 45438-2025 的二手公开文档(标准正文不可机器抓取,项目里明确标注了这一点)。核心七字段:

字段含义
Label1 = 生成方;2/3 只对传播平台开放
ContentProducer内容生成方
ProduceID内容 ID
ReservedCode1完整性码:SHA256(producer|produceID)[:16]
ContentPropagator传播方(可选)
PropagateID传播 ID(可选)
ReservedCode2预留(可选)

完整性码只覆盖生成方与内容 ID 两个字段,不覆盖整个文件;它回答的是“字段有没有被改”,不是“文件有没有被改”。文件指纹由台账侧的 SHA-256 负责。这两个检查是分开的:改字段会先撞完整性码,改文件字节会先撞台账指纹。

还有一个约束:生成方只能写 Label=1。2/3 档只对传播平台开放;生成侧写 2 或 3 等于给自己留一条“疑似也能盖章”的后门。

38 个承载位与“不重编码像素”

项目按文件类型分承载位:PNG、JPEG、MP3/音频、PDF、Office 等,共 8 个。PNG 的实现是往 iTXt 块写一条 AIGC: 开头的 JSON,插在 IHDR 之后,其余块字节不动——不重编码像素,所以写标识不会让图片画质变化。

PNG 签名
IHDR
iTXt  ← keyword = "AIGC\0",文本 = AIGC:{...七字段...}
IDAT  ← 像素数据,一个字节不动
IEND

写完之后立刻读回,逐字段比对;不一致就记 written_unverified,而不是把可疑产物静默丢掉。不同承载位的字节保真能力不一样,库重写文件的承载位会带 non_byte_exact 风险标记——这是“写入成功”与“字节保真”两件事,不能混着说。

4只追加哈希链台账

台账的每条记录都包含上一条的 entry_hash,当前条用 seq|prev_hash|字段串 重新算 SHA-256。改中间一条,重算时会在那一条断开;删中间一条也能定位。可识别主体只以 HMAC(pepper, 值) 的摘要进链,删掉 pepper 就等于让这些摘要再也反查不回主体——这是保留期处置能落地、同时链又不允许 UPDATE 的原因。

body = "|".join("" if row.get(f) is None else str(row[f]) for f in CHAIN_FIELDS)
canonical = f"{seq}|{row['prev_hash']}|{body}"
entry_hash = hashlib.sha256(canonical.encode()).hexdigest()

链的边界要写清楚:哈希链查不出删掉链尾最后几条,因为链里没有“应当共 N 条”的外部锚点。删中间一条能准确定位;删尾不行。这不是实现缺陷,是只追加链在没有外部见证时的固有限制,项目把它写进了 README,而不是藏起来。

5实测、边界与合规口径

170
passed(2026-10-24 复跑)
14s
本机全套墙钟
8
承载位
6/24
平台重编码后 in-band 存活

能跑通的是本地闭环:写标识 → 立刻读回 → 进链 → 只读核验 → 批量 zip → 三张演示页;demo_roundtrip.py 走真实 HTTP,14 条判据覆盖无 key 被拒、落标回读一致、篡改检出、zip-slip 拒收、失败行不进链、全量重算链。

明确不做的/做不到的:

合规边界也写死在项目里:只做“写标识、核验、留存”,不剥标识、不去水印、不清洗元数据、不做绕过平台审核的接口。第十条连“为此提供工具”一起禁止;这不是能力问题,是不做。

这个项目真正有意思的地方是把“证明”拆成两层:文件内的七字段回答“这条内容是谁生成的”,文件外的哈希链回答“这条记录有没有被动过”。两层都只能证明各自那一段,不能互相冒充。

想亲手写一条再把它改坏,去 AIGC 标识实验;完整边界在项目的 README 与 docs/retention.md。