NS · 应用通道
状态:通用应用通道与文件应用已接入,验收按应用和候选分别记录(2026-10-25)。
confirm与 Quick 配对(kind = "pairing")保持既有实现;peer、relay PAKE、隧道内 PAKE 和受限 IPC 已作为应用会话基础接通。文件发送本轮范围已完成,C05 按用户决定延期不阻塞本轮,Windows 原生验证保留为发布门;固定候选与证据边界见文件发送和应用模式,不由单个应用的验收推断所有应用、平台或发布均已完成。应用通道是 ns 内部的通用层:Quick 配对、文件发送以及之后的语音、聊天等应用都运行在它上面。通道的两端都是 ns;relay 只看到会合协议的不透明消息,nsplane 只看到对端与端口。本页取代会合协议原 §9 的逐应用映射、Quick 协议 v2 原 §10.2 的配对载荷,以及文件发送原 §4.2 与 §5.1 的会合与密钥协商。协议引用(§n)除非另有说明均指本页。
| 目标 | 推论 |
|---|---|
| 新应用只声明参数 | 应用给出一份 profile(§3);会合、引入、身份交换、PSK、对端安装与隧道入口全部复用 |
| 一套引入流程,三种鉴权 | 人工核对节点 ID(confirm)、一次性口令 PAKE(pake)、已有对端(peer),覆盖配对、票据与已配对设备之间的应用 |
| 应用之间互相隔离 | kind 进入每个签名与每次密钥派生,一个应用的消息不能在另一个应用里重放 |
| relay 不感知通道 | 只使用会合协议的信箱;不新增 relay 操作、报文类型或白名单 |
| 数据只走隧道 | 信箱只承载引入所需的小消息;应用数据始终在 WireGuard 隧道内(会合协议 §10) |
非目标:多于两方的会话、离线投递、按名字或身份查找对方、隧道内应用协议本身(各应用自己定义)。
| 层 | 组件 | 职责 |
|---|---|---|
| 应用 | ns:pairing、transfer,之后的语音、聊天;第三方应用经 ns 的应用接口 | 声明 profile;命令、确认提示与界面;隧道内的应用协议 |
| 应用通道 | ns | 本页:profile、引入、身份交换、密钥派生、对端安装、隧道内入口 |
| 会合 | relay 的 /rendezvous/v1;消息类型在 nsshared | 信箱、短码、接纳、队列、上限,与应用无关 |
| 数据面 | nsplane | 安装与移除对端、隧道内连接、按来源命名空间的 ACL 与 pinhole |
通道只在 ns 中实现,不进入 nsshared:两端都是 ns,第三方应用也经 ns 的应用接口使用它,不自行实现本页的协议。
3. 应用 profile
Section titled “3. 应用 profile”AppProfile { kind [a-z0-9-]{1,32}(会合协议 §5.1) auth confirm | pake | peer lifetime permanent | session | none ttl 信箱 TTL 的默认值与上限,≤ 900 s app_port 隧道内 TCP 端口;null 表示没有隧道内阶段}auth | 信箱用法 | 适用场景 | lifetime |
|---|---|---|---|
confirm | admission: owner,candidates 1–4 | 双方核对对方节点 ID 后建立长期关系 | permanent |
pake | admission: first,candidates: 1 | 经信箱会合,持有一次性口令即授权一次会话 | session |
peer | 不使用信箱 | 两端已是 ready 对端;可在隧道内追加 PAKE 鉴权(§5.2),不改变 peer / none 归属 | none |
内置 profile:
| 应用 | kind | auth | lifetime | ttl | app_port |
|---|---|---|---|---|---|
| Quick 配对 | pairing | confirm | permanent | 默认 600 s,最多 900 s | null |
| 文件发送(relay 票据) | transfer | pake | session | 600 s | 47901 |
| 文件发送(已有对端、对端票据) | transfer | peer;对端票据另在隧道内运行 pake | none | — | 47901 |
| 语音、聊天 | 待定 | peer 或 pake | none 或 session | — | 待定 |
其他组合不允许:confirm 总是建立长期对端,pake 从不写入允许名单,peer 从不安装对端。
lifetime 描述对端安装归属,不是文件会话的完整计时规则。内置 transfer 的邀请期限与认证后文件阶段分别遵守文件发送 §5.6;已有 peer 的 none 不表示无限寿命。该规则不修改 CPace、HELLO/FINISH bytes、通用第三方 IPC 或其它应用的期限。
4. 通道消息
Section titled “4. 通道消息”所有消息都是规范 CBOR 定长数组(Quick 协议 v2 §12.1),放在信箱的 intro 与 data 里;解码器在任何签名、MAC 或 AEAD 校验之前拒绝不规范的输入、多余字节、未知版本与未知类型。
// confirm:信箱 introOffer = [ver = 1, kind text, record_o, e_o bytes32, authority text, expires u64, name text|null, sig_o] sig_o = Ed25519_Io("ns/channel-offer/v1" ‖ CBOR(前七个字段))Answer = [ver = 1, kind text, record_p, e_p bytes32, mailbox_id bytes16, offer_hash bytes32, name text|null, sig_p] sig_p = Ed25519_Ip("ns/channel-answer/v1" ‖ CBOR(前七个字段))
// pake:信箱 open intro;join intro 为空,Share 在接纳后经 data 交换PakeDesc = [ver = 1, kind text, suite = 1]
// 两种模式:信箱 dataMessage = [ver = 1, type u8, body] 1 ACCEPT [answer_hash bytes32, sig] // confirm;sig = Ed25519_Io("ns/channel-accept/v1" ‖ offer_hash ‖ answer_hash) 2 COMPLETE [] 3 HELLO [ciphertext bytes] // pake;AEAD(k_hello_dir, nonce = 0, Hello) 4 APP [payload bytes] // 应用自己的小消息,通道建立后才允许 5..8 PAKE 的 PARAMS / NONCE / SHARE / FINISH,见精确 profile
Hello = [ver = 1, record, endpoint text|null, not_after u64, sig] sig = Ed25519_I("ns/channel-hello/v1" ‖ kind ‖ th ‖ CBOR(前四个字段))
offer_hash = SHA-256(CBOR(Offer)),answer_hash = SHA-256(CBOR(Answer))th = SHA-256(CBOR([C bytes, Y_o bytes32, Y_p bytes32])) // C 定义见 PAKE 精确 profilerecord是发送方当前的签名NodeRecord(Quick 协议 v2 §3);接收方按其有效性与 §3.3 的代际 / 冲突规则处理。- 每个消息中的
kind必须等于信箱的kind与本地 profile 的kind,否则丢弃并中止通道。 name是对方给出的显示名,只用于界面与默认昵称,从不参与授权。APP受信箱的 8 KiBdata上限约束;大于此的数据走隧道。
4.1 密钥
Section titled “4.1 密钥”confirm: ikm = X25519(e_o, e_p)(全零结果被拒绝),t = offer_hash ‖ answer_hashpake: ikm = K_pake(CPace,§5.2), t = thK_ch = HKDF-SHA256(salt = 空, ikm, info = "ns/channel-key/v1" ‖ kind ‖ t)
psk = HKDF-Expand(K_ch, "ns/channel-psk/v1" ‖ I_o ‖ I_p, 32) // WireGuard PSKk_hello_o = HKDF-Expand(K_ch, "ns/channel-hello-o/v1", 32) // 创建方发出的 HELLOk_hello_p = HKDF-Expand(K_ch, "ns/channel-hello-p/v1", 32) // 加入方发出的 HELLOK_confirm = HKDF-Expand(K_ch, "ns/channel-confirm/v1", 32) // 隧道内 OPEN 的 MAC(§7)PAKE 的具体 KDF 输出长度、AAD 与完成确认见精确 profile §2–3。o 指信箱创建方,p 指被接纳的加入方。AEAD 为 ChaCha20-Poly1305;每个 HELLO 密钥只加密一条消息,因此 nonce 固定为 0。临时密钥 e_o、e_p、CPace 秘密与 K_ch 在通道结束后丢弃;psk 只交给对端安装(§6)。
5.1 confirm
Section titled “5.1 confirm”- 创建。 创建方
open {kind, admission: owner, candidates, ttl, intro: Offer};authority是所拨 relay 的标识,expires = now + ttl。用户分享短码(relay 短码 + 8 位数字,会合协议 §5.4)。 - 查看。 加入方
lookup得到 Offer 并校验:签名与记录有效、record_o.I与签名者一致、authority等于所拨 relay、now < expires、对方不在本机移除记录中、地址组不与自己或已有对端冲突。界面显示对方的节点 ID 与name,用户确认;应用可以提供”指定节点 ID 时自动确认、不匹配时失败”的参数。 - 加入。 加入方
join {id, intro: Answer}。 - 接纳。 创建方从
recv的waiting读取每个 Answer,做与第 2 步相同的校验,并检查mailbox_id与offer_hash。用户admit一个、reject其余(同样可以按指定节点 ID 自动接纳),随后发送 ACCEPT。 - 安装。 创建方发送 ACCEPT 后、加入方校验 ACCEPT 后,各自派生
psk并按lifetime安装对端(§6)。 - 结束。 加入方就绪后发送 COMPLETE 并
close;创建方收到 COMPLETE 后close,或在自己就绪 30 s 后关闭。
5.2 pake
Section titled “5.2 pake”本模式严格采用PAKE 精确 profile:固定 CPace suite、字节编码、时序、两方向完成确认、失败预算和秘密清除。先取得信箱 ID,接纳后交换 nonce 和 Share,加入方先发送 HELLO,创建方验证后才回复;两端完成认证确认后才允许安装/交付。短码最多三次失败尝试,重试刷新全部临时秘密,不复用 Share。
隧道内模式在双向 peer OPEN(mac=null)后由连接方发送 LOCATE,在已唯一确定的本地授权来源内定位邀请,再运行同一 profile。定位值精确等于 app_context,并纳入握手绑定,不能作为来源或授权证明;不同邀请可隔离并发,同一邀请的 attempt 与失败预算统一管理,见邀请定位。握手还绑定 channel_id 与承载流的已认证身份,只增加应用口令门,不安装新 peer、不更换既有 PSK。
5.3 peer
Section titled “5.3 peer”两端已是 ready 对端(已配对,或同一账户内由 NSD 投影)。不使用会合与信箱:发起方直接在隧道内连接对方的 app_port 并发送 OPEN(§7),身份就是承载这条流的 WireGuard 对端。接纳必须同时通过本地 app grant、精确选定来源的有效策略和应用 OPEN 验证;pinhole 只是端口原语,普通宽 ACL 不能绕过应用门,见来源与会话授权。
6. 对端安装
Section titled “6. 对端安装”lifetime | 写入 | AllowedIPs | 入站过滤 | 生命周期 |
|---|---|---|---|---|
permanent | Quick 来源的 Allow {I, added_by: kind, psk} 与对方记录(Quick 协议 v2 §10.1) | N6/128 + N4/128 | 发往本机的任意流量 | 直到移除 |
session | 仅限本次会话的对端:不进允许名单,不分配别名或名字,不留下移除记录 | 只有 N6/128 | 只接受 app_port 上来自 N6(peer) 的 TCP 及其返回流,加上隧道内控制端口 47900 | 直到会话完成、失败或过期;自创建起 ≤ ttl,加上应用报告有进展的时间,最长 24 h |
none | 不安装;使用已有对端 | — | 对端来源命名空间允许的 app_port pinhole,随会话关闭 | 随会话 |
- 已存在的长期对端被同一身份重新
confirm时,只替换 PSK 与记录,保留别名与昵称。 - 从安装起 5 s 内 WireGuard 路径可用并完成本地发布,否则报告
prepare_timed_out;长期对端保留供正常重连,会话对端删除。 - 仅限本次会话的对端计入每个网络 1 024 个对端的上限,另有每个节点默认最多 8 个并发的单独上限;超过任一上限时,通道在会合之前以容量错误失败。
- 两台设备之后经
confirm建立长期关系时,先删除双方之间的会话对端;会话 PSK 从不复用。 - 崩溃遗留的会话对端在节点下次启动时删除。
7. 隧道内入口
Section titled “7. 隧道内入口”有 app_port 的应用,在隧道内的 TCP 流上使用带长度前缀的帧 u32 length ‖ u8 class ‖ u8 type ‖ payload,每帧最大 1 MiB。length 包含 class/type/payload、不包含长度字段本身,最小为 2;class 0 为通道控制(type 0=OPEN、1=PAKE、2=LOCATE),class 1 的 type 由应用独立定义,见精确 framing:
OPEN = [ver = 1, kind text, channel_id bytes16, mac bytes32 | null] mac = HMAC-SHA256(K_confirm, "ns/channel-open/v1" ‖ channel_id ‖ I_sender ‖ I_receiver)- 连接方先发 OPEN,被连接方校验后回一个 OPEN(
I_sender与I_receiver对调),在本地要求的额外 PAKE 完成后(如适用)才交换应用帧。 - 经信箱建立的通道:
channel_id = mailbox_id,mac必须存在;MAC 中的两个身份必须与承载这条流的 WireGuard 对端一致。这样隧道证明对端身份,MAC 证明对端就是完成引入的那一方。 peer通道:channel_id为发起方生成的 16 字节随机值,mac为 null;需要口令时随后用 LOCATE 定位邀请并运行隧道内pake(§5.2)。channel_id 标识本次流,不能代替邀请定位或重置邀请失败预算。kind不匹配、MAC 错误或第一帧不是 class=0/type=0 的 OPEN 时立即关闭连接。
8. 安全性质
Section titled “8. 安全性质”- relay 看到临时参与方密钥、
kind、短码、消息大小与时间。confirm的 Offer / Answer 明文携带双方的签名记录与临时 DH 公钥;pake的身份只出现在加密的 HELLO 中。relay 看不到口令、K_ch、PSK 或应用数据。 - relay 不能伪造 Offer、Answer、ACCEPT 或 HELLO,也不能伪造”已接纳”:加入方只认创建方签名的 ACCEPT 或能解开的 HELLO。
confirm中 relay 用自己的身份替换整份 Offer 或 Answer 时,用户核对的节点 ID 会不一致,这是唯一需要人工的一步;节点 ID 有 96 位,无法碰撞出相同的值。pake中知道短码的 relay 运营方,每次尝试最多在线猜一次口令;第 3 次失败后信箱关闭。失败预算也由创建端本地维护,隧道内按同一邀请累计;口令熵由应用按自身安全需求决定(文件发送约 33 位),不能只信任 relay 的计数。kind进入签名、CPace 的CI与K_ch的派生:为一个应用产生的消息在另一个应用里无法通过校验。- 信箱过期、被拒绝或取消时什么也不安装。
9. 添加一个新应用
Section titled “9. 添加一个新应用”- 选择
kind,以及 §3 中允许的auth与lifetime组合。 - 有隧道内阶段时分配
app_port,并定义 OPEN 之后的应用帧。 - 需要会合时定义票据或短码的界面;口令长度满足自己的安全需求。
- 在 ns 的应用接口中登记 profile 与命令。relay、nsshared、nsplane 都不需要改动。
10. 常量与上下文
Section titled “10. 常量与上下文”| 项目 | 值 |
|---|---|
| 消息版本 | 1 |
| 签名上下文 | ns/channel-offer/v1、ns/channel-answer/v1、ns/channel-accept/v1、ns/channel-hello/v1 |
| 派生标签 | ns/channel-key/v1、ns/channel-psk/v1、ns/channel-hello-o/v1、ns/channel-hello-p/v1、ns/channel-confirm/v1、ns/channel-open/v1 |
| CPace | suite 1:draft-21 X25519/SHA-512;sid/CI 绑定会话标识与双方新鲜 nonce,见精确 profile |
| AEAD | ChaCha20-Poly1305,nonce 0,每个密钥一条消息 |
| 隧道内帧 | u32 length ‖ u8 class ‖ u8 type ‖ payload,最大 1 MiB;class 0 为控制(type 0=OPEN、1=PAKE、2=LOCATE),class 1 为应用,各自独立编号 |
| 会话对端 | 每个节点默认最多 8 个并发;最长 24 h |
签名为 RFC 8032 pure Ed25519,严格校验;被签名的消息是 ASCII 上下文紧接后续字节,没有分隔符(同 Quick 协议 v2 §12.2)。
11. 与原设计的对应
Section titled “11. 与原设计的对应”| 原设计 | 应用通道 |
|---|---|
配对 Invite / Join(ver = 2) | confirm 的 Offer / Answer(ver = 1),增加 kind 字段 |
ns/pairing-invite/v2、ns/pairing-join/v2、ns/pairing-accept/v2 | ns/channel-offer/v1、ns/channel-answer/v1、ns/channel-accept/v1 |
ns/pairing-psk/v2 | ns/channel-key/v1 + ns/channel-psk/v1 |
文件发送 TransferHello | pake 的 HELLO |
ns-transfer/psk/v1、ns-transfer/confirm/v1 | ns/channel-psk/v1、ns/channel-confirm/v1 + 隧道内 OPEN |
ns-transfer/cpace/v1 | ns/channel-cpace/v1 |
12. 待决事项
Section titled “12. 待决事项”- 第三方接入与临时 peer 权限已按应用模式 §3.1界定;NSD 缺少投影 peer 不回退 Quick。实现已在分支推进,完整来源与平台验收尚未完成。
- 语音、聊天的
kind、端口与是否需要pake。
桌面受限 peer 接入
Section titled “桌面受限 peer 接入”最小 owner grant/capability、IPC 封包、超时和关闭契约见应用模式 §3.2。本地 IPC 的 CANCEL 仅是本地控制,不能转发或混用为隧道 LOCATE;隧道 OPEN 仍严格遵循本文的 class/type、规范 CBOR、kind/channel 与 peer 模式 mac=null 规则。