文档 · 开发者

02 二维码与深链 lom://pair

二维码与深链 lom://pair 的逐字段格式与手机端校验步骤。

文档来源本栏目是公开仓库 rfdiosuao/lom-desktop 中 docs/open/phone-access/ 的原文,提交 92044e2;本页对应 02-qr.md。以 GitHub 上的最新版为准。

桌面添加手机时显示一个二维码。二维码内容就是一个普通字符串(下称「配对链接」),格式固定:

lom://pair?v=1&h=<h>&p=<p>&t=<t>&k=<k>&f=<f>

链接的来源不影响协议:相机实时扫描、从相册图片解码、系统相机扫到后通过 lom:// 深链跳转进 App,得到的都是同一个字符串,走同一套校验和同一套配对流程。无论来源是什么,手机端都必须:完整校验链接 → 核对桌面公钥指纹 → 让用户在手机上看到桌面名称与脱敏账户后明确点「确认连接」(见 03)。

二维码是一次性临时口令:5 分钟过期、只能被认领一次、关闭桌面弹窗即作废。不要缓存、转发或把链接写进日志。

1. 二维码本身

项取值
内容编码UTF-8 文本(全是 ASCII)
纠错级别M
典型长度约 180–190 字符(官方主机名下 186 字符)
解析上限建议拒绝长度超过 2048 的输入

2. 字段

链接 = scheme lom + authority pair + 查询串。查询串里恰好有下表六个字段,每个字段出现一次,顺序不限;缺字段、多字段、重复字段、未知字段一律视为无效。

字段含义原始字节数编码后字符数编码
v协议版本—1十进制文本,当前必须为 1
h云端主机名可变(UTF-8 文本)可变主机名的 UTF-8 字节做 base64url,不是明文
p配对会话 ID pairing_id1622base64url
t认领令牌 claim_token3243base64url
k预共享密钥 psk3243base64url
f桌面公钥指纹1622base64url

编码统一规则(全协议通用,详见 04):base64url(RFC 4648 §5 字母表 A–Z a–z 0–9 - _),不带 = 填充,且必须是规范编码——解码后再编码必须与原文一字不差,否则视为无效。

注意两点:

  • p、t 在后续 HTTP 请求里按原字符串使用(不要解码后再重编码,也不要大小写转换);
  • k(psk)要解码成 32 字节再作为 HMAC 密钥使用;f 要解码成 16 字节再与自己算出的指纹比较。

示例(取自测试向量,非真实会话)

lom://pair?v=1&h=bG9tLmVudGVybW9kZXR3by5jb20&p=OgX7dsvX6Pp6hNZsT6w8Sw&t=FQw1yQZ_QDI9gaoYuU4eRwQdFFjrO-YJZWp5v_TVKaE&k=3yUTIoVtslOrzFoP_u6Mv94XCSibzlDW7C6unULIp3o&f=rp1mfT5Bdyg1iFGecQecVQ

其中 h=bG9tLmVudGVybW9kZXR3by5jb20 解码为 lom.entermodetwo.com。

3. 手机端校验步骤

任何一步失败都直接中止,提示「这不是有效的麓鸣配对码」(错误码 pairing.qr_invalid,此时不得发出任何网络请求):

  1. 总长度 ≤ 2048;
  2. 能按 URI 解析,且 scheme 为 lom、authority 为 pair、路径为空、没有 fragment(#…);
  3. 查询串按 & 分割、每段按第一个 = 分成键值,键恰好是 v h p t k f 六个且无重复;
  4. v == "1"(其他版本见 08 版本策略);
  5. h 是合法规范 base64url,解码结果是合法 UTF-8;主机名匹配 [a-z0-9]+([.-][a-z0-9]+)*(小写字母、数字、.、-);
  6. 主机名在 App 内置的白名单内。白名单目前只有 lom.entermodetwo.com。这是防止伪造二维码把手机引到别的服务器的关键检查,必须在 App 里写死,不得从二维码或网络动态取;
  7. p 解码后恰为 16 字节,t 为 32 字节,k 为 32 字节,f 为 16 字节,且全部是规范 base64url。

通过校验之后,才能用 https://<h 解码出的主机名> 发起第一个请求(03)。只允许 HTTPS,证书按系统默认校验。

4. 深链接入(Android)

在 AndroidManifest.xml 里给配对 Activity 声明:

<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="lom" android:host="pair" />
</intent-filter>

收到的 Intent.data 就是配对链接字符串。系统相机、第三方扫码工具、浏览器点链接都可能触发它,所以深链进来的内容与手动输入一样不可信:走完整校验,并且必须停在「确认连接」页让用户确认,不能因为是深链就自动完成配对。

5. 配对码路径(没有二维码时)

扫不了码时,用户可以在手机上输入桌面显示的配对码。它不经过上面的链接,见 03 §3。输入规范化:

  • 配对码是 8 个字符,显示为 XXXX-XXXX;
  • 字母表是 31 个字符:23456789ABCDEFGHJKMNPQRSTUVWXYZ(去掉了易混淆的 0 O 1 I L);
  • 用户输入不区分大小写,可以不输连字符,也可以带空格:去掉 - 和空格、转大写后必须恰好 8 个字符且都在字母表内,否则本地提示「配对码不正确」(pairing.code_invalid),不必请求云端;
  • 提交给云端的 manual_code 建议规范化成大写、带连字符的 XXXX-XXXX 形式(云端也接受不带连字符的形式)。

6. 局域网模式的区别

社区版桌面的局域网模式也使用 lom://pair?v=1&…,唯一区别是 h 解码后是 内网IPv4:端口(而不是云端主机名),且没有配对码路径。详见公开仓库 protocol/docs/LAN-PAIR-01.md。本文只覆盖官方云端模式。