- 后端
- 内容协同
【免费下载链接】core
:cloud: ownCloud web server core (Files, DAV, etc.)
本篇技术指南以 ownCloud core 仓库中的 tests/data/integritycheck/golden/README.md 为核心,介绍完整性校验模块中“G2 代码签名验证器”的跨实现一致性契约——Golden 测试向量(golden test vectors)。通过本文,读者将理解规范清单(canonical manifest)为何必须与 Go 语言 ocsign 签名工具保持字节级一致、golden 向量的目录结构与两类文件的用途,以及 PHP 侧CanonicalManifest类如何以手写序列化方式逐字节复现 Go 签名器产生的 manifest 字节,并配合仓库中的测试用例掌握验证与自测方法。
一、Golden 测试向量的定位:跨语言实现的一致性契约
在 ownCloud 的代码签名体系中,签名器(signer)与验证器(verifier)分属两个语言生态:Go 编写的 ocsign 工具负责生成签名,PHP 实现的 ownCloud core 负责在运行时校验签名。由于 JSON 序列化在不同语言、不同库之间存在空格、转义、键排序等细微差异,同一份文件树在 Go 与 PHP 中序列化出的清单字节可能不一致,导致签名验证失败。
golden 目录正是为解决这一问题而存在的。正如 README.md 所声明,这些文件原样复制自 ocsign 仓库的testdata/golden目录,作为 G2 代码签名验证器(G2 code-signing verifier)的跨实现一致性契约:
- 每个
tree-*子目录内有两份文件:manifest.canonical.json:Go 签名器逐字节生成的规范清单(canonical manifest),是签名所覆盖的真实字节;hashes.expected.json:美化排版(pretty-printed)的人类可读检查工件,不作为测试目标。
- 规范化规则(canonicalization rules)定义在 Go ocsign 仓库的
internal/manifest/serialize.go中。
从仓库结构可以推断,golden 目录与 tests/lib/IntegrityCheck/Verifier/ 下的测试套件(CanonicalManifestTest.php、ManifestVerifierTest.php等)以及 tests/data/integritycheck/verifier/ 下的签名夹具共同构成完整的 G2 验证器测试矩阵。
二、Golden 向量的目录结构与文件用途
当前仓库中golden目录包含四个向量,分别针对不同的文件树形态:
| 目录 | 覆盖场景 |
|---|---|
tree-basic/ | 基础文件树(含隐藏文件.hidden-config、PHP/JS/模板等典型应用文件) |
tree-cruft/ | 包含多余/杂散文件的文件树(如未纳入清单的多余 JS 文件) |
tree-edge/ | 边界情况:空文件、点号文件名(a.b与a/b)、深层嵌套路径 |
tree-unicode/ | 多字节 UTF-8 文件名与路径(如café/menü.txt、日本語.txt) |
每个目录内的manifest.canonical.json都是一段紧凑无空格的 JSON 对象,键为文件相对路径、值为该文件的哈希值。例如 tree-basic/manifest.canonical.json:
{".hidden-config":"419d2b26aa9a7065a6a588e108587e631cf676c8366bb28fe5c4868c9290387b5cfcb3e9a08c220d852cabea5b58b66e12c53c65df2d16680ff6003172d18abf","appinfo/info.xml":"2fafbce4571514444b5edd26e4ff01e42ddf0e81aacc15fda63f304ff019cff260bd4e5625aac4ac9efe81cbf38086f920ff3b7ba264048b7d62185cbded402a","js/app.js":"b5a224757740965d14836f0fdbd774b10dc07acb2da3e996aedab31c28540d17307b4a1057e51ae82ebb6f775bab7d1d02283575800699983a7d91605791d81c","lib/Controller/Page.php":"3f06bdeed15d14aa0a56fd2725f3afadf81f4efda75a4e57e5898f4ff35cf8abaecb11c5f231c2e33a3db884a0e2243cc50f2a7fd2ead310c215138de14c74b9","templates/index.php":"977cd37b9abc841a2c00b50231a328c5bb9a21803d130b40b169202c53208f4e9d82daece262553401c3cc883b62b5a4e6ba938b479ab14ecc852d8b671e1bb4"}关键点在于:哈希值长度为 128 个十六进制字符,对应 SHA-512 摘要;而 tree-edge/manifest.canonical.json 中empty.txt的值cf83e135...927da3e正是 SHA-512 对空输入的已知摘要,可作为哈希算法的独立验证锚点。对比同目录下的hashes.expected.json(如 tree-basic/hashes.expected.json)可以看到:两者数据完全一致,区别仅在排版——expected 文件是方便人工审查的美化 JSON,而 canonical 文件才是签名所覆盖的字节,因此 README 强调 expected 文件不作为测试目标。
三、为何“禁止手工编辑”:字节级对齐是校验的意义所在
README 用醒目的**DO NOT hand-edit these files.**强调这些夹具必须与 Go ocsign 仓库中的来源逐字节相同(byte-for-byte identical)。原因在于:
- 签名覆盖的是字节,不是语义。签名方对
manifest.canonical.json的原始字节做哈希并签名;验证方也必须对完全相同的字节做哈希再验签。任何多余空格、换行、键序变化都会导致验签失败。 - golden 是“奇偶校验(parity)”的基准。PHP 侧实现的序列化若与 Go 侧有任何偏差,golden 对比测试就会立即失败,从而暴露不一致。
- 手工编辑会破坏这份契约,使 parity 测试失去意义,并可能掩盖真实的实现回归。
该约束在测试代码中体现为严格的assertSame断言(见下文第四节)。
四、PHP 侧实现:CanonicalManifest 的字节级复刻
golden 向量的对标实现位于 lib/private/IntegrityCheck/Verifier/CanonicalManifest.php。该类文档注释明确写道:“Reproduces the exact manifest bytes M that the Go ocsign tool signs. No I/O, no dependencies — pure utility class”,即一个无 I/O、无依赖的纯序列化工具类,目标是逐字节复现 Go 签名器产生的 manifest。
4.1 序列化规则(与 Goserialize.go对齐)
CanonicalManifest::serialize()的实现(CanonicalManifest.php)体现了完整的规范化规则:
- 键按原始字节序排序:
ksort($hashes, SORT_STRING),即按字节(bytewise)而非 locale 排序; - 紧凑 JSON,零空白:对间用
,分隔、键值间用:分隔,无任何空格、换行、尾部换行; - 键与值均需转义:
escapeString()逐字节处理(CanonicalManifest.php)。
escapeString()的转义表如下:
| 输入字节 | 输出 | 说明 |
|---|---|---|
"(0x22) | \" | 双引号转义 |
\(0x5C) | \\ | 反斜杠转义 |
| 0x08 | \b | 退格 |
| 0x09 | \t | 制表符 |
| 0x0A | \n | 换行 |
| 0x0C | \f | 换页 |
| 0x0D | \r | 回车 |
其余< 0x20的控制字节 | \uXXXX | 小写十六进制,如\u0001 |
其余字节(含/与 UTF-8 前导/延续字节) | 原样输出 | 不做转义 |
注意几个容易踩坑的细节:正斜杠/绝不转义(对应testForwardSlashNotEscaped),UTF-8 多字节字符以原始字节透传(对应testUtf8Passthrough,这也是tree-unicode向量存在的意义),而\u后的十六进制必须是小写。
4.2 生产路径与解码防御
fromRawHashesBytes()(CanonicalManifest.php)是生产路径的命名接缝(named seam):签名数据signature.json中的原始哈希字节本身就是规范清单 M,直接原样返回(恒等函数),仅在跨检查场景才做重序列化。decodeHashesMap()(CanonicalManifest.php)将规范 JSON 解码回哈希映射,并针对畸形输入(json_decode返回null)和 JSON 标量(如42)抛出MissingSignatureException,避免损坏数据在SignatureEnvelope数组类型构造处退化为TypeError,而是干净地降级为MISSING_SIGNATURE原因码。
五、测试用例如何消费 golden 向量
golden 向量的直接消费者是 tests/lib/IntegrityCheck/Verifier/CanonicalManifestTest.php,其verifyGoldenVector()私有方法(CanonicalManifestTest.php)对每个向量执行三重断言:
- 恒等检查:
fromRawHashesBytes($raw)返回输入原字节,assertSame严格比对; - 往返一致:
decodeHashesMap($raw)解码后再serialize(),要求与 golden 原文件assertSame严格相等(byte-exact),任何序列化偏差都会在此暴露; - 结构检查:解码后的哈希映射非空、且为数组。
四个向量分别对应testGoldenTreeBasic、testGoldenTreeCruft、testGoldenTreeEdge、testGoldenTreeUnicode四个测试方法。除此之外,该测试类还覆盖转义规则的边界(testEscapeDoubleQuote、testEscapeBackslash、testEscapeTab、testEscapeNewline、testEscapeControlByte、testForwardSlashNotEscaped)以及畸形输入防护(testDecodeHashesMapRejectsMalformed、testDecodeHashesMapRejectsScalar)。
golden 清单还被下游的签名验证测试复用:tests/lib/IntegrityCheck/Verifier/ManifestVerifierTest.php 直接读取golden/tree-basic/manifest.canonical.json作为待验签消息 M,配合 tests/data/integritycheck/verifier/ 下的签名与证书夹具完成端到端验签:
testEcHappyPath:用sig-ec-p384-tree-basic.b64与ec-leaf.crt验证 ECDSA P-384 + SHA-384 签名通过;testRsaPssHappyPath:用sig-rsa-pss-sha384-tree-basic.b64与rsa-leaf.crt验证 RSA-PSS + SHA-384 签名通过;- 篡改测试(
testEcTamperM、testRsaPssTamper、testEcWrongSignature):在 M 末尾追加/删除字符或篡改签名,均期望抛出BadSignatureException,证明“验签失败”分支的正确性; testLegacyPath:验证旧版(G1)格式——将hashes键排序后json_encode得到 legacy 字节,用rsa-pss-sha1算法与旧证书验签通过,说明新验证器对历史签名的兼容。
六、签名夹具的生成方式
verifier/gen_signatures.sh 展示了这些夹具的来源:以openssl dgst -sha384直接对 golden 清单manifest.canonical.json的原始字节做哈希签名,再将签名 base64 编码输出。脚本明确注释“Uses ocsign test keys to sign the golden tree-basic manifest”,其中关键命令:
# EC P-384 + SHA-384(默认填充) openssl dgst -sha384 -sign "${OCSIGN_KEYS}/ec-leaf.key" "$M" | base64 # RSA-PSS + SHA-384(显式 PSS 填充,salt 长度 48) openssl dgst -sha384 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:48 \ -sign "${OCSIGN_KEYS}/rsa-leaf.key" "$M" | base64从中可以确认:签名对象就是 golden 清单的原始字节本身,不存在“先美化再签名”的环节——这正是 golden 向量必须保持字节精确的根本原因。仓库中另有 gen_g1_legacy_fixture.sh 等脚本用于生成 G1 旧版签名夹具,对应测试套件中的 G1 兼容性用例。
七、本地复现与回归检查
若需在本地验证 golden 向量的一致性,可在仓库根目录运行相关 PHPUnit 测试(需 PHP 环境与依赖就绪):
# 仅运行 golden 向量与转义规则的序列化测试 ./lib/../vendor/bin/phpunit tests/lib/IntegrityCheck/Verifier/CanonicalManifestTest.php # 运行端到端验签测试(EC/RSA-PSS/legacy) ./lib/../vendor/bin/phpunit tests/lib/IntegrityCheck/Verifier/ManifestVerifierTest.php若 PHP 侧序列化逻辑(CanonicalManifest.php)与 Go 侧serialize.go出现任何偏差,testGoldenTreeBasic等用例中的assertSame($raw, $serialized)会立即失败并指出具体向量,从而快速定位差异所在。这也正是 golden 向量作为“跨实现一致性契约”的核心价值:它把语言间的隐式约定固化为可执行、可回归的显式测试。
总结
ownCloud core 的 golden 测试向量目录(tests/data/integritycheck/golden/)虽小,却是 G2 代码签名体系可靠性的基石。它通过四组精心构造的文件树,把 PHP 验证器与 Go 签名器之间必须满足的字节级序列化契约固化下来:紧凑无空白的 JSON、字节序键排序、逐字节转义规则、UTF-8 原始透传,以及“禁止手工编辑”的纪律。配合 CanonicalManifest.php 的手写序列化实现与 CanonicalManifestTest.php、ManifestVerifierTest.php 的严格断言,任何跨语言的不一致都会在 CI 中第一时间暴露——这正是现代代码签名系统在异构语言栈下保障一致性的工程范本。
- 后端
- 内容协同
【免费下载链接】core
:cloud: ownCloud web server core (Files, DAV, etc.)
相关推荐
GeoLibre 双引擎向量工具黄金测试夹具(Golden Fixtures)规范解析
GeoLibre 双引擎向量工具黄金测试夹具(Golden Fixtures)规范解析 本指南深入剖析 GeoLibre 中共享向量工具夹具(shared ve
GIS数据可视化前端桌面应用后端Bokeh 属性系统(bokeh.core.properties)完全指南:模型校验、序列化与向量化
Bokeh 属性系统(bokeh.core.properties)完全指南:模型校验、序列化与向量化 Bokeh 是一个面向浏览器的交互式数据可视化库,其核心建
数据可视化图表库CANN ops-transformer 算子测试实战:quant_flash_attn HIF8 全量化(quant_mode=0)pytest 测试用例执行与 Golden 校验指南
CANN ops transformer 算子测试实战:quant_flash_attn HIF8 全量化(quant_mode=0)pytest 测试用例执行
算子库人工智能大模型深度学习CANNAscend
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考