豫言 / 安全外壳服务端互通验证 @ 0.1.0

与 OpenSSH 客户端验证服务端换钥、认证及终端会话。

源码浏览已生成;不能解析的文件以纯文本展示,不表示通过类型检查。

包说明.汉语.md

# 安全外壳服务端互通验证

会话运行期间识别本通道、不要求应答的 `signal`;无 PTY 时仅向登记的子进程发信号,PTY 时仅向登记的控制终端进程组发送。豫言客户端的无 PTY、PTY 模式均验证 `TERM` 与 `INT`,服务端返回对应的 `exit-signal`。未知扩展信号名不执行。

客户端在 exec 应答后连续发送加密 `IGNORE`、`UNIMPLEMENTED`、`DEBUG`,服务端在无 PTY 与 PTY 会话中均继续处理后续 `INT` 请求并回传 `exit-signal`。

在无 PTY 与 PTY 会话中,客户端发送要求应答的未知 `GLOBAL_REQUEST`,服务端返回 `REQUEST_FAILURE` 后继续处理 `INT`;无 PTY 会话还验证了 `want_reply=false` 时直接继续而不发送应答。

创建 `/tmp/yyssh服务端互通/服务端会话控制模式` 后,服务端在命令应答后发送 `IGNORE`、`UNIMPLEMENTED`、`DEBUG` 和不要求应答的未知 `GLOBAL_REQUEST`。再创建 `/tmp/yyssh服务端互通/服务端会话全局待答模式`,服务端另发要求应答的未知全局请求,并验证收到客户端的 `REQUEST_FAILURE`。豫言客户端使用单步会话收包库时,两种模式均完成命令会话;撤去标记后的默认路径也通过。

真实 OpenSSH 客户端以 `RekeyLimit=4K` 在通道运行期间多次发起 KEXINIT:无 PTY 的 `/bin/cat` 会话回传 131072 字节逐字相同;PTY 的 `/bin/cat` 会话在去除终端正常添加的 CR 后,48894 字节与输入逐字相同。两条路径均在多轮重换钥后正常发送退出和关闭报文,服务端正常退出。

服务端标准流和 PTY 双向桥接还按单调时钟检查本轮换钥间隔,默认 3600 秒。创建 `/tmp/yyssh服务端互通/计时重换钥模式` 后改为 1 秒:真实 OpenSSH 客户端执行 `sleep 2; printf YYSSH_SERVER_TIME_OK`,以及 PTY `/bin/cat` 接收延迟两秒的输入,两条路径的客户端日志均记录首次握手和两次运行中重换钥,输出正确,双方正常退出。撤去标记后的 `exit 23` 短会话仅记录首次握手并返回 23。

创建 `/tmp/yyssh服务端互通/会话主动重换钥模式` 后,探针在 exec 应答后发送 KEXINIT。旧钥下先到的通道报文会被有界暂存,并在 NEWKEYS 后按原顺序交给桥接层。豫言客户端无 PTY 小窗口输出和 PTY `/bin/cat` 输入均已通过;真实 OpenSSH 客户端在该模式下既收到预期的命令退出码 23,也用无 PTY `cat` 完成 120000 字节逐字回传。关闭此模式时的普通 OpenSSH 命令路径已回归。

认证成功后,服务端探针可识别客户端主动发来的加密 KEXINIT,使用原有密钥交换第二轮 Curve25519/Ed25519 报文,并在双向 NEWKEYS 边界分别切换密钥和 IV。创建 `/tmp/yyssh服务端互通/主动重换钥模式` 后,服务端会主动发送 KEXINIT;它可保存随后先到的单条 CHANNEL_OPEN,待换钥完成再处理。豫言客户端的两种发起方向及真实 OpenSSH 客户端的服务端主动重协商均已完成命令会话。通道运行期间可处理客户端发起的重协商,也可按任一方向的报文数自动发起,默认阈值为每轮 4096 包。创建 `/tmp/yyssh服务端互通/自动重换钥模式` 后阈值降至 4 包,供互通测试;无 PTY 的 262144 字节回传和 PTY 的 48894 字节回传均逐字一致,OpenSSH 日志分别记录 11 次和 60 次 KEXINIT 接收(含首次握手)。按时间自动换钥尚未接入,在途通道报文有 16 条及 1 MiB 上限。

若有 `/tmp/yyssh服务端互通/主机私钥`,探针优先解析无口令 OpenSSH Ed25519 私钥;缺少时才读取 `主机私种` 原始 32 字节种子。加密或损坏的标准私钥会被拒绝。

PTY、shell 和 exec 请求均按 `want_reply` 处理:为真才发送 CHANNEL_SUCCESS;为假仍执行请求,并直接进入后续会话。豫言客户端已分别验证无 PTY exec 及 PTY 加 exec 的不要求应答路径,真实 OpenSSH 客户端的要求应答 exec 回归也通过。

无 PTY `shell` 请求也已接入同一标准流会话循环:服务端启动当前用户的 `/bin/sh`,接收客户端 stdin,并分别转发输出和错误。真实 OpenSSH 客户端以 `ssh -T` 发送 `echo YUYAN_NO_PTY_SHELL_OK`、`exit`,双方均以 0 退出;新增分流后 `exec` 的双路输出回归也通过。

无 PTY `exec` 路径在认证后校验非空、至多 65536 字节且不含 NUL 的命令,再以当前用户身份运行 `/bin/sh -c`。服务端按加密通道数据写入子进程 stdin,分别把 stdout、stderr 封为 CHANNEL_DATA 和 CHANNEL_EXTENDED_DATA,共用远端窗口,并返回实际 exit-status。真实 OpenSSH 客户端已验证双路输出、`cat` 回传 120894 字节逐字相同,以及 `exit 23` 返回 23。探针仍限回环地址、单连接和有界空闲时间;高权限多用户模式尚未实现。

PTY shell 路径可响应真实 OpenSSH 客户端的 `shell` 请求,启动当前用户的 `/bin/sh`,双向转发命令与输出,并以实际子进程退出码返回 exit-status。已验证输入 `echo YUYAN_SHELL_OK`、`exit` 后客户端与服务端均以 0 退出。`cat` 和 `tty` 路径仍分别通过双向与单向转发回归。

若存在 `/tmp/yyssh服务端互通/authorized_keys`,探针优先从其中逐行查找请求所呈的 Ed25519 公钥;可列出多把钥。`no-pty`、`restrict` 等受支持限制会随认证结果传入会话,终端分配请求收到 CHANNEL_FAILURE 和 CHANNEL_CLOSE;无 PTY 的命令仍可运行。真实 OpenSSH 客户端已验证这两条路径。其他前置选项不授权。缺少此文件时继续使用下述 `.pub` 或 32 字节单钥文件。

若存在 `/tmp/yyssh服务端互通/获准公钥.pub`,探针从单行 OpenSSH `ssh-ed25519` 公钥文本读取授权钥;否则沿用 `/tmp/yyssh服务端互通/获准公钥` 的 32 字节原始公钥。真实 OpenSSH 客户端用 `ssh-keygen` 生成的 `.pub` 文件认证已通过。

`/bin/cat` 路径调用 `安全外壳会话/双向终端。豫`,以同一次多路等待交错推进 TCP 收包、PTY 待读写和窗口增量;只按实际写入 PTY 的字节回补接收窗口,输出则受客户端窗口约束。PTY 从端关闭回显但保留规范行模式。真实 OpenSSH 客户端发送 22,000 条编号记录(132,000 字节,超过两倍初始接收窗口)后,全部记录按序各回传一次,无缺号或重复。

豫言客户端探针另以 64 字节和 1 字节初始接收窗口请求 PTY `cat`;服务端分别完整回传 1,000 字节与 5 字节。1 字节窗口使服务端每发送一个字节就必须等待下一次 WINDOW_ADJUST。

服务端现用单步非阻塞加密收包库保留半包、余帧与接收 IV。OpenSSH 默认在 `exec` 前发送数条 `env` 通道请求;探针严格解析这些请求但不应用环境变量,若要求应答则回 CHANNEL_FAILURE。无 PTY 的 `true`、PTY 的 `/usr/bin/tty` 和 `/bin/cat` 路径都已与真实 OpenSSH 客户端通过;PTY 路径也在发送 CLOSE 后等待客户端 CLOSE,再关闭 TCP。

认证成功后,探针还会发送一个未知且要求应答的 `probe@example.com` 全局请求;它允许 CHANNEL_OPEN 与 REQUEST_FAILURE 按任一顺序到达,验证两者后再确认通道。固定 `true` 路径发送退出状态、EOF 和 CLOSE 后,会等客户端回送 CHANNEL_CLOSE 再关闭 TCP。真实 OpenSSH 客户端和豫言客户端均已完成此流程。

此一次性测试程序监听 `127.0.0.1:22222`,读取标准主机私钥或原始种子、`用户名` 以及 `authorized_keys`、`.pub` 或 32 字节 `获准公钥`。它与 OpenSSH 客户端交换版本、KEXINIT、Curve25519 回报和 NEWKEYS,接受加密的 `ssh-userauth` 服务请求,然后拒绝 `none` 认证。公钥认证循环最多处理 12 次请求:未授权请求收到 USERAUTH_FAILURE,获准公钥试探收到 PK_OK,只有指定用户、服务和会话号上的有效 Ed25519 签名才收到 USERAUTH_SUCCESS;第 12 次失败则发送加密 DISCONNECT,原因码 14。真实 OpenSSH 客户端使用两把临时密钥时,先收到未授权密钥的失败,再以第二把获准密钥登录;仅用 12 把错误密钥时收到明确的 DISCONNECT。两种结局下探针均关闭连接和监听端并正常退出;成功后才解析 session 通道开启。无 PTY 时执行获准用户所请的 `exec` 命令,发送请求成功、exit-status 0、EOF 和 CLOSE,并以通道寿命状态等待客户端 CLOSE,拒绝重复 EOF。收到 PTY 请求时接受 `shell` 请求启动当前用户的 `/bin/sh`,或接受受限的 `/usr/bin/tty` 与 `/bin/cat` 的 exec 请求,启动当前用户的真实控制终端子进程,并按客户端窗口将 PTY 输出转发为 CHANNEL_DATA。`cat` 路径把客户端 CHANNEL_DATA 写入 PTY,对部分写入的尾部等待可写并限次重试,按累计实际写入量发送 WINDOW_ADJUST,客户端 EOF 后向终端写入 EOT。真实 OpenSSH 客户端在四种路径下均以 0 退出;`tty` 返回实际 `/dev/ttys...`,`cat` 回传测试文本。cat 路径可处理经验证的多轮接收窗口数据;探针仍限定单连接、受限 exec 命令和有界空闲时间,尚不支持通用长期会话。测试须预置匹配的主机公钥并严格校验,结束后删除临时私钥。

包说明.文言.md

# 安全外壳服务端互通验证

会话行中识本通道不待答 `signal`;无 PTY 惟向在籍子进程发之,PTY 惟向在籍控制终端进程组发之。豫言客二式皆验 `TERM`、`INT` 并得相应 `exit-signal`。未知扩展名不施。

客户于 exec 应答后连发加密 `IGNORE`、`UNIMPLEMENTED`、`DEBUG`,服务者于无 PTY 及 PTY 会话皆续治其后 `INT` 请,返 `exit-signal`。

客户于无 PTY 与 PTY 会话发待答之未知 `GLOBAL_REQUEST`,服务者回 `REQUEST_FAILURE` 后续治 `INT`;无 PTY 亦验不待答之请径续而不回报。

置 `/tmp/yyssh服务端互通/服务端会话控制模式`,服务者于命令应答后发 `IGNORE`、`UNIMPLEMENTED`、`DEBUG` 及不待答未知 `GLOBAL_REQUEST`。复置 `/tmp/yyssh服务端互通/服务端会话全局待答模式`,别发待答未知全局请,核客户回 `REQUEST_FAILURE`。豫言客户用单步会话收报术,二式皆继成命令会话;去旗之常路亦通。

真 OpenSSH 客以 `RekeyLimit=4K` 于通道行中屡发 KEXINIT:无 PTY `/bin/cat` 回十三万一千七十二字,与输入逐字同;PTY `/bin/cat` 去终端常添之 CR 后,四万八千八百九十四字亦逐字同。二途皆于多轮易钥后常发退出与关闭报,服务者常退。

服务者常流及 PTY 双向桥亦察单调钟,自本轮易钥起常隔三千六百秒。置 `/tmp/yyssh服务端互通/计时重换钥模式` 则限一秒;真 OpenSSH 客行 `sleep 2; printf YYSSH_SERVER_TIME_OK`,及 PTY `/bin/cat` 延二秒得输入,二途客户日志皆见首握与运行中再议二回,回文正确,双方常退。去旗之 `exit 23` 短会惟见首握,退码二十三。

置 `/tmp/yyssh服务端互通/会话主动重换钥模式`,探针于 exec 应答后送 KEXINIT。旧钥下先至之通道报有界暂存,于 NEWKEYS 后循序交桥。豫言客户之无 PTY 微窗输出与 PTY `/bin/cat` 输入皆通;真 OpenSSH 客于此式既得预期退码二十三,亦以无 PTY `cat` 回十二万字,逐字同输入。去此旗之常式 OpenSSH 命令亦复验。

认证既成,服务者探针可识客户主动所送加密 KEXINIT,复以曲线、Ed25519 行第二轮换钥,于双向 NEWKEYS 之界各易钥与 IV。置 `/tmp/yyssh服务端互通/主动重换钥模式`,服务者乃主动送 KEXINIT;若单条 CHANNEL_OPEN 先至,暂存而于换钥后行之。豫言客户之二发起方向与真 OpenSSH 客对服务者主动再议,皆继成命令会话。通道行中既可治客户所发再议,亦可依任一向报数自发;常限每轮四千零九十六报。置 `/tmp/yyssh服务端互通/自动重换钥模式` 则限四报,以供互通试。无 PTY 回二十六万二千一百四十四字、PTY 回四万八千八百九十四字皆同输入;真 OpenSSH 日志分别见十一、六十次 KEXINIT 接收,含首次握手。依时易钥尚未备,在途通道报限十六条、共一兆字。

若有 `/tmp/yyssh服务端互通/主机私钥`,探针先析无口令 OpenSSH Ed25519 私钥;无之乃读 `主机私种` 原三十二字。加密及坏常式钥皆拒。

PTY、shell、exec 诸请皆循 `want_reply`:阳乃报 CHANNEL_SUCCESS,阴亦行其请而径入后会。豫言客已验无 PTY exec 及 PTY 连 exec 之不待答路;真 OpenSSH 客之待答 exec 亦复验通过。

无 PTY `shell` 请亦入同一标准流会话循环:服务者起当前用户 `/bin/sh`,收客户 stdin,别转常出与误出。真 OpenSSH 客以 `ssh -T` 送 `echo YUYAN_NO_PTY_SHELL_OK`、`exit`,双方皆零退;添此分流后,`exec` 二出之回归亦过。

无 PTY `exec` 路认证后核命令非空、至多六万五千五百三十六字节且无内零,再以当前用户身行 `/bin/sh -c`。服务者以加密通道文写子进程 stdin,别封 stdout、stderr 为 CHANNEL_DATA、CHANNEL_EXTENDED_DATA,同耗彼窗,返实 exit-status。已与真 OpenSSH 客验二出分流、`cat` 回文一十二万零八百九十四字逐字皆同,及 `exit 23` 得二十三。探针仍限回环、单连、有界空闲;高权限多用户制未备。

PTY shell 路受真 OpenSSH 客之 `shell` 请,起当前用户 `/bin/sh`,双向转令文与输出,依子进程实退码报 exit-status。已验输入 `echo YUYAN_SHELL_OK`、`exit`,客与服务者皆零退。`cat`、`tty` 二路亦各复验双向、单向转发。

若有 `/tmp/yyssh服务端互通/authorized_keys`,探针先逐行寻请求所呈 Ed25519 钥,可列多钥。`no-pty`、`restrict` 等可行之限随认证入会话;终端请获 CHANNEL_FAILURE 与 CHANNEL_CLOSE,无 PTY 命令仍可行,真 OpenSSH 客二路皆已验。余前置选项不授登。此文若无,仍循下述 `.pub` 或三十二字单钥文。

若有 `/tmp/yyssh服务端互通/获准公钥.pub`,探针自单行 OpenSSH `ssh-ed25519` 公钥文取获准钥;否则循 `/tmp/yyssh服务端互通/获准公钥` 之三十二原字公钥。真 OpenSSH 客用 `ssh-keygen` 所造 `.pub` 文件认证已通过。

`/bin/cat` 路今调用「安全外壳会话/双向终端。豫」,一候网连与 PTY,交治收报、终端读写及增窗;己窗惟依 PTY 实写字数回补,出文则守彼窗。从端禁回显而存规范行。真 OpenSSH 客送二万二千编号行,计十三万二千字节,逾初收窗二倍;诸行皆按序各回一回,无缺无重。

豫言客探针另以六十四字节与一字节初收窗请 PTY `cat`;服务者分别全回一千字节与五字节。一字节窗每发一字即尽,须候下一次 WINDOW_ADJUST 方续。

服务者今以非阻塞单步收加密报,存半报、余帧及收 IV。OpenSSH 于 `exec` 前常发数条 `env` 通道请;探针严析而不施环境,待答则回 CHANNEL_FAILURE。无 PTY 之 `true`、有 PTY 之 `/usr/bin/tty` 与 `/bin/cat` 皆已与真 OpenSSH 客互通;PTY 路发 CLOSE 后亦候彼 CLOSE,乃闭 TCP。

无 PTY 之 `true` 路发己 EOF/CLOSE 后,以通道寿命态候彼 CLOSE;彼 EOF 重至则拒,二端皆闭方止 TCP。

认证成后,探针发未知而待答之 `probe@example.com` 全局请求;CHANNEL_OPEN 与 REQUEST_FAILURE 先后皆可,二者既验乃确认通道。固定 `true` 路发退出状态、EOF、CLOSE 后,候客户回 CHANNEL_CLOSE 始闭 TCP。真 OpenSSH 客与豫言客皆已行此程。

此一次测试程序听 `127.0.0.1:22222`,读 `/tmp/yyssh服务端互通/主机私种` 之 Ed25519 私种、`用户名` 及 `authorized_keys`、`.pub` 或 `获准公钥`。与 OpenSSH 客互通版本、KEXINIT、Curve25519、NEWKEYS 及加密服务请求;拒 `none`。公钥认证至多试十二次:非获准者回 USERAUTH_FAILURE,获准试探回 PK_OK,惟用户、服务、会话号及 Ed25519 签皆合,乃发 USERAUTH_SUCCESS;第十二次败则发加密 DISCONNECT,原因码十四。OpenSSH 客先试未授权钥获拒,继以获准钥登成;惟试十二错钥则得明确断开报。两途探针皆闭连接及监听而常退;惟认证成者继验 session 开通。无 PTY 时行获准用户所请 `exec` 命令,有 PTY 时受 shell 请起 `/bin/sh`,或许 `/usr/bin/tty` 与 `/bin/cat` 之 exec,起当前用户之真控制终端子进程,依彼窗转发其输出。`cat` 路收客户 CHANNEL_DATA 入 PTY,部分写入则候可写限次续冲,按累计实写数回增窗;遇客户 EOF,则写 EOT 于终端。上述测试四路皆报退出状态零,OpenSSH 客亦以零退出;`tty` 实回 `/dev/ttys...`,`cat` 回测试文。cat 路已验多轮收窗之文;探针仍限单连、受限 exec 及有界空闲,未纳通用久会话。测试须预置相合主公钥而严验之;毕则删暂私钥。