Skip to content

XHIM 正式制品、真机、安全与合规证据

一键正式门禁

bash
scripts/release/commercial_gate.sh \
  /secure/final-artifacts 1.0.0 /secure/evidence \
  /secure/legal-approval.json /secure/capacity-evidence.json

当前源码的开发/预览制品默认版本是 0.1.0-beta.1。正式 Production 商业门禁 只接受不含预发布段的稳定 SemVer(例如 1.0.0);-alpha-beta-rc-dev 版本不能作为 Production 制品。

正式门禁还要求当前 HEAD commit 已签名,且仓库中精确存在 refs/tags/v<version>。该引用必须是 annotated/signed Tag,最终解析到同一 HEAD;轻量 Tag、未签名 Tag、Tag 指向其他 Commit、未签名 Commit,或者受 保护发行机执行以下任一命令失败都会立即终止:

bash
git verify-tag "refs/tags/v1.0.0"
git verify-commit HEAD

受保护发行机的 Git/GPG 配置和 Keyring 只能信任获批发行身份;CI 必须拉取完整 目标 Commit 和精确 Tag,不能用分支名、环境变量或仅文件名模拟 Tag。上述 Git 身份检查只进入 Stable commercial_gate.sh,不会阻塞 0.1.0-beta.1 等开发/预览构建。

该命令随后依次验证五端最终包、平台签名、Release Manifest、GPG 签名、SPDX/CycloneDX SBOM、安全扫描、第三方许可证、律师审批、 真机矩阵、容量/故障注入和 SHA-256。任何工具、证书、设备、报告或签字缺失都会 失败。

整体证据封装

commercial_gate.sh 要求每次发行使用新的 evidence-root。只有人工提前填写的 devices/ 真机证据目录可以预先存在;一旦发现旧的 Release/Evidence Manifest、SBOM、security、licenses、法务/容量副本、设备/容量策略副本、 capacity-observability/、容量报告或 SHA256SUMS,门禁立即失败且不会删除 或覆盖原文件。这样可以避免不同版本、不同 Commit 的证据混装。

正式门禁不会在调用者可变的 artifact-root 上分阶段取证。它先通过 openat/O_NOFOLLOW 语义逐项复制到仅发行进程可访问的临时目录,拒绝符号 链接、特殊文件、控制字符路径、Unicode/大小写碰撞和已有目标,并在复制前后 核对源文件身份与元数据。快照中的目录和文件随即改成只读,独立的 canonical 快照清单固定每个相对路径、大小和 SHA-256。后续平台验签、Release Manifest、 SBOM、真机制品 Hash 和 SHA256SUMS 全部只读取这一个快照;调用者即使替换 原目录也不会混入另一批字节。

生成最终 Evidence Manifest 并签名前,门禁会再次逐项验证快照 Hash、全部平台 签名、Windows 证书链/RFC 3161 时间戳、Release Manifest 的每项 Hash、Release Manifest 的 GPG 签名以及 SHA256SUMS。任一只读位被取消、文件增删改、签名者 变化或清单不一致都会 fail-closed。临时快照只在门禁进程生命周期内存在并在 退出时清理,不作为客户分发目录。

法务审批、device-matrix.jsoncapacity-plan.json 均以 O_EXCL 语义 安全复制到最终证据根。容量证据中的每个 observability_artifacts 必须是 {path, sha256},路径位于容量 JSON 同级的 capacity-observability/,且只能指向普通非符号链接文件。门禁从已固定打开的 源文件复制到最终证据根,复制前后都重新计算 SHA-256,再验证已封装的容量 JSON;符号链接、路径穿越、Hash 不符和已有目标都会被拒绝。

全部真机、安全、许可证、SBOM、容量报告、Release Manifest 和 SHA256SUMS 完成后,门禁生成 canonical evidence-manifest.json。清单固定绑定:

  • product=XHIM、正式版本和完整 Git Commit;
  • evidence-root 中每个普通文件的相对路径、字节大小和 SHA-256;
  • 固定 schema,稳定 UTF-8 JSON 排序,不记录生成机器的绝对路径、GPG Key ID 或任何私钥。

工具递归拒绝符号链接、特殊文件以及 Unicode 归一化后的大小写冲突。 evidence-manifest.json 自身和其 .asc 被明确排除以避免递归哈希;除此之外 新增、删除或修改任一证据都会使验证失败。门禁生成清单后先执行内容验证,再用 XHIM_GPG_KEY_ID 创建 ASCII armored detached signature,并立即由统一签名 助手解析 GPG VALIDSIG 状态。发行机必须同时设置规范化后的完整 40/64 位 XHIM_GPG_FINGERPRINT;短 Key ID、不同主密钥、缺少 VALIDSIG 或出现多份有效签名都会失败。

收到证据包后可独立复核:

bash
scripts/release/evidence_manifest.py verify /secure/evidence \
  --version 1.0.0 --commit <完整 Git Commit>
XHIM_GPG_FINGERPRINT='完整主密钥指纹' \
scripts/release/gpg_release_signature.py verify \
  /secure/evidence/evidence-manifest.json \
  /secure/evidence/evidence-manifest.json.asc

五端正式制品

  • iOS 与 macOS:同一静态 XCFramework 内必须同时包含 Device、Simulator 和 macOS Slice;在写入构建来源后只签名一次 XCFramework 外层 Bundle,再用最终 SwiftPM/CocoaPods 空白工程消费;
  • Android:arm64-v8a/x86_64 原生库进入 AAR,Maven POM/Module 与 AAR 使用 OpenPGP 分离签名;
  • Windows:x64/arm64 DLL 使用 Authenticode,NuGet 包使用仓库签名;
  • HarmonyOS:arm64 HAR/OHPM 包使用组织发布密钥和分离签名;
  • 所有包必须来自同一 Commit/版本,Manifest 记录逐文件大小和 SHA-256。

Android、Windows 和 HarmonyOS 的 Core 不能由组包机临时替换。受保护原生 构建器必须先在干净、固定的 release Commit 上生成二进制,并把该 Commit 的 ffi/include/xhim/xhim_v1.h 原样放入每个 native root 的 include/xhim/xhim_v1.h,再执行:

bash
scripts/release/attest_native_build.sh \
  android 1.0.0 /secure/native/android \
  'android-ndk-r27c-clang-18.1.8'
scripts/release/attest_native_build.sh \
  harmony 1.0.0 /secure/native/harmony \
  'ohos-sdk-API15-clang-15.0.4'

Windows 的 x64/arm64 DLL 必须先完成 Authenticode 签名和验签,再执行 attest_native_build.sh windows ...。证明文件 XHIMNativeBuildAttestation.json 及其 .asc 会绑定:

  • 完整 Git Commit、Git Tree 和 dirtyWorktree=false
  • 发行版本、受控工具链标识、目标平台和架构;
  • 每个原生库的大小、SHA-256,以及 Android/Harmony 的精确 xhim_v1_* 动态导出表;
  • 公开 C ABI 头文件的固定分发路径、大小和 SHA-256;证明创建与验证都会直接 读取目标 Commit 中的 Git blob,拒绝同名但来自其他提交的头文件;
  • 二进制内嵌的分发版本和完整源码 Commit 标记。

平台组包器只接受已签名且与当前 checkout 一致的证明,并在复制后按 release 目录布局再次校验。三端输出均保留 include/xhim/xhim_v1.h,因此后续 platform provenance 也会覆盖公开头:

bash
scripts/release/build_android_packages.sh \
  1.0.0 /secure/native/android /secure/final-artifacts
scripts/release/build_windows_packages.sh \
  1.0.0 /secure/native/windows /secure/final-artifacts
scripts/release/build_harmony_package.sh \
  1.0.0 /secure/native/harmony /secure/final-artifacts

移动端组包和最终输入门禁还会解析 AAR/HAR 中的 ELF 动态段,要求 Core DT_SONAME 与 JNI/Node-API Bridge 的 DT_NEEDED 都精确为 libxhim_core_v1.so。带 .so.1 后缀、绝对路径、缺失依赖或包内外 Core 字节 不一致都会失败。

verify_release_inputs.sh 还会读取 Apple Release Manifest/Attestation/Podspec、 Maven POM/Gradle Module、NuGet .nuspec 和 Harmony oh-package.json5 内部版本,拒绝仅重命名文件但包元数据版本不一致的制品; 随后对 AAR、NuGet 和 HAR 执行归档安全检查,拒绝路径穿越、重复条目、符号 链接、加密条目、异常压缩比、超出上限的展开大小、私钥/证书容器和 C/C++ 实现源码。该检查保护客户安装环境,也把“闭源内核只以二进制交付”变成可执行 门禁。平台公开门面和类型声明可以按各包管理器规范交付,但不得夹带内核或 Product Adapter 实现。

归档安全规则本身可在没有发行证书的开发机上回归:

bash
python3 -m unittest discover \
  -s scripts/release/tests -p 'test_*.py' -v

签名脚本本身不内置私钥,只读取环境提供的证书标识或受控安全路径。Release 机器必须使用硬件令牌、CI Secret 或受控 Key Vault。可从 release/signing.env.example 复制发行机配置模板,但真实值必须放在仓库之外。

Apple 签名身份只传给 build_ios_xcframework.sh。签名完成后 assemble_ios_package.sh 才能生成 SwiftPM checksum、CocoaPods ZIP SHA-256 和 Release Attestation;统一 sign_artifacts.sh 只验证 Apple,不会再次签名 并破坏这些 Hash。正式组包目录用以下命令原子放入统一制品根:

bash
scripts/release/stage_apple_packages.sh \
  1.0.0 /secure/apple-assembled /secure/final-artifacts FG4922H85G

统一制品根还必须原子放入同一 Commit 的客户公开 C ABI 头。脚本要求工作区 干净、HEAD 与指定 Commit 完全一致,并拒绝覆盖已有文件:

bash
scripts/release/stage_public_header.sh \
  /secure/final-artifacts <完整 Git Commit>

五端组包和公开头完成后,统一签名步骤必须显式绑定同一完整 Commit:

bash
scripts/release/sign_artifacts.sh \
  /secure/final-artifacts 1.0.0 <完整 Git Commit>

该步骤签名前先验证顶层公开头、三端 native attestation 和平台 provenance; 公开头及每端副本都必须与指定 Commit 的 Git blob 字节完全一致。Android/ Harmony 包签名、Windows NuGet 仓库签名完成后重新封存并立即复核 provenance, 最后再次验证原生证明、包内外二进制和全部平台签名。它不会重签 Apple XCFramework,也不会重签或回填 Windows DLL,因而不会破坏已经证明的字节。 Windows .snupkg/.symbols.nupkg 不进入客户公开制品;符号包应单独进入受控 内部符号服务。

跨平台签名阶段设置 XHIM_APPLE_TEAM_IDXHIM_GPG_KEY_ID、完整 XHIM_GPG_FINGERPRINT 和对应 Windows 证书变量。Windows 正式发行优先让 signtool.exe 和 NuGet 6.12+ 从受保护的用户/机器证书存储读取证书:

  • DLL 用完整 40 位 XHIM_WINDOWS_CERT_SHA1 选择 Authenticode 叶证书,并以 完整 64 位 XHIM_WINDOWS_CERT_SHA256 再次固定其 DER 身份;
  • NuGet 用完整 64 位 XHIM_WINDOWS_NUGET_CERT_FINGERPRINT 同时签名和验签;
  • XHIM_WINDOWS_TIMESTAMP_URL 必须指向批准且不在 URL 中携带凭证或查询令牌的 RFC 3161 服务;
  • 验证器要求 Authenticode/NuGet 链策略成功、时间戳证书存在且用途正确,绝不 再以 Publisher 名称子串作为身份依据。

非 Windows 发行机只能走显式的 osslsigncode 受控回退: XHIM_WINDOWS_PFX_PATHXHIM_WINDOWS_PFX_PASSWORD_FILE 必须是当前发行 用户所有、单硬链接且权限为 0600 或更严格的普通文件。脚本把二者复制到生命 周期受控的 0700 临时目录,以 -readpass <临时文件> 传递并在退出时清理;密码值不会进入 argv。验签还必须 提供完整 64 位 XHIM_WINDOWS_CERT_SHA256、代码签名 CA 和时间戳 CA,缺少任 一项即失败。NuGet 不接受 -CertificatePassword 或证书路径密码模式。

验证器同时会拒绝 Team ID、OpenPGP 指纹、Windows 完整证书指纹不匹配,以及 Apple 包内来源、源码树、Privacy Manifest、法务复核编号、私有 Specs 或外部 ZIP Hash 不一致。

真机证据

release/device-matrix.json 固定 iOS、macOS、Android arm64、Windows x64、 Windows arm64 和 Harmony arm64 目标及九个公共场景。每台设备复制 release/evidence/device-result.template.json 填写真实 OS、制品 Hash、操作 人和日志/视频。attachments 不接受 URL 或任意说明文字;每项必须写成 {"path":"devices/...","sha256":"..."},指向最终 evidence-root 下的普通 非符号链接文件,验证器会重新计算 Hash。模拟器、模板值、空附件、错误 Hash 和 not_run 均不能通过。矩阵策略自身也会作为 device-matrix.json 归档。

安全审计

scripts/security/run_security_audit.sh 的正式模式要求 gitleaks、Semgrep、 govulncheck、OSV-Scanner 和 Trivy 全部存在且成功,并额外执行 go vetgit diff --check。风险接受必须复制 security/exceptions.template.json, 填写 Owner、到期日、补偿控制和批准人;不能用“误报”文字直接跳过。 脚本同时记录工具版本;Gitleaks 覆盖 Git 历史与工作区,Semgrep 使用官方 p/default 规则集且关闭遥测,OSV-Scanner 使用 v2 scan source 接口, govulncheck 先保存 JSON 再以普通模式执行一次,确保发现可达漏洞时确实返回 失败码。

SBOM 与法务

每个最终制品目录同时生成 SPDX JSON 和 CycloneDX JSON。第三方许可证收集器 从 Go Module Cache 复制实际版本的 LICENSE/NOTICE,并保留模块 Sum;原生依赖 表必须按最终链接结果复核。legal-approval.template.json 默认 not_reviewed,只有律师/法务把准确版本和 Commit 标记为 approved 才能过门。

容量报告

容量门禁要求 15 分钟校准、60 分钟目标负载、30 分钟双倍目标和 24 小时 Soak, 并完成 WebSocket 重连风暴、PostgreSQL 切换、对象存储超时/429、Push 429 和 凭证密钥轮换。报告必须附硬件、拓扑、本地监控导出、p95/p99、错误率和峰值 吞吐;URL 不能替代证据。observability_artifacts 的文件会安全复制为 capacity-observability/,并连同固定的 capacity-plan.json 一起进入最终 Evidence Manifest。当前仓库的报告明确标记为 NOT EXECUTED,不能作为销售 数据。

XHIM 客户端 SDK 与服务端文档