HRR/0-RTT 基础设施: 内部字段、early_data 扩展、key_share 单组模式与 early traffic key 派生; 修复 key_share_client 参数不匹配编译错误; STATUS 命令表改为独立命令名
这个提交包含在:
@@ -58,14 +58,14 @@
|
||||
|
||||
| OpenSSH | PazeSSH | 说明 |
|
||||
|---|---|---|
|
||||
| `ssh` | `pssh` / `pssh ssh` | 远程 shell / 执行命令 / 端口转发 |
|
||||
| `scp` | `pssh scp` | SFTP 优先 + legacy SCP 回退 |
|
||||
| `sftp` | `pssh sshd` 子系统 | 服务端 SFTP v3,互操作通过 |
|
||||
| `ssh-keygen` | `pssh keygen` | ed25519/rsa/ecdsa |
|
||||
| `ssh-keyscan` | `pssh keyscan` | known_hosts 格式 |
|
||||
| `ssh-copy-id` | `pssh copy-id` | 安装公钥 |
|
||||
| `ssh-agent` / `ssh-add` | `pssh agent` / `add` | agent 密钥管理 |
|
||||
| `sshd` | `pssh sshd` | 多用户 + 权限 + 公钥认证 + SFTP |
|
||||
| `ssh` | `pssh` | 远程 shell / 执行命令 / 端口转发 |
|
||||
| `scp` | `pscp` | SFTP 优先 + legacy SCP 回退 |
|
||||
| `sftp` | `psftp` | 独立 SFTP 客户端;服务端为 `psshd` 子系统 |
|
||||
| `ssh-keygen` | `pkeygen` | ed25519/rsa/ecdsa |
|
||||
| `ssh-keyscan` | `pkeyscan` | known_hosts 格式 |
|
||||
| `ssh-copy-id` | `pcopy-id` | 安装公钥 |
|
||||
| `ssh-agent` / `ssh-add` | `pagent` / `padd` | agent 密钥管理 |
|
||||
| `sshd` | `psshd` | 多用户 + 权限 + 公钥认证 + SFTP |
|
||||
|
||||
## 遗留项(已知)
|
||||
|
||||
|
||||
+30
-10
@@ -120,7 +120,8 @@ size_t paze_tls_ext_write_signature_algorithms(uint8_t *buf) {
|
||||
/* ============================================================
|
||||
* key_share (client: 多 share; server: 单 share; HelloRetryRequest: 单 selected_group)
|
||||
* ============================================================ */
|
||||
size_t paze_tls_ext_write_key_share_client(uint8_t *buf, const paze_tls_kex_t *kex) {
|
||||
size_t paze_tls_ext_write_key_share_client(uint8_t *buf, const paze_tls_kex_t *kex,
|
||||
uint16_t only_group) {
|
||||
uint8_t *p = buf + 4;
|
||||
uint8_t *s = p;
|
||||
size_t bodylen = 0;
|
||||
@@ -128,19 +129,23 @@ size_t paze_tls_ext_write_key_share_client(uint8_t *buf, const paze_tls_kex_t *k
|
||||
|
||||
if (kex->kind == PAZE_KEX_NONE) return 0; /* 至少 1 个 share */
|
||||
|
||||
/* X25519 share */
|
||||
if (kex->kind == PAZE_KEX_X25519 || kex->kind == PAZE_KEX_P256) {
|
||||
/* 先写 X25519 */
|
||||
/* HRR 后(only_group != 0):只发指定组的 share(RFC 8446 §4.1.4)。
|
||||
* 否则按 kind 全量发送(首次 CH:尽可能避免 HRR 往返)。 */
|
||||
if (only_group == PAZE_TLS_GROUP_X25519 ||
|
||||
(only_group == 0 && (kex->kind == PAZE_KEX_X25519 || kex->kind == PAZE_KEX_P256))) {
|
||||
/* X25519 share */
|
||||
paze_store16_be(p, PAZE_TLS_GROUP_X25519); p += 2;
|
||||
paze_store16_be(p, 32); p += 2;
|
||||
memcpy(p, kex->x25519_pub, 32); p += 32;
|
||||
/* 如存在 P-256,也写上(备选) */
|
||||
if (kex->kind == PAZE_KEX_P256) {
|
||||
paze_store16_be(p, PAZE_TLS_GROUP_SECP256R1); p += 2;
|
||||
paze_store16_be(p, 65); p += 2;
|
||||
memcpy(p, kex->p256_pub, 65); p += 65;
|
||||
}
|
||||
}
|
||||
if (only_group == PAZE_TLS_GROUP_SECP256R1 ||
|
||||
(only_group == 0 && kex->kind == PAZE_KEX_P256)) {
|
||||
/* P-256 share(仅 kind==P256 或 HRR 选定组时) */
|
||||
paze_store16_be(p, PAZE_TLS_GROUP_SECP256R1); p += 2;
|
||||
paze_store16_be(p, 65); p += 2;
|
||||
memcpy(p, kex->p256_pub, 65); p += 65;
|
||||
}
|
||||
if (p == s + 2) return 0; /* 无 share 可写 */
|
||||
bodylen = (size_t)(p - s);
|
||||
paze_store16_be(s, (uint16_t)(bodylen - 2)); /* client_shares 长度 */
|
||||
return put_ext_header(buf, PAZE_TLS_EXT_KEY_SHARE, bodylen) + bodylen;
|
||||
@@ -279,6 +284,21 @@ size_t paze_tls_ext_write_renegotiation_info(uint8_t *buf) {
|
||||
return put_ext_header(buf, PAZE_TLS_EXT_RENEGOTIATION_INFO, body) + body;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* early_data (RFC 8446 §4.2.10)
|
||||
* ClientHello: 空 body(声明客户端支持 0-RTT)
|
||||
* EncryptedExtensions: 空 body(服务端接受 0-RTT)
|
||||
* ============================================================ */
|
||||
size_t paze_tls_ext_write_early_data(uint8_t *buf) {
|
||||
return put_ext_header(buf, PAZE_TLS_EXT_EARLY_DATA, 0);
|
||||
}
|
||||
|
||||
/* 解析 early_data 扩展:存在即返回 1(body 应为空,不校验) */
|
||||
int paze_tls_ext_parse_early_data(const uint8_t *data, size_t len) {
|
||||
(void)data; (void)len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* PSK 会话恢复扩展 (RFC 8446 §4.2.11)
|
||||
* ============================================================ */
|
||||
|
||||
@@ -162,7 +162,7 @@ paze_status_t paze_tls13_build_ch(paze_tls_t *tls, uint8_t *out, size_t *outlen)
|
||||
n = paze_tls_ext_write_supported_versions(p, 1, cfg->enable_1_3, cfg->enable_1_2); p += n;
|
||||
n = paze_tls_ext_write_supported_groups(p); p += n;
|
||||
n = paze_tls_ext_write_signature_algorithms(p); p += n;
|
||||
n = paze_tls_ext_write_key_share_client(p, &tls->kex);
|
||||
n = paze_tls_ext_write_key_share_client(p, &tls->kex, tls->had_hrr ? tls->hrr_group : 0);
|
||||
if (n == 0) return PAZE_ERR_INVALID;
|
||||
p += n;
|
||||
n = paze_tls_ext_write_sni(p, cfg->sni); p += n;
|
||||
|
||||
+32
-1
@@ -129,6 +129,9 @@ struct paze_tls_config {
|
||||
* 即可实现跨连接会话恢复。 */
|
||||
uint8_t ticket_key[32];
|
||||
int ticket_key_ready;
|
||||
|
||||
/* 0-RTT: 客户端启用(服务端默认接受,1=接受) */
|
||||
int enable_early;
|
||||
};
|
||||
|
||||
/* ============================================================
|
||||
@@ -245,6 +248,26 @@ struct paze_tls {
|
||||
uint8_t *ch1; size_t ch1_len;
|
||||
int hello_retry;
|
||||
|
||||
/* HRR(HelloRetryRequest,RFC 8446 §4.1.3/§4.1.4) */
|
||||
uint8_t *hrr_raw; size_t hrr_raw_len; /* 服务端发送的 / 客户端收到的 HRR 原始 */
|
||||
uint8_t *ch2; size_t ch2_len; /* 客户端 HRR 后发送的 ClientHello2 原始 */
|
||||
int had_hrr; /* 本次握手是否发生 HRR */
|
||||
uint16_t hrr_group; /* HRR 选择的组 */
|
||||
int sent_hrr; /* 服务端已发送 HRR(等待 CH2) */
|
||||
|
||||
/* 0-RTT 状态(RFC 8446 §4.2.10/§4.5) */
|
||||
int early_offer; /* 客户端 CH 携带 early_data 扩展 */
|
||||
int early_sent; /* 客户端已发送 0-RTT 数据 */
|
||||
int early_accepted; /* 服务端 EE 含 early_data(客户端视角) */
|
||||
int early_accept; /* 服务端接受 0-RTT(服务端视角) */
|
||||
paze_tls_traffic_key_t early_read;
|
||||
paze_tls_traffic_key_t early_write;
|
||||
int early_read_valid;
|
||||
int early_write_valid;
|
||||
uint8_t early_buf[2048]; /* 服务端收到的 0-RTT 数据(握手完成后交应用) */
|
||||
size_t early_len;
|
||||
size_t early_off;
|
||||
|
||||
/* 客户端:TLS 1.2 回退时保存的 ServerHello 原始消息 */
|
||||
uint8_t *sh1; size_t sh1_len;
|
||||
|
||||
@@ -328,6 +351,11 @@ paze_status_t paze_tls_derive_secret(const paze_hash_alg_t *alg,
|
||||
paze_status_t paze_tls13_derive_handshake_keys(paze_tls_t *tls,
|
||||
const uint8_t *shared, size_t shared_len);
|
||||
paze_status_t paze_tls13_derive_app_keys(paze_tls_t *tls);
|
||||
/* 0-RTT early traffic key:
|
||||
* client: 由 early_secret + hash(CH) 派生 "c e traffic" → early_write
|
||||
* server: 同上 → early_read
|
||||
* is_client 决定写入哪个方向;aead 由调用方从协商/首选套件决定。 */
|
||||
paze_status_t paze_tls13_derive_early_key(paze_tls_t *tls, int is_client);
|
||||
/* 由 traffic secret 派生 traffic key 对象 */
|
||||
paze_status_t paze_tls13_make_traffic_key(paze_tls_traffic_key_t *tk,
|
||||
const paze_hash_alg_t *alg,
|
||||
@@ -367,10 +395,13 @@ paze_status_t paze_tls12_prf(const paze_hash_alg_t *alg,
|
||||
size_t paze_tls_ext_write_supported_versions(uint8_t *buf, int is_client, int v13, int v12);
|
||||
size_t paze_tls_ext_write_supported_groups(uint8_t *buf);
|
||||
size_t paze_tls_ext_write_signature_algorithms(uint8_t *buf);
|
||||
size_t paze_tls_ext_write_key_share_client(uint8_t *buf, const paze_tls_kex_t *kex);
|
||||
size_t paze_tls_ext_write_key_share_client(uint8_t *buf, const paze_tls_kex_t *kex,
|
||||
uint16_t only_group);
|
||||
size_t paze_tls_ext_write_sni(uint8_t *buf, const char *host);
|
||||
size_t paze_tls_ext_write_alpn(uint8_t *buf, char *const *alpn, int n);
|
||||
size_t paze_tls_ext_write_renegotiation_info(uint8_t *buf);
|
||||
size_t paze_tls_ext_write_early_data(uint8_t *buf);
|
||||
int paze_tls_ext_parse_early_data(const uint8_t *data, size_t len);
|
||||
|
||||
/* 服务端:构造 key_share 扩展(单 group) */
|
||||
size_t paze_tls_ext_write_key_share_server(uint8_t *buf, uint16_t group,
|
||||
|
||||
@@ -243,6 +243,43 @@ paze_status_t paze_tls13_psk_early_secret(paze_tls_t *tls,
|
||||
return paze_tls_derive_secret(H, tls->early_secret, hl, "res binder", NULL, tls->binder_key);
|
||||
}
|
||||
|
||||
/* 0-RTT early traffic key (RFC 8446 §7.1):
|
||||
* client_early_traffic_secret = Derive-Secret(early_secret, "c e traffic", hash(CH))
|
||||
* → client 侧为 early_write;server 侧为 early_read。
|
||||
* hash 用 PSK 关联 hash;transcript 取"最终 ClientHello"的哈希(直接用原始字节算,
|
||||
* 因为 early_secret 的 hash 可能不同于 transcript 对象的协商 hash)。
|
||||
* aead 由调用方经 tls->neg.aead(已协商)或客户端首选套件决定。 */
|
||||
paze_status_t paze_tls13_derive_early_key(paze_tls_t *tls, int is_client) {
|
||||
const paze_hash_alg_t *H = psk_hash_of(tls->psk_hash);
|
||||
size_t hl = H->digest_len;
|
||||
const uint8_t *ch = tls->ch1 ? tls->ch1 : NULL;
|
||||
size_t chl = tls->ch1 ? tls->ch1_len : 0;
|
||||
/* HRR 后:early key 基于最终 ClientHello(本实现 HRR 时客户端放弃 0-RTT,
|
||||
* 服务端收到 CH2 时 ch1 仍指向 CH1;此处客户端不会调用)。 */
|
||||
if (tls->had_hrr && tls->ch2 && is_client == 0) { ch = tls->ch2; chl = tls->ch2_len; }
|
||||
if (!ch || chl == 0) return PAZE_ERR_PROTOCOL;
|
||||
|
||||
uint8_t th[64];
|
||||
if (H->digest_len == 48) paze_sha384(ch, chl, th);
|
||||
else paze_sha256(ch, chl, th);
|
||||
|
||||
uint8_t c_e[64];
|
||||
paze_status_t st = paze_tls_hkdf_expand_label(H, tls->early_secret, hl,
|
||||
"c e traffic", th, hl, c_e, hl);
|
||||
if (st != PAZE_OK) return st;
|
||||
/* 客户端在收到 ServerHello 前派生:aead 用 CH 首选套件(1301 → AES-128-GCM) */
|
||||
paze_tls_aead_kind_t aead = tls->neg.aead;
|
||||
if (is_client && aead == PAZE_AEAD_NONE) aead = PAZE_AEAD_AES_128_GCM;
|
||||
st = paze_tls13_make_traffic_key(is_client ? &tls->early_write : &tls->early_read,
|
||||
H, c_e, hl, aead);
|
||||
paze_memzero(c_e, hl);
|
||||
if (st == PAZE_OK) {
|
||||
if (is_client) tls->early_write_valid = 1;
|
||||
else tls->early_read_valid = 1;
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
/* binder = HMAC(binder_key, transcript_hash); 输出长度 = H->digest_len */
|
||||
paze_status_t paze_tls13_compute_binder(paze_tls_t *tls,
|
||||
const uint8_t *tr_hash, size_t tr_hash_len,
|
||||
|
||||
在新工单中引用
屏蔽一个用户