Skip to content

fix(android): 对齐应用内更新 minisign 并修复凭据 vault - #1061

Merged
H-Chris233 merged 10 commits into
Open-Less:betafrom
HKLHaoBin:fix/android-updater-minisign-and-vault-error
Sep 12, 2026
Merged

H-Chris233 merged 10 commits into
Open-Less:betafrom
HKLHaoBin:fix/android-updater-minisign-and-vault-error

Conversation

@HKLHaoBin

Copy link
Copy Markdown
Contributor

摘要

Fixes #1054

修复 Android 应用内更新的 minisign 验签,以及凭据信封在 HyperOS KeyMint 失败时无法读写、进而挡住启动和「增加提供商」的问题。

修复 / 新增 / 改进

  • 应用内更新:按桌面 Tauri 方式解析再 Base64 包装过的 minisign 公钥/签名,避免 Invalid encoding in minisign data 让安装器根本打不开。
  • 启动:Keystore/信封暂时读失败时不再让 Core.start() / Tauri setup abort,也不再用第二次 start() 的门禁挡住 2.0 握手。
  • 错误语义:vault 读失败暴露 Persistence「无法读取已保存的凭据」,不再伪装成未配置火山 ASR;创建渠道失败时前端展示后端原文。
  • 信封写入:每次 mutate 都重试 Keystore;for_update 失败禁止用空根覆盖信封;无法用于 Cipher 的密钥按失效信封恢复。
  • HyperOS KeyMint:主线程 hop + v3 别名仍 KeyStoreException:10 时,回退到应用私有目录的软件 AES-GCM(credentials.sw.key),migration 标记同步走文件。
  • 诊断:保留 [agent-dbg] 埋点(H7/H9/H12/H14 等),导出日志即可区分 Keystore 异常码、信封是否落盘、软件回退是否生效。不把密钥/明文打进日志。
  • CI:顺手补齐 linux-egui 根证书指纹字段与 fcitx5 门禁,避免该分支 Release Linux egui 红灯。

兼容

  • 不包含:桌面 Keychain/凭据格式变更;不卸载、不清应用数据。
  • 对现有用户 / 本地环境 / 构建流程的影响:
    • Keystore 正常的设备仍走 AndroidKeyStore;仅 KeyMint 拒绝 AES-GCM 的设备改用 UID 私有软件密钥(与信封同目录,非硬件保护)。
    • 本轮真机已 wipe 无法解密的旧信封,旧 API Key 无法自动恢复,需重填。
    • 软件回退密钥文件:files/OpenLess/credentials.sw.key
    • 诊断日志会增加 [agent-dbg] WARN,便于后续真机排障;不含密钥。

测试计划

  • 命令:node scripts/android-credential-keystore-contract.test.mjs(在 openless-all/app
  • 结果:passed
  • 证据路径:本地合同测试输出
  • 命令:GitHub Actions Android APK (debug) @ fix/android-updater-minisign-and-vault-erroree0761ea
  • 结果:真机 adb install -r 后可启动;设置里增加提供商成功;听写 + DeepSeek 润色成功
  • 证据路径:用户导出日志 openless-2026-09-11T09-08-02.log
    • H1 core start ok / running:true
    • H9 seal/open/markMigrationCompletestatus_ok(不再出现 KeyStoreException:10
    • H14 keystore_seal ok(packetLen 212→257,信封在增长)
    • H7 ok:true, lastReadError:false
    • [llm] polish_streaming provider=deepseekstream done

HKLHaoBin and others added 9 commits September 10, 2026 21:45
… can open

Android updater called PublicKey::from_base64 on the wrapped tauri.conf pubkey, so install died before installApk. Also keep vault envelope failures from looking like an unconfigured Volcengine ASR provider.

Co-authored-by: Cursor <cursoragent@cursor.com>
Release Linux egui 在 beta 上因 RemoteInputStatus 缺字段、match 臂类型不一致,以及门禁要求 ensure_fcitx5_ready(?) 而失败;安装调用本身已在监听器之前,错误会展示在 UI 里。

Co-authored-by: Cursor <cursoragent@cursor.com>
Core.start() 会走 configuration_snapshot → load_credentials_for_update;信封读失败若返回 Err,setup hook 直接 SIGABRT。改为记录 last_read_error 并返回未缓存默认值,应用可以启动。

Co-authored-by: Cursor <cursoragent@cursor.com>
Core.start() 在 setup 成功后还会被 event bridge 和 get_startup_snapshot 再调一次;此时 last_read_error 已记下,require_readable_vault 把 Persistence 抛成「暂时无法启动」。握手改为降级空凭据状态继续运行。

Co-authored-by: Cursor <cursoragent@cursor.com>
启动时信封暂时读失败后,require_readable_vault 会在真正 load 之前短路,导致添加提供商一直操作失败。改为每次读写都重试 Keystore;读失败返回 Persistence 且不缓存/不写入默认空根;前端展示后端具体错误。

Co-authored-by: Cursor <cursoragent@cursor.com>
添加提供商已会重试信封,但 Keystore 仍返回 temporarily unavailable。JNI 原先丢掉 Kotlin 异常与 JNI 失败路径,无法区分 class load、status 3 还是 BackendBusy。

Co-authored-by: Cursor <cursoragent@cursor.com>
真机 open 稳定抛出 InvalidKeyException(非 UserNotAuthenticated)。继续当成 temporarily unavailable 会让已保存凭据永远读不出来。改为走密钥缺失路径,删除无法解密的信封后可以重新配置提供商。

Co-authored-by: Cursor <cursoragent@cursor.com>
清掉失效信封后,增加提供商仍在 seal 上失败(ProviderException/KeyStoreException)。HyperOS 上 Keystore2 从 JNI 后台线程调用常失败;改为主线程执行,并换用未损坏的 v3 别名,失败时删除别名再生成一次。

Co-authored-by: Cursor <cursoragent@cursor.com>
HyperOS 上 seal 稳定返回 KeyStoreException 10,主线程与 v3 别名仍无法写信封。Keystore 不可用时改用 UID 私有软件 AES-GCM,避免增加提供商失败。

Co-authored-by: Cursor <cursoragent@cursor.com>
@HKLHaoBin

Copy link
Copy Markdown
Contributor Author

现在已经不再依赖那把会突然坏掉的硬件密钥。 KeyMint 再抽风,一般不会再把听写打成「没配置」;应用也不会再因为读密钥失败而闪退。

这次保存成功之后,设备上会留下 files/OpenLess/credentials.sw.key。有这个文件后,读写信封 只走软件 AES,不再问 AndroidKeyStore。所以「系统 Keystore 进程被杀 / TEE 报错 10 / 硬件钥匙变成 InvalidKeyException」这类故障,对当前这份凭据基本无效。

还可能出问题的,只有这几类,新代码都能处理,但结果不一样:

情况 会怎样 你要不要重填 Key
KeyMint / Keystore 再坏 继续用软件密钥,启动和保存照常 不用
别的设备仍走 Keystore,后来硬件钥匙失效 清掉解不开的信封,应用还能开;下次保存会自动改走软件 AES 要,旧 Key 解不出来
系统清理/卸载清掉应用私有目录,或只删了 .sw.key 信封解不开,按失效处理,不会卡死
软件 AES 本身写失败(磁盘满、目录不可写) 保存会报错,但不会伪装成「没填火山」 修好存储后再保存

@H-Chris233

Copy link
Copy Markdown
Collaborator

开始审查

@H-Chris233 H-Chris233 self-assigned this Sep 12, 2026
@H-Chris233
H-Chris233 merged commit 82357e8 into Open-Less:beta Sep 12, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Android:应用内更新无法打开 APK 安装器,且凭据信封读失败被误报成未配置火山 ASR

2 participants