1问题:重编码之后靠什么证明
AI 生成内容越来越多,但“这张图是不是 AI 生成的”不能只靠平台自报。隐式标识要解决的是:文件离开生成方之后,还能不能读出谁生成的、哪一条内容、完整性有没有被动过。
难点在承载位。图片上传到社交平台会被重编码,音频会被转码,PDF 可能被重新导出;写进文件里的标识不保证存活。所以这个项目做两层:
- 文件内:往 8 个承载位写七字段隐式标识,写完立刻读回逐字段比对。
- 文件外:把每次落标/核验记进一条只追加的哈希链台账,用文件指纹和感知哈希召回“同一张图被重编码后的版本”。
2七字段与完整性码
标识的字段名与取值来自 GB 45438-2025 的二手公开文档(标准正文不可机器抓取,项目里明确标注了这一点)。核心七字段:
| 字段 | 含义 |
|---|---|
Label | 1 = 生成方;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实测、边界与合规口径
能跑通的是本地闭环:写标识 → 立刻读回 → 进链 → 只读核验 → 批量 zip → 三张演示页;demo_roundtrip.py 走真实 HTTP,14 条判据覆盖无 key 被拒、落标回读一致、篡改检出、zip-slip 拒收、失败行不进链、全量重算链。
明确不做的/做不到的:
- 标识不承诺上传后仍可验。 平台重编码后 in-band 实测存活 6/24(只有 GIF 扛住 Pillow 重存);所以上传场景靠台账侧链 + 文件指纹 + pHash 召回(72/72 在阈值内)。
- 限流是进程内的。 一个 worker 一份账,多副本要换 Redis 或网关层限流。
- 容器没 build 过。 本机没有 Docker 守护进程,部署文档里明确写了“容器命令未经 build 验证”;跑通的是 uvicorn 直启路径。
- MP4 一期不做。 承载位读写靠第三方库,回答的是“写不写得进、读不读得出”,不对真实世界全兼容。
- 标准正文未逐字复核。 键名与取值来自二手公开文档;任何对外声明前需以正版正文复核。
合规边界也写死在项目里:只做“写标识、核验、留存”,不剥标识、不去水印、不清洗元数据、不做绕过平台审核的接口。第十条连“为此提供工具”一起禁止;这不是能力问题,是不做。
这个项目真正有意思的地方是把“证明”拆成两层:文件内的七字段回答“这条内容是谁生成的”,文件外的哈希链回答“这条记录有没有被动过”。两层都只能证明各自那一段,不能互相冒充。
想亲手写一条再把它改坏,去 AIGC 标识实验;完整边界在项目的 README 与 docs/retention.md。