Files
MeowOS 5fac5e380d fix(nyai): 导出 exec 绝对路径相对化 + gzip stderr 重定向 (agent-签名产签链路)
- cmd_export 原先对 app.json 的 exec(绝对路径,如 /usr/data/mnt/sd_0/MeowAPPs/<pkg>/<bin>)
  误用 sanitize_member 剥前导 '/' 拼到包目录下,导致 access(<src>/usr/data/...) 必然失败,
  所有真实 app 导出被拒(exec 文件不存在或不可执行),卡死「导出→产签」链路。
  新增 exec_rel_to_pkg(): 按 ①src/ 前缀 ②/MeowAPPs/<pkg>/ 段(根路径不一致的沙箱/CI)
  ③相对路径 ④绝对但定位不到包目录→拒, 正确相对化 exec。
- install/upgrade 解包命令把 2>/dev/null 只绑到 tar,gzip stderr(footer trailing garbage /
  损坏 crc error)会漏到用户输出;改 gzip 也重定向。
- 同链验证: 宿主 meow_sign/meow_verify_host/qemu-mipsel 验签矩阵通过; 真机(10.78.193.50)
  无签 os.meow.*拒装/已签安装OK+.mpk.src/篡改拒装/降级拒装 全过; 装后已卸并恢复原状。
  (devdocs 本地不入库; 产签实走记录+README 见 patch_v2/meow_sign/README.md §8)
2026-08-31 19:30:32 +08:00
..

meow_sign — MeowOS .mpk 产签/验签工具(宿主端)

定位:离线维护者侧工具,给 .mpk 包打 Ed25519 签名,供设备端 meow_verify(固件 /usr/bin/meow_verify) 验签。本工具不落设备、不入库私钥。设计依据:devdocs/plan/MPK_SIGNATURE_DESIGN.md(§4.2 产签流程 / §3.1 块格式 / §5.1 挂点)。


0. 安全模型(先读,诚实标注)

项 说明
算法 Ed25519(非对称)。私钥仅在离线维护机,设备只持公钥(固化进 meow_sign 的 meow_signing_pub.h)。
私钥 .keys/signing.key(0600)——绝不入库(.gitignore 已忽略 .keys/)、绝不落设备;全网唯一,务必单独备份。
设备验证 meow_verify(固件)只读内嵌公钥数组验签;os.meow.* 包必须有有效签名,否则 nyai/launcher 拒装(防伪造系统包名)。
签名覆盖 payload_sha256(整个 tar.gz 前缀,含二进制/资源/app.json)+ 显式 package(包名)。改内容/改包名 → 验签失败。
诚实天花板 签名 + IPC 鉴权是防误装/防伪造包名/防沿途篡改,不是真安全边界:有 shell 的 root 用户可直接建目录/换 meow_verify。它挡住第三方 App 的「无意识越权」,挡不住 root 有意攻击(见 MPK_SIGNATURE_DESIGN §8)。

1. 用法

meow_sign keygen <priv_path>                    # 生成密钥对:priv(0600) + <priv>.pub(32B raw) + stdout 打印 pub_hex
meow_sign sign <pkg>.mpk <priv> [--v1]          # 给纯 tar.gz 追加签名 footer(默认 v2 含时间戳;--v1=2A 旧版无 ts)
meow_sign verify <mpk> <pub|hex>                # 用公钥(.pub 文件或 64 位 hex)验签指定 .mpk(产签后自测)
  • sign 的 <pkg>.mpk 文件名茎必须等于包名(app.json.package),这是三重绑定①(meow_verify 会二次校验)。
  • 默认产出 v2 块(184B):含被签时间戳 ts,验签端做「未来宽限」校验(超出 now+86400 拒)。 如需兼容旧固件(2A 的 meow_verify 只认 180B 的 v1)可加 --v1 产旧版。新固件 meow_verify v1/v2 都读。
  • verify 是宿主侧镜像(产签后、分发前确认签名有效);设备侧真正把关的是 meow_verify。

2. 产签流程(给新系统 App 签名)

以新增 os.meow.xxx 为例。

2.1 首次:生成/备份密钥(仅一次)

./patch_v2/meow_sign/meow_sign keygen .keys/signing.key
# 私钥 .keys/signing.key(0600) 请离线备份;公钥 .keys/signing.key.pub 可入库/固化进固件。

若已存在 .keys/signing.key(本仓库 2A 已生成),直接复用,勿覆盖(覆盖会令旧包全部失效)。

2.2 打包(不含签名;.mpk = 纯 tar.gz)

mkdir -p <build>/os.meow.xxx
# 放入 app.json + meow 二进制 + 资源;app.json 的 package 必须 == 目录名 == 文件名茎;version 单调递增(禁降级)
tar czf os.meow.xxx.mpk os.meow.xxx

app.json 要点:package(= os.meow.xxx)、exec(指向 MeowAPPs/os.meow.xxx/ 内二进制)、version(≥ 已装,禁止降级)。

2.3 签名(默认 v2)

./patch_v2/meow_sign/meow_sign sign os.meow.xxx.mpk .keys/signing.key
# => sign ok (v2): ... package=os.meow.xxx ts=<unix> block_len=184 ...

2.4 产签后自测(可选但推荐)

./patch_v2/meow_sign/meow_sign verify os.meow.xxx.mpk .keys/signing.key.pub   # 用自己公钥确认
./patch_v2/meow_verify/meow_verify_host os.meow.xxx.mpk                      # 用固件内嵌公钥确认(与设备同口径)

2.5 装到设备 / 分发

  • 把 os.meow.xxx.mpk 放到卡上(如 MeowOS/opt/nyai/ 或目标目录),用 nyai install 安装。
  • 安装期由 nyai/launcher 调 meow_verify:验签通过 + 三重绑定(footer.package==文件名茎==app.json.package + os.meow.* 前缀)才落盘;否则拒装并回滚。
  • os.meow.* 无签名/验签失败 → 一律拒装(防伪造系统包名);第三方包无签可直接装(但建议也签名防篡改)。

3. 三重绑定(三方各司其职)

绑定 校验方 校验点
① footer.package == 文件名茎 meow_verify(--package/推导) 防「把 os.meow.xxx.mpk 改名为 evil.mpk」
② footer.package == 解包 app.json.package nyai / launcher 安装流 防「改 app.json 的 package」
③ os.meow.* 前缀 + 必须验签过 nyai / launcher 安装流 防「无签名冒充系统包」

4. 多公钥 / 密钥轮换(2B)

固件 meow_verify 内嵌公钥数组(meow_signing_pub.h),验签时任一公钥通过即放行。轮换 = 加钥/去钥 + 重编 meow_verify(随固件更新):

#define MEOW_PUB_KEY_COUNT 2
static const uint8_t g_meow_pub[MEOW_PUB_KEY_COUNT][32] = {
    { /* key #0:旧主钥(保留做跨代兼容) */ ... },
    { /* key #1:新钥 */ ... },
};
  • 加钥:新钥 meow_sign keygen 后追加到数组尾部、MEOW_PUB_KEY_COUNT +1、重编 meow_verify → 旧钥签的包(新固件仍内嵌旧钥)与新旧包都放行(平滑过渡)。
  • 去钥:从数组移除 + 重编 → 原钥签的包在新固件拒装。去钥必须与包重签协同(先用新钥重签所有在维护的系统包)。
  • 私钥更新(新钥签新包)时,务必保证新私钥离线备份,且旧私钥在轮换期内保留(否则旧钥签的包失效)。

5. v1 / v2 块与兼容

v1(2A 旧版) v2(2B 加固)
总长 180B 184B
时间戳 无 有(ts,unix 秒,被签名)
被签消息 "MEOWSIG1"‖alg‖pkg_len‖package‖sha256 v1 msg ‖ ts(4B,LE)
meow_verify 读取(不检 ts) 读取(校验 ts 未来宽限,超 now+86400 拒;ts==0 视为畸形拒)
  • 新 meow_verify(2B 固件)v1/v2 都读 → 旧 2A 签的包仍可装/可导出复验。
  • --v1 产出的包在旧 2A 固件可装;v2 包需 2B+ 固件(旧固件 meow_verify 只认 180B,会把 184B 块误判为无签 → os.meow.* 拒装)。固件与工具链同版本发布即可避免此坑。

6. 明确不做(诚实)

  • 防重放 / 密钥过期 / CRL / 在线验证:需网络+后端,超出「单机 + 卡分发」模型(MPK_SIGNATURE_DESIGN §8)。
  • 时间戳只做「未来异常」宽限拒绝(防错误时钟/手工伪造未来 ts),不做「旧包过期拒装」。
  • 签名是防误装/防伪造包名/防篡改,不是安全边界。

7. 相关文件

文件 作用
meow_sign.c 产签/验签工具源码(复用 ../meow_verify 的 tweetnacl.c + sha256.c + meow_sig.h)
build_sign.sh 宿主编译(./build_sign.sh / ./build_sign.sh static)
../meow_verify/meow_verify.c 设备侧校验器(固件 /usr/bin/meow_verify)
../meow_verify/meow_signing_pub.h 固件内嵌公钥数组(仅公钥,可入库)
../meow_verify/meow_sig.h 签名块格式 + 被签消息构造(verifier/signer 共用)
.keys/ 私钥目录(已 gitignore,绝不入库)

8. 端到端实走(真机已验证,2026-08-31)

完整「产签→验签→真机安装→导出保留签名」的可复现步骤、逐命令预期输出、6 个失败矩阵项、常见错误速查与本次修复,见 devdocs/plan/MPK_SIGNATURE_DESIGN.md 的「产签实走记录 / 操作步骤(端到端)」节。此处只记三个最容易踩的坑:

  1. .mpk 文件内文件须在 tar 根:app.json/meow/资源须并列于 tar 根(无 <pkg>/ 顶层包裹目录)。 打包用 tar -C <pkg> .(导出生成本就是 tar -C src .),否则设备安装报 解压/校验 app.json 失败。
  2. 包名 = 文件名茎:meow_sign sign 从 <pkg>.mpk 文件名茎推导包名并写入 footer;<pkg>.mpk 必须 == app.json.package。 改文件名/包名 → 验签 package mismatch。
  3. exec 可为绝对路径,导出须相对化:真实 app 的 app.json.exec 是绝对路径(launcher 直接 execve)。 nyai export 须把 exec 相对化到包目录(已修 nyai.c 的 exec_rel_to_pkg)再存在性检查/白名单打包; 否则所有真实 app 导出报「exec 文件 … 不存在或不可执行」。