实现 TLS 1.2 握手与版本回退,修复 GCM 记录层 RFC 偏差
- tls_handshake12.c: 完整 TLS 1.2 client/server 状态机(ECDHE-RSA/ECDSA/RSA AES-GCM + CHACHA20-POLY1305),支持 1.3 收到 1.2 ServerHello/ClientHello 后回退 - tls_record.c: 修正 TLS 1.2 AEAD-GCM 两处 RFC 偏差: AAD length 字段应为明文长度 ctlen(RFC 5246 6.2.3.3)原为 ct+16; GCMNonce = fixed_iv(4) || explicit_nonce(8)(RFC 5288 3)原顺序相反 - tls_handshake13.c: CV/Finished transcript 处理、1.3 套件记录版本修正、链式证书构造 - x509.c: AttributeTypeAndValue 补 SEQUENCE; validity 2050 年前用 UTCTime - apps/pazessl: s_client/s_server 增加 -tls1_2 - 新增 build.ps1、build/run_tls_test.ps1、build/run_openssl_interop.ps1 验证: 自测 4/4(1.2/1.3 双向 + 回退); OpenSSL 互操作 4/4(s_client/s_server 双向 1.2/1.3)
这个提交包含在:
@@ -1,6 +1,6 @@
|
||||
# PazeSSL / PazeSSH 项目代理规范 (AGENT.MD)
|
||||
|
||||
> **版本**: v0.4 (2026-08-11) — X.509 RSA round-trip 修复 + PEM 密钥加载完成 + 多证书链支持
|
||||
> **版本**: v0.5 (2026-08-13) — TLS 1.2 完成(版本回退 + OpenSSL 互操作 4/4)+ X.509 RSA round-trip 修复 + PEM 密钥加载 + 多证书链支持
|
||||
> **目标**: 用纯 libc + 自底向上 C 语言实现 **OpenSSL(平替 PazeSSL) ≤ 100,000 行** 与 **OpenSSH(平替 PazeSSH) ≤ 30,000 行**。
|
||||
> **约束**: 仅使用libc 要能在裸机上实现,**禁止**依赖 OpenSSL / LibreSSL / libcrypto / mbedTLS / libgcrypt / libssh 等外部密码学或协议库。
|
||||
|
||||
@@ -102,7 +102,7 @@ paze_tls_free(tls);
|
||||
| tls_keys.c | ✅ 完成 | HKDF-Expand-Label / Derive-Secret / 1.3 key schedule / 1.2 PRF / cipher_params |
|
||||
| tls_extensions.c | ✅ 完成 | SNI/ALPN/key_share/supported_groups/sig_algos/supported_versions/renegotiation_info 编码+解析 |
|
||||
| tls_handshake13.c | ✅ 完成 | TLS 1.3 client+server 状态机:ClientHello/ServerHello/EncryptedExtensions/Certificate/CertificateVerify/Finished (无 PSK/0-RTT) |
|
||||
| tls_handshake12.c | ⬜ 占位 | TLS 1.2 回退(返回 PAZE_ERR_NOTSUP) |
|
||||
| tls_handshake12.c | ✅ 完成 | TLS 1.2 client+server 状态机:ServerHello/Certificate/ServerKeyExchange/CKE/CCS/Finished;含 1.3↔1.2 回退 |
|
||||
| tls_client.c | ✅ 完成 | 客户端证书验证辅助:链验证+主机名匹配 |
|
||||
| tls_server.c | ✅ 完成 | 服务端证书有效性检查 |
|
||||
| tls_conn.c | ✅ 完成 | paze_tls_t 生命周期(new/free/set_bio/read/write/close/handshake) |
|
||||
@@ -138,7 +138,7 @@ Finished: verify_data = HMAC(finished_key, Transcript-Hash)
|
||||
### 3.4 关键 AEAD / Key Schedule 不变量
|
||||
- **TLS 1.3 nonce**: `iv[0..11] XOR (00000000 || seq_be64)`,**不重复使用**。
|
||||
- **TLS 1.3 AAD**: `type || 0x0303 || (plaintext_len+16)`。
|
||||
- **TLS 1.2 AAD**: `seq(8) || type || version || (ct_len+16)`,explicit nonce = seq(8),fixed_iv=key_material 中 write_IV(4 字节)。
|
||||
- **TLS 1.2 AAD**: `seq(8) || type || version || (明文长度)`,explicit nonce(8) 写在记录 body 开头,nonce = `fixed_iv(4) || explicit_nonce(8)`(RFC 5288 §3 GCMNonce),fixed_iv=key_material 中 write_IV(4 字节)。
|
||||
- **Transcript**: 每次发送/收到握手消息后,**连同 4 字节握手头**一并 update。
|
||||
- **HRR 回退**:调用 `paze_tls_tr_reset_to_synthetic()` 重置 transcript 为 `MessageHash(Hash(CH1))`,随后 HRR、CH2、SH… 追加其 后。
|
||||
- **PSS/RSA-PKCS1 签名**:由 `paze_rsa_pss_sign` / `paze_rsa_pkcs1_sign` 直接产出 signature 字节流。
|
||||
@@ -361,7 +361,7 @@ int main(void) {
|
||||
|
||||
### 遗留问题 (pre-existing, 非本次引入)
|
||||
|
||||
11. ⬜ tls_handshake12 (TLS 1.2 回退,当前返回 PAZE_ERR_NOTSUP — 预算允许时补充)
|
||||
11. ✅ tls_handshake12 (TLS 1.2 回退,ECDHE-RSA/ECDSA/RSA AES-GCM + CHACHA20-POLY1305;OpenSSL s_client/s_server 双向 1.2 互操作通过)
|
||||
12. 🔶 X.509 self-signed 证书解析 — RSA 部分已修复(X.509 SPKI 外层 SEQUENCE 缺失问题), ECDSA 仍失败(p256_from_affine 曲线点验证不通过)
|
||||
13. ⬜ X25519 ECDH 共享密钥不一致 (RFC 7748 标准测试向量不通过)
|
||||
14. ✅ SSH 密钥解析 — paze_rsa_priv_from_pem / paze_ecdsa_priv_from_pem 已实现, tls_conn 私钥加载已更新
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
| 层 | 模块 | 状态 |
|
||||
|---|---|---|
|
||||
| 原语层 | `src/crypto` — 哈希/对称/非对称/编码/ASN.1/X.509 | ✅ 完整实现 |
|
||||
| 协议层 | `src/ssl` — TLS 1.2 / 1.3 | 🔶 TLS 1.3 完成;TLS 1.2 未实现(返回 NOTSUP) |
|
||||
| 协议层 | `src/ssl` — TLS 1.2 / 1.3 | ✅ TLS 1.2 + 1.3 完整实现(含版本回退与 OpenSSL 互操作验证) |
|
||||
| 协议层 | `src/ssh` — SSH-2 transport/kex/auth/channel + SFTP | ✅ 完整实现 |
|
||||
| 应用层 | `apps/*` — 11 个 CLI | ✅ 可用 |
|
||||
| 测试 | ~~tests/*~~ | ❌ 已从仓库移除(KAT 本地化) |
|
||||
@@ -24,8 +24,9 @@
|
||||
## PazeSSL — TLS(🔶 部分完成)
|
||||
|
||||
- ✅ TLS 1.3 客户端/服务端状态机(record、handshake、key schedule、扩展)
|
||||
- ✅ TLS 1.2 客户端/服务端状态机 + 1.3↔1.2 版本回退(ECDHE-RSA/ECDSA/RSA AES-GCM、CHACHA20-POLY1305)
|
||||
- ✅ OpenSSL 互操作对测 4/4:s_server↔s_client 双向 TLS 1.3 / TLS 1.2(`build/run_openssl_interop.ps1`)
|
||||
- ✅ 证书链与信任锚加载、RSA/ECDSA 私钥 PEM 解析
|
||||
- ⬜ TLS 1.2 回退(`tls_handshake12` 当前返回 NOTSUP)
|
||||
- ⬜ TLS 1.3 服务端 SHA-384 密码套件握手重放(SHA-256 已覆盖)
|
||||
- ⬜ PSK / 0-RTT / HRR 实际数据路径
|
||||
|
||||
@@ -66,6 +67,5 @@
|
||||
|
||||
## 遗留项(已知)
|
||||
|
||||
- TLS 1.2 回退未实现
|
||||
- X.509 ECDSA 证书解析仍失败(P-256 曲线点验证)
|
||||
- TLS 1.3 SHA-384 服务端握手重放、PSK/0-RTT 未覆盖
|
||||
|
||||
+12
-1
@@ -177,7 +177,18 @@ int pssh_cmd_copyid(int argc, char **argv) {
|
||||
const uint8_t *blob = ssh_privkey_pubblob(k, &blen);
|
||||
char b64[8192];
|
||||
paze_base64_encode(blob, blen, b64);
|
||||
snprintf(keyline, sizeof(keyline), "%s %s", ssh_privkey_alg(k), b64);
|
||||
const char *alg = ssh_privkey_alg(k);
|
||||
size_t alglen = strlen(alg), b64len = strlen(b64);
|
||||
if (alglen + 1 + b64len + 1 > sizeof(keyline)) {
|
||||
fprintf(stderr, "paze-copy-id: key line too long\n");
|
||||
free(d);
|
||||
ssh_privkey_free(k);
|
||||
return 1;
|
||||
}
|
||||
memcpy(keyline, alg, alglen);
|
||||
keyline[alglen] = ' ';
|
||||
memcpy(keyline + alglen + 1, b64, b64len);
|
||||
keyline[alglen + 1 + b64len] = '\0';
|
||||
ssh_privkey_free(k);
|
||||
privfile = keyfile;
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -158,8 +158,8 @@ int pssh_cmd_keygen(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
char def[256];
|
||||
if (!comment) {
|
||||
char def[256];
|
||||
const char *u = getenv("USER");
|
||||
if (!u) u = getenv("USERNAME");
|
||||
const char *h = getenv("COMPUTERNAME");
|
||||
|
||||
+9
-2
@@ -70,8 +70,8 @@ static int usage(void) {
|
||||
fprintf(stderr,
|
||||
"PazeSSL v0.1 (libc-only TLS 1.3 + 1.2)\n"
|
||||
"Usage:\n"
|
||||
" pazessl s_client -connect host:port [-CAfile roots.pem] [-servername SNI]\n"
|
||||
" pazessl s_server -accept port -cert cert.pem -key key.pem\n"
|
||||
" pazessl s_client -connect host:port [-CAfile roots.pem] [-servername SNI] [-tls1_2]\n"
|
||||
" pazessl s_server -accept port -cert cert.pem -key key.pem [-tls1_2]\n"
|
||||
" pazessl verify -CAfile roots.pem cert.pem\n"
|
||||
" pazessl genrsa -out key.pem [2048]\n"
|
||||
" pazessl req -x509 -newkey rsa:2048 -keyout k.pem -out c.pem -days D -subj /CN=X\n"
|
||||
@@ -106,6 +106,7 @@ int main(int argc, char **argv) {
|
||||
const char *host = NULL; int port = 443;
|
||||
const char *cafile = NULL;
|
||||
const char *sni = NULL;
|
||||
int only_12 = 0;
|
||||
for (int i = 2; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-connect") == 0 && i + 1 < argc) {
|
||||
char buf[256]; snprintf(buf, sizeof(buf), "%s", argv[++i]);
|
||||
@@ -116,11 +117,14 @@ int main(int argc, char **argv) {
|
||||
cafile = argv[++i];
|
||||
} else if (strcmp(argv[i], "-servername") == 0 && i + 1 < argc) {
|
||||
sni = argv[++i];
|
||||
} else if (strcmp(argv[i], "-tls1_2") == 0) {
|
||||
only_12 = 1;
|
||||
}
|
||||
}
|
||||
if (!host) { fprintf(stderr, "need -connect host:port\n"); return 1; }
|
||||
|
||||
paze_tls_config_t *cfg = paze_tls_config_new(PAZE_TLS_CLIENT);
|
||||
if (only_12) paze_tls_config_set_versions(cfg, 0, 1);
|
||||
if (sni) paze_tls_config_set_sni(cfg, sni);
|
||||
const char *alpn_default[] = { "http/1.1" };
|
||||
paze_tls_config_set_alpn(cfg, alpn_default, 1);
|
||||
@@ -298,10 +302,12 @@ int main(int argc, char **argv) {
|
||||
if (strcmp(cmd, "s_server") == 0) {
|
||||
int port = 4433;
|
||||
const char *certf = NULL; const char *keyf = NULL;
|
||||
int only_12 = 0;
|
||||
for (int i = 2; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-accept") == 0 && i + 1 < argc) port = atoi(argv[++i]);
|
||||
else if (strcmp(argv[i], "-cert") == 0 && i + 1 < argc) certf = argv[++i];
|
||||
else if (strcmp(argv[i], "-key") == 0 && i + 1 < argc) keyf = argv[++i];
|
||||
else if (strcmp(argv[i], "-tls1_2") == 0) only_12 = 1;
|
||||
}
|
||||
if (!certf || !keyf) {
|
||||
fprintf(stderr, "s_server needs -cert and -key\n");
|
||||
@@ -316,6 +322,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
/* Setup TLS config */
|
||||
paze_tls_config_t *cfg = paze_tls_config_new(PAZE_TLS_SERVER);
|
||||
if (only_12) paze_tls_config_set_versions(cfg, 0, 1);
|
||||
paze_tls_config_set_cert_chain(cfg, (const char *)cdata, (const char *)kdata);
|
||||
paze_tls_config_set_verify(cfg, PAZE_TLS_VERIFY_NONE, NULL, NULL);
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ static int local_dir_enum(const char *dir, ldir_ent_t *ents, int max) {
|
||||
#ifdef _WIN32
|
||||
char pat[1024];
|
||||
size_t dl = strlen(dir);
|
||||
if (dl >= sizeof(pat) - 2) return -1;
|
||||
if (dl > 0 && (dir[dl - 1] == '/' || dir[dl - 1] == '\\'))
|
||||
snprintf(pat, sizeof(pat), "%s*", dir);
|
||||
else
|
||||
|
||||
+16
-2
@@ -96,10 +96,24 @@ static void parse_userhost(const char *spec, char *user, size_t ulen,
|
||||
char *at = strchr(buf, '@');
|
||||
char *hs = buf;
|
||||
if (user && ulen) user[0] = '\0';
|
||||
if (at) { *at = '\0'; if (user && ulen) snprintf(user, ulen, "%s", buf); hs = at + 1; }
|
||||
if (at) {
|
||||
*at = '\0';
|
||||
if (user && ulen) {
|
||||
size_t sl = strlen(buf);
|
||||
if (sl >= ulen) sl = ulen - 1;
|
||||
memcpy(user, buf, sl);
|
||||
user[sl] = '\0';
|
||||
}
|
||||
hs = at + 1;
|
||||
}
|
||||
char *colon = strchr(hs, ':');
|
||||
if (colon) { *colon = '\0'; if (port) *port = atoi(colon + 1); }
|
||||
snprintf(host, hlen, "%s", hs);
|
||||
{
|
||||
size_t sl = strlen(hs);
|
||||
if (sl >= hlen) sl = hlen - 1;
|
||||
memcpy(host, hs, sl);
|
||||
host[sl] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* 交互输入密码(以 * 显示) */
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
# build.ps1 —— 无 CMake 环境下的源码构建脚本
|
||||
# 编译三个静态库(paze_crypto/paze_ssl/paze_ssh)与 CLI 应用(pssh/pazessl)
|
||||
# 用法: .\build.ps1 [-Clean] [-Debug]
|
||||
param(
|
||||
[switch]$Clean,
|
||||
[switch]$Debug
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Continue"
|
||||
$Root = $PSScriptRoot
|
||||
$Build = Join-Path $Root "build"
|
||||
$Obj = Join-Path $Build "obj"
|
||||
|
||||
if ($Clean -and (Test-Path $Build)) { Remove-Item -Recurse -Force $Build }
|
||||
New-Item -ItemType Directory -Force -Path $Build, $Obj | Out-Null
|
||||
|
||||
$cc = "gcc"
|
||||
$opt = if ($Debug) { "-O0", "-g" } else { "-O2" }
|
||||
$cflags = @("-std=c17", "-Wall", "-Wextra", "-Iinclude", "-Isrc", "-D_WIN32_WINNT=0x0601") + $opt
|
||||
|
||||
function Compile-Dir($Dir, $Tag) {
|
||||
$objs = @()
|
||||
foreach ($f in (Get-ChildItem (Join-Path $Root $Dir) -Filter *.c)) {
|
||||
$o = Join-Path $Obj ($f.BaseName + "_" + $Tag + ".o")
|
||||
& $cc @cflags -c $f.FullName -o $o
|
||||
if ($LASTEXITCODE -ne 0) { throw "compile failed: $($f.Name)" }
|
||||
$objs += $o
|
||||
}
|
||||
return $objs
|
||||
}
|
||||
|
||||
function Link-Lib($Objs, $Out) {
|
||||
& $cc -r $Objs -o $Out
|
||||
if ($LASTEXITCODE -ne 0) { throw "link lib failed: $Out" }
|
||||
}
|
||||
|
||||
Write-Host "== crypto =="
|
||||
$crypto = Compile-Dir "src\crypto" "c"
|
||||
$util = Compile-Dir "src\util" "u"
|
||||
Link-Lib (($util + $crypto) | ForEach-Object { $_.Replace("\", "/") }) (Join-Path $Build "paze_crypto.a")
|
||||
|
||||
Write-Host "== ssl =="
|
||||
$ssl = Compile-Dir "src\ssl" "s"
|
||||
Link-Lib ($ssl | ForEach-Object { $_.Replace("\", "/") }) (Join-Path $Build "paze_ssl.a")
|
||||
|
||||
Write-Host "== ssh =="
|
||||
$ssh = Compile-Dir "src\ssh" "h"
|
||||
Link-Lib ($ssh | ForEach-Object { $_.Replace("\", "/") }) (Join-Path $Build "paze_ssh.a")
|
||||
|
||||
$cryptoA = (Join-Path $Build "paze_crypto.a").Replace("\", "/")
|
||||
$sslA = (Join-Path $Build "paze_ssl.a").Replace("\", "/")
|
||||
$sshA = (Join-Path $Build "paze_ssh.a").Replace("\", "/")
|
||||
|
||||
function Link-App($Main, $Libs, $Out) {
|
||||
Write-Host "== $Out =="
|
||||
& $cc @cflags $Main @Libs -lws2_32 -ladvapi32 -o (Join-Path $Build $Out)
|
||||
if ($LASTEXITCODE -ne 0) { throw "link app failed: $Out" }
|
||||
}
|
||||
|
||||
# pssh: 分发入口 + 全部子命令实现(各 apps/*/main.c 提供 pssh_cmd_*)
|
||||
Write-Host "== pssh apps =="
|
||||
$psshMain = (Join-Path $Root "apps\pssh\main.c").Replace("\", "/")
|
||||
& $cc @cflags -c $psshMain -o (Join-Path $Obj "app_pssh_main.o")
|
||||
if ($LASTEXITCODE -ne 0) { throw "compile pssh main failed" }
|
||||
$appObjs = @()
|
||||
foreach ($ad in (Get-ChildItem (Join-Path $Root "apps") -Directory | Where-Object { $_.Name -ne "pazessl" -and $_.Name -ne "pssh" })) {
|
||||
$am = Join-Path $ad.FullName "main.c"
|
||||
if (-not (Test-Path $am)) { continue }
|
||||
$o = Join-Path $Obj ("app_" + $ad.Name + ".o")
|
||||
& $cc @cflags -c $am -o $o
|
||||
if ($LASTEXITCODE -ne 0) { throw "compile app failed: $($ad.Name)" }
|
||||
$appObjs += $o
|
||||
}
|
||||
$appObjs += (Join-Path $Obj "app_pssh_main.o")
|
||||
$appObjs = $appObjs | ForEach-Object { $_.Replace("\", "/") }
|
||||
& $cc @cflags $appObjs @($sshA, $cryptoA) -lws2_32 -ladvapi32 -o (Join-Path $Build "pssh.exe")
|
||||
if ($LASTEXITCODE -ne 0) { throw "link pssh failed" }
|
||||
|
||||
Link-App (Join-Path $Root "apps\pazessl\main.c") @($sslA, $cryptoA) "pazessl.exe"
|
||||
|
||||
Write-Host "== done =="
|
||||
@@ -0,0 +1,91 @@
|
||||
# OpenSSL interop 测试(一次性)
|
||||
# 四组: pazessl s_server ↔ openssl s_client (1.3/1.2)
|
||||
# openssl s_server ↔ pazessl s_client (1.3/1.2)
|
||||
$ErrorActionPreference = "Continue"
|
||||
$Root = "F:\TLS-SSH"
|
||||
$exe = Join-Path $Root "build\pazessl.exe"
|
||||
$openssl = "C:\Program Files\Git\usr\bin\openssl.exe"
|
||||
$key = Join-Path $Root "build\key.pem"
|
||||
$cert = Join-Path $Root "build\cert.pem"
|
||||
$empty = Join-Path $Root "build\empty.in"
|
||||
|
||||
# 空 stdin 文件(openssl s_client 握手后 EOF 立即关闭)
|
||||
if (-not (Test-Path $empty)) { New-Item -ItemType File -Path $empty -Force | Out-Null }
|
||||
|
||||
$pass = 0; $fail = 0
|
||||
|
||||
function Test-Case($tag) {
|
||||
Write-Host ""
|
||||
Write-Host "== $tag =="
|
||||
}
|
||||
|
||||
function Test-PazesslSrv($tag, $port, $srvExtra, $cliArgs) {
|
||||
$sout = Join-Path $Root "build\i_$tag.srv.out.log"
|
||||
$serr = Join-Path $Root "build\i_$tag.srv.err.log"
|
||||
$cout = Join-Path $Root "build\i_$tag.cli.out.log"
|
||||
$cerr = Join-Path $Root "build\i_$tag.cli.err.log"
|
||||
$srv = Start-Process -FilePath $exe -ArgumentList (@("s_server","-accept",$port,"-cert",$cert,"-key",$key) + $srvExtra) `
|
||||
-NoNewWindow -PassThru -RedirectStandardOutput $sout -RedirectStandardError $serr
|
||||
Start-Sleep -Milliseconds 1200
|
||||
$cli = Start-Process -FilePath $openssl -ArgumentList $cliArgs -NoNewWindow -PassThru `
|
||||
-RedirectStandardOutput $cout -RedirectStandardError $cerr -RedirectStandardInput $empty
|
||||
$null = $cli.WaitForExit(8000)
|
||||
if (-not $cli.HasExited) { Stop-Process -Id $cli.Id -Force }
|
||||
if (-not $srv.HasExited) { Stop-Process -Id $srv.Id -Force }
|
||||
$t = ""
|
||||
if (Test-Path $cerr) { $t = Get-Content $cerr -Raw -ErrorAction SilentlyContinue }
|
||||
$ok = $t -match "Protocol version: TLSv"
|
||||
Write-Host "server=$($cli.ExitCode) proto=$($t -replace '(?s).*Protocol version: (TLSv[0-9.]+).*','$1')"
|
||||
if ($t -notmatch "Protocol version:") { Get-Content $cerr -ErrorAction SilentlyContinue | Select-Object -First 6 | ForEach-Object { Write-Host "C: $_" } }
|
||||
return $ok
|
||||
}
|
||||
|
||||
function Test-OpensslSrv($tag, $port, $srvExtra, $cliArgs, $expectVer) {
|
||||
$sout = Join-Path $Root "build\i_$tag.srv.out.log"
|
||||
$serr = Join-Path $Root "build\i_$tag.srv.err.log"
|
||||
$cout = Join-Path $Root "build\i_$tag.cli.out.log"
|
||||
$cerr = Join-Path $Root "build\i_$tag.cli.err.log"
|
||||
$srv = Start-Process -FilePath $openssl -ArgumentList (@("s_server","-accept",$port,"-cert",$cert,"-key",$key,"-quiet") + $srvExtra) `
|
||||
-NoNewWindow -PassThru -RedirectStandardOutput $sout -RedirectStandardError $serr
|
||||
Start-Sleep -Milliseconds 1200
|
||||
$cli = Start-Process -FilePath $exe -ArgumentList $cliArgs -NoNewWindow -PassThru `
|
||||
-RedirectStandardOutput $cout -RedirectStandardError $cerr
|
||||
$ok = $false
|
||||
for ($i = 0; $i -lt 20; $i++) {
|
||||
Start-Sleep -Milliseconds 500
|
||||
if (Test-Path $cerr) {
|
||||
$t = Get-Content $cerr -Raw -ErrorAction SilentlyContinue
|
||||
if ($t -match "Connected \(TLS 0$expectVer") { $ok = $true; break }
|
||||
}
|
||||
if ($cli.HasExited) { break }
|
||||
}
|
||||
if (-not $cli.HasExited) { Stop-Process -Id $cli.Id -Force }
|
||||
if (-not $srv.HasExited) { Stop-Process -Id $srv.Id -Force }
|
||||
Write-Host "client-ok=$ok"
|
||||
if (Test-Path $cerr) { Get-Content $cerr -ErrorAction SilentlyContinue | Select-Object -Last 4 | ForEach-Object { Write-Host "C: $_" } }
|
||||
return $ok
|
||||
}
|
||||
|
||||
# 1) pazessl s_server (1.3) ↔ openssl s_client -tls1_3
|
||||
Test-Case "1. pazessl-srv / openssl-cli 1.3"
|
||||
$r = Test-PazesslSrv "p2o13" "14500" @() @("s_client","-connect","127.0.0.1:14500","-tls1_3","-brief")
|
||||
if ($r) { $pass++ } else { $fail++ }
|
||||
|
||||
# 2) pazessl s_server (1.2) ↔ openssl s_client -tls1_2
|
||||
Test-Case "2. pazessl-srv / openssl-cli 1.2"
|
||||
$r = Test-PazesslSrv "p2o12" "14501" @("-tls1_2") @("s_client","-connect","127.0.0.1:14501","-tls1_2","-brief")
|
||||
if ($r) { $pass++ } else { $fail++ }
|
||||
|
||||
# 3) openssl s_server (1.3) ↔ pazessl s_client
|
||||
Test-Case "3. openssl-srv / pazessl-cli 1.3"
|
||||
$r = Test-OpensslSrv "o2p13" "14502" @("-tls1_3") @("s_client","-connect","127.0.0.1:14502") "304"
|
||||
if ($r) { $pass++ } else { $fail++ }
|
||||
|
||||
# 4) openssl s_server (1.2) ↔ pazessl s_client -tls1_2
|
||||
Test-Case "4. openssl-srv / pazessl-cli 1.2"
|
||||
$r = Test-OpensslSrv "o2p12" "14503" @("-tls1_2") @("s_client","-connect","127.0.0.1:14503","-tls1_2") "303"
|
||||
if ($r) { $pass++ } else { $fail++ }
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "== RESULT: pass=$pass fail=$fail =="
|
||||
exit $fail
|
||||
@@ -0,0 +1,59 @@
|
||||
# TLS 1.2/1.3 自测脚本(一次性)
|
||||
$ErrorActionPreference = "Continue"
|
||||
$Root = "F:\TLS-SSH"
|
||||
$exe = Join-Path $Root "build\pazessl.exe"
|
||||
$key = Join-Path $Root "build\key.pem"
|
||||
$cert = Join-Path $Root "build\cert.pem"
|
||||
|
||||
# 1) 生成证书(若不存在)
|
||||
if (-not (Test-Path $cert)) {
|
||||
& $exe req -x509 -newkey rsa:2048 -keyout $key -out $cert -days 365 -subj /CN=localhost 2>&1 | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host "FAIL: req"; exit 1 }
|
||||
}
|
||||
|
||||
function Test-Conn($tag, $port, $serverArgs, $clientArgs) {
|
||||
$sout = Join-Path $Root "build\srv_$tag.out.log"
|
||||
$serr = Join-Path $Root "build\srv_$tag.err.log"
|
||||
$cout = Join-Path $Root "build\cli_$tag.out.log"
|
||||
$cerr = Join-Path $Root "build\cli_$tag.err.log"
|
||||
# 启动服务端(输出重定向到文件)
|
||||
$srv = Start-Process -FilePath $exe -ArgumentList $serverArgs -NoNewWindow -PassThru `
|
||||
-RedirectStandardOutput $sout -RedirectStandardError $serr
|
||||
Start-Sleep -Milliseconds 1200
|
||||
$alive = -not $srv.HasExited
|
||||
# 启动客户端(中继只读,握手后阻塞,故用轮询判据)
|
||||
$cli = Start-Process -FilePath $exe -ArgumentList $clientArgs -NoNewWindow -PassThru `
|
||||
-RedirectStandardOutput $cout -RedirectStandardError $cerr
|
||||
$ok = $false
|
||||
for ($i = 0; $i -lt 20; $i++) {
|
||||
Start-Sleep -Milliseconds 500
|
||||
if ($cli.HasExited) { break }
|
||||
if (Test-Path $cerr) {
|
||||
$t = Get-Content $cerr -Raw -ErrorAction SilentlyContinue
|
||||
if ($t -match "Connected \(TLS 03") { $ok = $true; break }
|
||||
}
|
||||
}
|
||||
if (-not $cli.HasExited) { Stop-Process -Id $cli.Id -Force }
|
||||
if (-not $srv.HasExited) { Stop-Process -Id $srv.Id -Force }
|
||||
Write-Host "== $tag =="
|
||||
Write-Host "server-alive=$alive client-ok=$ok"
|
||||
if (Test-Path $cerr) { Get-Content $cerr | ForEach-Object { Write-Host "C: $_" } }
|
||||
if (Test-Path $serr) { Get-Content $serr | ForEach-Object { Write-Host "S: $_" } }
|
||||
return $ok
|
||||
}
|
||||
|
||||
# TLS 1.2 自测
|
||||
Test-Conn "tls1_2-self" "14433" @("s_server","-accept","14433","-cert",$cert,"-key",$key,"-tls1_2") `
|
||||
@("s_client","-connect","127.0.0.1:14433","-tls1_2")
|
||||
|
||||
# TLS 1.3 自测(默认版本)
|
||||
Test-Conn "tls1_3-self" "14434" @("s_server","-accept","14434","-cert",$cert,"-key",$key) `
|
||||
@("s_client","-connect","127.0.0.1:14434")
|
||||
|
||||
# 回退场景 A: 客户端默认(先试 1.3) ↔ 服务端仅 1.2 → 客户端回退到 1.2
|
||||
Test-Conn "fallback-cli" "14435" @("s_server","-accept","14435","-cert",$cert,"-key",$key,"-tls1_2") `
|
||||
@("s_client","-connect","127.0.0.1:14435")
|
||||
|
||||
# 回退场景 B: 客户端仅 1.2 ↔ 服务端默认(先试 1.3) → 服务端回退到 1.2
|
||||
Test-Conn "fallback-srv" "14436" @("s_server","-accept","14436","-cert",$cert,"-key",$key) `
|
||||
@("s_client","-connect","127.0.0.1:14436","-tls1_2")
|
||||
@@ -12,6 +12,8 @@
|
||||
#ifndef PAZE_SSH_CONFIG_H
|
||||
#define PAZE_SSH_CONFIG_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
+3
-1
@@ -65,9 +65,11 @@ extern "C" {
|
||||
#define PAZE_TLS_HS_END_OF_EARLY_DATA 5
|
||||
#define PAZE_TLS_HS_ENCRYPTED_EXTENSIONS 8
|
||||
#define PAZE_TLS_HS_CERTIFICATE 11
|
||||
#define PAZE_TLS_HS_CERTIFICATE_KEY_USAGE 12 /* 不用 */
|
||||
#define PAZE_TLS_HS_SERVER_KEY_EXCHANGE 12 /* TLS 1.2 */
|
||||
#define PAZE_TLS_HS_CERTIFICATE_REQUEST 13
|
||||
#define PAZE_TLS_HS_SERVER_HELLO_DONE 14 /* TLS 1.2 */
|
||||
#define PAZE_TLS_HS_CERTIFICATE_VERIFY 15
|
||||
#define PAZE_TLS_HS_CLIENT_KEY_EXCHANGE 16 /* TLS 1.2 */
|
||||
#define PAZE_TLS_HS_FINISHED 20
|
||||
#define PAZE_TLS_HS_KEY_UPDATE 24
|
||||
#define PAZE_TLS_HS_MESSAGE_HASH 254
|
||||
|
||||
+9
-13
@@ -327,16 +327,6 @@ int paze_bn_mul(paze_bn_t *r, const paze_bn_t *a, const paze_bn_t *b) {
|
||||
* 除法与取模 (Knuth Algorithm D 简化版)
|
||||
* ============================================================ */
|
||||
|
||||
/* 单 limb 除法: a = q * d + r,a 是 2-limb,d 是 1-limb。
|
||||
* 返回 q,余数存 *r。d != 0。 */
|
||||
static uint32_t divmod_2by1(uint32_t a_hi, uint32_t a_lo, uint32_t d,
|
||||
uint32_t *r) {
|
||||
uint64_t a = ((uint64_t)a_hi << 32) | a_lo;
|
||||
uint32_t q = (uint32_t)(a / d);
|
||||
*r = (uint32_t)(a % d);
|
||||
return q;
|
||||
}
|
||||
|
||||
/* q = a / b, r = a mod b。q/r 可为 NULL。b != 0。
|
||||
* 使用 Knuth Algorithm D (规范化除数 + 试商修正)。
|
||||
* q 和 r 不能与 a 别名(内部用临时副本)。 */
|
||||
@@ -400,10 +390,16 @@ int paze_bn_divmod(paze_bn_t *q, paze_bn_t *r,
|
||||
uint64_t uu = ((uint64_t)un.d[j + n] << 32) | un.d[j + n - 1];
|
||||
uint64_t qhat = uu / v_top;
|
||||
uint64_t rhat = uu % v_top;
|
||||
if (qhat > 0xFFFFFFFFull) qhat = 0xFFFFFFFFull;
|
||||
/* D3:qhat 溢出 32 位时,取上限 B-1 并调整 rhat。
|
||||
* 标准算法中 rhat = u[j+n] + v1(对应 uu 的高位部分)。 */
|
||||
if (qhat > 0xFFFFFFFFull) {
|
||||
qhat = 0xFFFFFFFFull;
|
||||
rhat = (uint64_t)un.d[j + n] + v_top;
|
||||
}
|
||||
|
||||
/* 修正 qhat */
|
||||
while (qhat * v_top2 > (rhat << 32) + un.d[j + n - 2]) {
|
||||
/* 修正 qhat(rhat 可能 >= 2^32,此时 qhat*v2 < B*rhat 恒成立,无需修正) */
|
||||
while (rhat <= 0xFFFFFFFFull &&
|
||||
qhat * v_top2 > (rhat << 32) + un.d[j + n - 2]) {
|
||||
qhat--;
|
||||
rhat += v_top;
|
||||
if (rhat > 0xFFFFFFFFull) break;
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ static paze_status_t rfc6979_k(uint8_t k_be[32],
|
||||
const paze_hash_alg_t *alg = hash_alg_of(hash_alg);
|
||||
if (!alg) return PAZE_ERR_NOTSUP;
|
||||
size_t hlen = alg->digest_len;
|
||||
paze_bn_t n, qbits;
|
||||
paze_bn_t n;
|
||||
paze_p256_load_n(&n);
|
||||
int qlen_bits = paze_bn_bit_len(&n); /* 256 */
|
||||
|
||||
|
||||
+7
-11
@@ -165,16 +165,14 @@ paze_status_t paze_p256_add(paze_p256_point_t *R,
|
||||
fe_mul(&U1, &P->X, &Z2Z2, &p);
|
||||
fe_mul(&U2, &Q->X, &Z1Z1, &p);
|
||||
|
||||
paze_bn_t t1, t2;
|
||||
paze_bn_t t1;
|
||||
fe_mul(&t1, &Q->Z, &Z2Z2, &p); /* Z2*Z2Z2 */
|
||||
fe_mul(&S1, &P->Y, &t1, &p);
|
||||
fe_mul(&t1, &P->Z, &Z1Z1, &p); /* Z1*Z1Z1 */
|
||||
fe_mul(&S2, &Q->Y, &t1, &p);
|
||||
|
||||
fe_sub(&H, &U2, &U1, &p);
|
||||
fe_sub(&t1, &S2, &S1, &p);
|
||||
paze_bn_t two; paze_bn_set_u32(&two, 2);
|
||||
paze_bn_modmul(&rr, &t1, &two, &p); /* r = 2*(S2-S1) */
|
||||
fe_sub(&rr, &S2, &S1, &p); /* r = S2-S1 (与下方 X3/Y3 公式匹配) */
|
||||
|
||||
if (paze_bn_is_zero(&H)) {
|
||||
if (paze_bn_is_zero(&rr)) {
|
||||
@@ -198,6 +196,7 @@ paze_status_t paze_p256_add(paze_p256_point_t *R,
|
||||
paze_bn_t U1H2;
|
||||
fe_mul(&U1H2, &U1, &H2, &p);
|
||||
paze_bn_t U1H2_2;
|
||||
paze_bn_t two; paze_bn_set_u32(&two, 2);
|
||||
paze_bn_modmul(&U1H2_2, &U1H2, &two, &p);
|
||||
fe_sub(&X3, &X3, &U1H2_2, &p);
|
||||
|
||||
@@ -209,13 +208,10 @@ paze_status_t paze_p256_add(paze_p256_point_t *R,
|
||||
fe_mul(&S1H3, &S1, &H3, &p);
|
||||
fe_sub(&Y3, &Y3, &S1H3, &p);
|
||||
|
||||
/* Z3 = H*((Z1+Z2)^2 - Z1Z1 - Z2Z2) */
|
||||
paze_bn_t Z3, zsum;
|
||||
fe_add(&zsum, &P->Z, &Q->Z, &p);
|
||||
fe_sqr(&Z3, &zsum, &p);
|
||||
fe_sub(&Z3, &Z3, &Z1Z1, &p);
|
||||
fe_sub(&Z3, &Z3, &Z2Z2, &p);
|
||||
fe_mul(&Z3, &H, &Z3, &p);
|
||||
/* Z3 = Z1 * Z2 * H (与 r=S2-S1, X3/Y3 标准公式配套) */
|
||||
paze_bn_t Z3;
|
||||
fe_mul(&Z3, &P->Z, &Q->Z, &p);
|
||||
fe_mul(&Z3, &Z3, &H, &p);
|
||||
|
||||
paze_bn_copy(&R->X, &X3);
|
||||
paze_bn_copy(&R->Y, &Y3);
|
||||
|
||||
+28
-15
@@ -579,27 +579,34 @@ static void unix_to_civil(int64_t t, int *yy, unsigned *mm, unsigned *dd,
|
||||
*yy = y;
|
||||
}
|
||||
|
||||
/* 编码 GeneralizedTime(15 字符 + Z),out 需 16 字节 */
|
||||
static void time_to_gt(int64_t t, char out[16]) {
|
||||
/* 编码证书时间(RFC 5280 §4.1.2.5):2050 年前用 UTCTime(YYMMDDHHMMSSZ),
|
||||
* 超出范围回退 GeneralizedTime(YYYYMMDDHHMMSSZ)。out 需 32 字节。 */
|
||||
static void time_to_cert_time(int64_t t, char out[32]) {
|
||||
int y; unsigned mo, d, h, mi, s;
|
||||
unix_to_civil(t, &y, &mo, &d, &h, &mi, &s);
|
||||
sprintf(out, "%04d%02u%02u%02u%02u%02uZ", y, mo, d, h, mi, s);
|
||||
if (y < 1950 || y >= 2050)
|
||||
snprintf(out, 32, "G%04d%02u%02u%02u%02u%02uZ", y, mo, d, h, mi, s);
|
||||
else
|
||||
snprintf(out, 32, "%02d%02u%02u%02u%02u%02uZ", y % 100, mo, d, h, mi, s);
|
||||
}
|
||||
|
||||
paze_status_t paze_x509_build_dn_cn(uint8_t *out, size_t *outlen, const char *cn) {
|
||||
if (!out || !outlen || !cn) return PAZE_ERR_INVALID;
|
||||
static const uint8_t cn_oid[] = {0x55, 0x04, 0x03};
|
||||
size_t cnlen = strlen(cn);
|
||||
paze_asn1_writer_t atv, rdn, name;
|
||||
paze_asn1_writer_t atv, ent, rdn, name;
|
||||
paze_asn1_w_init(&atv, NULL, 0);
|
||||
paze_asn1_w_init(&ent, NULL, 0);
|
||||
paze_asn1_w_init(&rdn, NULL, 0);
|
||||
paze_asn1_w_init(&name, NULL, 0);
|
||||
paze_status_t st = paze_asn1_w_oid(&atv, cn_oid, sizeof(cn_oid));
|
||||
if (st == PAZE_OK) { st = paze_asn1_w_tag_len(&atv, ASN1_UTF8, cnlen); }
|
||||
if (st == PAZE_OK) st = paze_asn1_w_data(&atv, (const uint8_t *)cn, cnlen);
|
||||
/* RDN = SET { atv } */
|
||||
if (st == PAZE_OK) { st = paze_asn1_w_tag_len(&rdn, ASN1_SET, atv.len); }
|
||||
if (st == PAZE_OK) st = paze_asn1_w_data(&rdn, atv.buf, atv.len);
|
||||
/* AttributeTypeAndValue = SEQUENCE { atv } (RFC 5280 §4.1.2.4) */
|
||||
if (st == PAZE_OK) st = w_seq(&ent, atv.buf, atv.len);
|
||||
/* RDN = SET { ent } */
|
||||
if (st == PAZE_OK) { st = paze_asn1_w_tag_len(&rdn, ASN1_SET, ent.len); }
|
||||
if (st == PAZE_OK) st = paze_asn1_w_data(&rdn, ent.buf, ent.len);
|
||||
/* Name = SEQUENCE { rdn } */
|
||||
if (st == PAZE_OK) st = w_seq(&name, rdn.buf, rdn.len);
|
||||
if (st == PAZE_OK) {
|
||||
@@ -607,6 +614,7 @@ paze_status_t paze_x509_build_dn_cn(uint8_t *out, size_t *outlen, const char *cn
|
||||
else { memcpy(out, name.buf, name.len); *outlen = name.len; }
|
||||
}
|
||||
paze_asn1_w_free(&atv);
|
||||
paze_asn1_w_free(&ent);
|
||||
paze_asn1_w_free(&rdn);
|
||||
paze_asn1_w_free(&name);
|
||||
return st;
|
||||
@@ -685,9 +693,9 @@ paze_status_t paze_x509_sign_cert(uint8_t *out, size_t *outlen,
|
||||
sig_oid = OID_ECDSA_SHA256; sig_oid_len = sizeof(OID_ECDSA_SHA256); with_null = 0;
|
||||
} else return PAZE_ERR_INVALID;
|
||||
|
||||
char nb[16], na[16];
|
||||
time_to_gt(not_before, nb);
|
||||
time_to_gt(not_after, na);
|
||||
char nb[32], na[32];
|
||||
time_to_cert_time(not_before, nb);
|
||||
time_to_cert_time(not_after, na);
|
||||
|
||||
paze_asn1_writer_t tbs; paze_asn1_w_init(&tbs, NULL, 0);
|
||||
paze_status_t st = PAZE_OK;
|
||||
@@ -703,13 +711,18 @@ paze_status_t paze_x509_sign_cert(uint8_t *out, size_t *outlen,
|
||||
if (st == PAZE_OK) st = paze_asn1_w_integer(&tbs, serial, serial_len);
|
||||
if (st == PAZE_OK) st = w_alg_id(&tbs, sig_oid, sig_oid_len, with_null);
|
||||
if (st == PAZE_OK) st = paze_asn1_w_data(&tbs, issuer_dn, iss_len);
|
||||
/* validity SEQUENCE { GT(nb), GT(na) } */
|
||||
/* validity SEQUENCE { Time(nb), Time(na) } */
|
||||
{
|
||||
paze_asn1_writer_t val; paze_asn1_w_init(&val, NULL, 0);
|
||||
if (st == PAZE_OK) st = paze_asn1_w_tag_len(&val, 0x18, 15);
|
||||
if (st == PAZE_OK) st = paze_asn1_w_data(&val, (const uint8_t *)nb, 15);
|
||||
if (st == PAZE_OK) st = paze_asn1_w_tag_len(&val, 0x18, 15);
|
||||
if (st == PAZE_OK) st = paze_asn1_w_data(&val, (const uint8_t *)na, 15);
|
||||
/* UTCTime(0x17, 13 字节)或 GeneralizedTime(0x18, 'G'+15 字节) */
|
||||
uint8_t nb_tag = (nb[0] == 'G') ? 0x18 : 0x17;
|
||||
uint8_t na_tag = (na[0] == 'G') ? 0x18 : 0x17;
|
||||
size_t nb_len = (nb_tag == 0x18) ? 16 : 13;
|
||||
size_t na_len = (na_tag == 0x18) ? 16 : 13;
|
||||
if (st == PAZE_OK) st = paze_asn1_w_tag_len(&val, nb_tag, nb_len);
|
||||
if (st == PAZE_OK) st = paze_asn1_w_data(&val, (const uint8_t *)(nb_tag == 0x18 ? nb + 1 : nb), nb_len);
|
||||
if (st == PAZE_OK) st = paze_asn1_w_tag_len(&val, na_tag, na_len);
|
||||
if (st == PAZE_OK) st = paze_asn1_w_data(&val, (const uint8_t *)(na_tag == 0x18 ? na + 1 : na), na_len);
|
||||
if (st == PAZE_OK) st = w_seq(&tbs, val.buf, val.len);
|
||||
paze_asn1_w_free(&val);
|
||||
}
|
||||
|
||||
@@ -246,6 +246,7 @@ static int dir_open(const char *path, srv_handle_t *hh) {
|
||||
#ifdef _WIN32
|
||||
size_t pl = strlen(path);
|
||||
char pat[1024];
|
||||
if (pl >= sizeof(pat) - 2) return -1;
|
||||
if (pl && path[pl - 1] == '/')
|
||||
snprintf(pat, sizeof(pat), "%s*", path);
|
||||
else
|
||||
|
||||
+4
-15
@@ -22,22 +22,11 @@
|
||||
*/
|
||||
#include "ssh_internal.h"
|
||||
|
||||
/* Compute hash of a string list (concatenated) */
|
||||
static void concat_hash(const paze_hash_alg_t *alg, const uint8_t *str, size_t len,
|
||||
uint8_t out[64]) {
|
||||
uint8_t ctx[256];
|
||||
alg->init(ctx);
|
||||
alg->update(ctx, str, len);
|
||||
alg->final(ctx, out);
|
||||
}
|
||||
|
||||
int ssh_kex_perform(ssh_session_t *s) {
|
||||
if (!s) return -1;
|
||||
|
||||
if (!s->is_server) {
|
||||
/* Client: generate ephemeral key, send KEX_INIT */
|
||||
uint8_t kexinit_payload[4096];
|
||||
/* Client sends KEXDH_INIT with ephemeral pub key */
|
||||
uint8_t our_pub[256]; size_t our_pub_len = 0;
|
||||
|
||||
if (strcmp(s->neg.kex, SSH_KEX_CURVE25519) == 0) {
|
||||
@@ -102,7 +91,6 @@ int ssh_kex_perform(ssh_session_t *s) {
|
||||
|
||||
uint32_t sigl = paze_load32_be(payload + poff); poff += 4;
|
||||
if (poff + sigl > plen) { free((void *)payload); return -1; }
|
||||
const uint8_t *sig = payload + poff;
|
||||
if (s->verbose)
|
||||
fprintf(stderr, "SSH: KEXDH_REPLY parsed: K_S=%u f=%u sig=%u\n", ksl, fl, sigl);
|
||||
|
||||
@@ -213,8 +201,10 @@ int ssh_kex_perform(ssh_session_t *s) {
|
||||
|
||||
/* Sign exchange hash with host key */
|
||||
uint8_t sig_blob[4096]; size_t sig_blob_len = sizeof(sig_blob);
|
||||
int sr = ssh_keys_sign(&s->host_key, exchange_hash, eh_len,
|
||||
sig_blob, &sig_blob_len);
|
||||
if (ssh_keys_sign(&s->host_key, exchange_hash, eh_len,
|
||||
sig_blob, &sig_blob_len) < 0) {
|
||||
free((void *)payload); return -1;
|
||||
}
|
||||
|
||||
/* Deactivate keys: KEXDH_REPLY must be UNENCRYPTED */
|
||||
size_t sv_key_len_c2s = s->enc_c2s.key_len;
|
||||
@@ -261,7 +251,6 @@ int ssh_kex_derive_keys(ssh_session_t *s,
|
||||
const uint8_t *e_pub, size_t e_len,
|
||||
const uint8_t *f_pub, size_t f_len,
|
||||
uint8_t *exchange_hash_out, size_t *eh_len) {
|
||||
const paze_hash_alg_t *H = paze_hash_sha256();
|
||||
size_t hl = 32;
|
||||
|
||||
/* Encode shared secret as SSH mpint.
|
||||
|
||||
+3
-3
@@ -485,7 +485,7 @@ int ssh_packet_recv(ssh_session_t *s,
|
||||
ssh_chacha20(k1, 1, nonce8, body_ct, body, body_len);
|
||||
|
||||
padl = body[0];
|
||||
if (padl < SSH_MIN_PADL || padl > SSH_MAX_PADL || (size_t)(padl + 2) > body_len) {
|
||||
if (padl < SSH_MIN_PADL || (size_t)(padl + 2) > body_len) {
|
||||
free(body); free(rest); return -1;
|
||||
}
|
||||
*type = body[1];
|
||||
@@ -515,7 +515,7 @@ int ssh_packet_recv(ssh_session_t *s,
|
||||
}
|
||||
|
||||
padl = body[0];
|
||||
if (padl < SSH_MIN_PADL || padl > SSH_MAX_PADL || (size_t)(padl + 2) > body_len) {
|
||||
if (padl < SSH_MIN_PADL || (size_t)(padl + 2) > body_len) {
|
||||
paze_memzero(body, body_len);
|
||||
free(body); free(rest); return -1;
|
||||
}
|
||||
@@ -533,7 +533,7 @@ int ssh_packet_recv(ssh_session_t *s,
|
||||
} else {
|
||||
/* No encryption: parse plain */
|
||||
padl = rest[0];
|
||||
if (padl < SSH_MIN_PADL || padl > SSH_MAX_PADL || (size_t)(padl + 2) > body_len) {
|
||||
if (padl < SSH_MIN_PADL || (size_t)(padl + 2) > body_len) {
|
||||
free(rest); return -1;
|
||||
}
|
||||
*type = rest[1];
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
/* 从通道精确读取 n 字节 (阻塞)。EOF/出错返回 -1。 */
|
||||
static int scp_read_exact(ssh_session_t *s, uint32_t ch, uint8_t *buf, size_t n) {
|
||||
(void)ch;
|
||||
size_t got = 0;
|
||||
while (got < n) {
|
||||
uint8_t tmp[16384];
|
||||
|
||||
+4
-1
@@ -443,7 +443,10 @@ int ssh_fwd_remote_connect(ssh_fwd_t *f, ssh_session_t *s, uint32_t ch,
|
||||
c->sock = sock;
|
||||
c->ch = ch;
|
||||
c->ch_ready = 1;
|
||||
snprintf(c->target, sizeof(c->target), "%s", R->target);
|
||||
size_t tl = strlen(R->target);
|
||||
if (tl >= sizeof(c->target)) tl = sizeof(c->target) - 1;
|
||||
memcpy(c->target, R->target, tl);
|
||||
c->target[tl] = '\0';
|
||||
c->target_port = R->target_port;
|
||||
fprintf(stderr, "pssh: remote connection on %s:%d -> %s:%d\n",
|
||||
R->bind, R->bind_port, R->target, R->target_port);
|
||||
|
||||
+2
-3
@@ -256,6 +256,7 @@ void paze_tls_free(paze_tls_t *tls) {
|
||||
paze_memzero(&tls->master_secret, sizeof(tls->master_secret));
|
||||
paze_tls_config_free(tls->cfg_owned);
|
||||
free(tls->ch1);
|
||||
free(tls->sh1);
|
||||
paze_memzero(tls, sizeof(*tls));
|
||||
free(tls);
|
||||
}
|
||||
@@ -289,9 +290,7 @@ paze_status_t paze_tls_handshake(paze_tls_t *tls) {
|
||||
if (!cfg->enable_1_2) return st;
|
||||
}
|
||||
if (cfg->enable_1_2) {
|
||||
/* 调用 1.2 实现(在 tls_handshake12.c 中) */
|
||||
extern paze_status_t paze_tls12_client_handshake(paze_tls_t *tls);
|
||||
extern paze_status_t paze_tls12_server_handshake(paze_tls_t *tls);
|
||||
/* 1.2 实现(tls_handshake12.c) */
|
||||
if (cfg->role == PAZE_TLS_CLIENT)
|
||||
return paze_tls12_client_handshake(tls);
|
||||
else
|
||||
|
||||
+2
-2
@@ -162,7 +162,7 @@ uint16_t paze_tls_ext_parse_key_share_server(const uint8_t *data, size_t len,
|
||||
if (len < 4) return 0;
|
||||
uint16_t group = paze_load16_be(data);
|
||||
uint16_t kl = paze_load16_be(data + 2);
|
||||
if (len != 4 + kl) return 0;
|
||||
if (len != (size_t)(4 + kl)) return 0;
|
||||
*pub = data + 4; *publen = kl;
|
||||
return group;
|
||||
}
|
||||
@@ -259,7 +259,7 @@ int paze_tls_ext_parse_alpn(const uint8_t *data, size_t len,
|
||||
size_t off = 2; int n = 0;
|
||||
while (off < len && n < PAZE_TLS_MAX_ALPN) {
|
||||
uint8_t nl = data[off];
|
||||
if (off + 1 + nl > len || nl >= 256) return n;
|
||||
if (off + 1 + nl > len) return n;
|
||||
memcpy(out[n], data + off + 1, nl);
|
||||
out[n][nl] = '\0';
|
||||
n++;
|
||||
|
||||
+866
-24
@@ -1,37 +1,879 @@
|
||||
/* tls_handshake12.c —— TLS 1.2 握手(占位,返回 PAZE_ERR_NOTSUP)
|
||||
/* tls_handshake12.c —— TLS 1.2 (RFC 5246 + RFC 8422/4492/5288/7905) 握手状态机
|
||||
*
|
||||
* TODO(后续版本):实现完整 TLS 1.2 状态机
|
||||
* Client → Server:
|
||||
* ClientHello (cipher ECDHE-RSA-AES128-GCM-SHA256 / RSA-AES128-GCM-SHA256 / ChaCha20-Poly1305)
|
||||
* Server → Client:
|
||||
* ServerHello + Certificate + ServerKeyExchange(ECDHE) + ServerHelloDone
|
||||
* Client → Server:
|
||||
* ClientKeyExchange(pre_master_secret_encrypted/RSA 或 ECDHE pub) + ChangeCipherSpec + Finished
|
||||
* Server → Client:
|
||||
* ChangeCipherSpec + Finished
|
||||
* 覆盖的握手流程:
|
||||
* Client → Server: ClientHello
|
||||
* Server → Client: ServerHello + Certificate
|
||||
* [+ ServerKeyExchange(ECDHE) + ServerHelloDone]
|
||||
* Client → Server: ClientKeyExchange + ChangeCipherSpec + Finished
|
||||
* Server → Client: ChangeCipherSpec + Finished
|
||||
*
|
||||
* TLS 1.2 key schedule (RFC 5246 §6.3 & RFC 8422 for ECDHE):
|
||||
* 支持的 cipher suites:
|
||||
* ECDHE-RSA-AES128-GCM-SHA256 (0xC02F, RFC 5288/4492)
|
||||
* ECDHE-ECDSA-AES128-GCM-SHA256 (0xC02B)
|
||||
* ECDHE-RSA-CHACHA20-POLY1305 (0xCCA8, RFC 7905)
|
||||
* RSA-AES128-GCM-SHA256 (0x009C, RFC 5288 静态 RSA 密钥交换)
|
||||
* 支持的组: X25519 / secp256r1 (ECDHE)
|
||||
*
|
||||
* key schedule (RFC 5246 §6.3):
|
||||
* master_secret = PRF(pre_master_secret, "master secret",
|
||||
* ClientHello.random + ServerHello.random, 48)
|
||||
* key_block = PRF(master_secret, "key expansion",
|
||||
* server_random + client_random, block_len_total)
|
||||
* write_MAC_key(客户端)
|
||||
* write_MAC_key(服务器)
|
||||
* write_key(客户端)
|
||||
* write_key(服务器)
|
||||
* write_IV(客户端) — GCM: 4 字节 fixed_iv
|
||||
* write_IV(服务器)
|
||||
* server_random + client_random, block_len)
|
||||
* AEAD 套件: MAC key 0, write_key, fixed_iv(4)
|
||||
* Finished: verify_data = PRF(master_secret, finished_label,
|
||||
* Hash(handshake_messages), 12)
|
||||
*
|
||||
* 入口:
|
||||
* 客户端: paze_tls12_client_handshake —— 可能从 TLS 1.3 回退
|
||||
* (ServerHello 已收并保存于 tls->sh1),也可能独立运行(INIT)。
|
||||
* 服务端: paze_tls12_server_handshake —— 可能从 TLS 1.3 回退
|
||||
* (ClientHello 已收并保存于 tls->ch1),也可能独立运行(INIT)。
|
||||
*/
|
||||
#include "tls_internal.h"
|
||||
#include <time.h>
|
||||
|
||||
/* ============================================================
|
||||
* 通用辅助
|
||||
* ============================================================ */
|
||||
|
||||
/* 发送握手消息:4 字节头 + body,并计入 transcript */
|
||||
static paze_status_t tls12_send_hs(paze_tls_t *tls, uint8_t type,
|
||||
const uint8_t *body, size_t blen) {
|
||||
uint8_t buf[PAZE_TLS_MAX_RECORD];
|
||||
if (blen + 4 > sizeof(buf)) return PAZE_ERR_OVERFLOW;
|
||||
paze_tls_hs_put_header(buf, type, blen);
|
||||
memcpy(buf + 4, body, blen);
|
||||
size_t total = 4 + blen;
|
||||
paze_tls_tr_update(&tls->transcript, buf, total);
|
||||
return paze_tls_record_send(tls, PAZE_TLS_CT_HANDSHAKE, buf, total);
|
||||
}
|
||||
|
||||
/* 读取握手消息(或 ChangeCipherSpec)。收到握手消息自动计入 transcript。
|
||||
* ccs=1 时 body/blen 指向 CCS 记录(不入 transcript)。 */
|
||||
static paze_status_t tls12_recv_hs(paze_tls_t *tls,
|
||||
const uint8_t **body, size_t *blen,
|
||||
uint8_t *type, int *ccs,
|
||||
const uint8_t **raw, size_t *raw_len) {
|
||||
const uint8_t *buf; size_t n; uint8_t ct;
|
||||
for (;;) {
|
||||
paze_status_t st = paze_tls_record_read(tls, &buf, &n, &ct);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (ct == PAZE_TLS_CT_ALERT) {
|
||||
if (n >= 2 && buf[0] == PAZE_TLS_ALERT_FATAL) {
|
||||
paze_tls_set_err(tls, "recv fatal alert %d", buf[1]);
|
||||
return PAZE_ERR_PROTOCOL;
|
||||
}
|
||||
if (n >= 2 && buf[1] == PAZE_TLS_ALERT_CLOSE_NOTIFY) {
|
||||
tls->close_recv = 1;
|
||||
return PAZE_ERR_IO;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (ct == PAZE_TLS_CT_CHANGE_CIPHER_SPEC) {
|
||||
*ccs = 1; *type = PAZE_TLS_CT_CHANGE_CIPHER_SPEC;
|
||||
*body = buf; *blen = n;
|
||||
if (raw) { *raw = buf; *raw_len = n; }
|
||||
return PAZE_OK;
|
||||
}
|
||||
if (ct != PAZE_TLS_CT_HANDSHAKE) {
|
||||
paze_tls_set_err(tls, "unexpected ct %d during 1.2 hs", ct);
|
||||
return PAZE_ERR_PROTOCOL;
|
||||
}
|
||||
if (n < 4) { paze_tls_set_err(tls, "short 1.2 handshake"); return PAZE_ERR_FORMAT; }
|
||||
*type = buf[0];
|
||||
size_t pl = ((size_t)buf[1] << 16) | ((size_t)buf[2] << 8) | buf[3];
|
||||
if (4 + pl > n) { paze_tls_set_err(tls, "1.2 hs len mismatch"); return PAZE_ERR_FORMAT; }
|
||||
*body = buf + 4; *blen = pl;
|
||||
if (raw) { *raw = buf; *raw_len = 4 + pl; }
|
||||
/* RFC 5246 §7.4.9: Finished 的 verify_data 覆盖"到此消息为止(不含)";
|
||||
* 故收到 Finished 时不自动计入 transcript,由调用方先验证再决定是否计入
|
||||
* (服务端需把客户端的 Finished 计入自身 Finished 的 transcript)。 */
|
||||
if (buf[0] != PAZE_TLS_HS_FINISHED)
|
||||
paze_tls_tr_update(&tls->transcript, buf, 4 + pl);
|
||||
*ccs = 0;
|
||||
return PAZE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* 等对端 ChangeCipherSpec(明文)。set_read_key=1 时切换接收密钥。 */
|
||||
static paze_status_t tls12_wait_ccs(paze_tls_t *tls, int set_read_key) {
|
||||
const uint8_t *body; size_t blen; uint8_t type; int ccs;
|
||||
for (;;) {
|
||||
paze_status_t st = tls12_recv_hs(tls, &body, &blen, &type, &ccs, NULL, NULL);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (ccs) {
|
||||
if (blen != 1 || body[0] != 0x01) {
|
||||
paze_tls_set_err(tls, "bad ChangeCipherSpec");
|
||||
return PAZE_ERR_PROTOCOL;
|
||||
}
|
||||
if (set_read_key) {
|
||||
tls->enc_read = 1;
|
||||
tls->read_key = &tls->handshake_read;
|
||||
}
|
||||
return PAZE_OK;
|
||||
}
|
||||
if (type == PAZE_TLS_HS_NEW_SESSION_TICKET) continue; /* 1.2 会话票,忽略 */
|
||||
paze_tls_set_err(tls, "expected ChangeCipherSpec, got hs %d", type);
|
||||
return PAZE_ERR_PROTOCOL;
|
||||
}
|
||||
}
|
||||
|
||||
/* TLS 1.2 Finished: PRF(master_secret, label, hash(transcript), 12) */
|
||||
static paze_status_t tls12_finished(paze_tls_t *tls, const char *label,
|
||||
uint8_t vd[12]) {
|
||||
uint8_t th[64];
|
||||
paze_tls_tr_current(&tls->transcript, th);
|
||||
paze_status_t st = paze_tls12_prf(paze_hash_sha256(), tls->ms12, 48, label,
|
||||
th, 32, vd, 12);
|
||||
return st;
|
||||
}
|
||||
|
||||
/* 由 key_block 配置单向 traffic key(fixed_iv 取 4 字节) */
|
||||
static void tls12_set_traffic_key(paze_tls_traffic_key_t *tk,
|
||||
paze_tls_aead_kind_t aead,
|
||||
const uint8_t *key, size_t klen,
|
||||
const uint8_t *fixed_iv) {
|
||||
paze_memzero(tk, sizeof(*tk));
|
||||
tk->kind = aead;
|
||||
tk->key_len = klen;
|
||||
memcpy(tk->key, key, klen);
|
||||
memcpy(tk->iv, fixed_iv, 4); /* record 层约定 implicit IV 在前 4 字节 */
|
||||
tk->seq = 0;
|
||||
if (aead == PAZE_AEAD_AES_128_GCM || aead == PAZE_AEAD_AES_256_GCM)
|
||||
paze_aes_init(&tk->aes, tk->key, (int)klen * 8);
|
||||
}
|
||||
|
||||
/* master_secret + key_block 推导;配置 handshake_read/write */
|
||||
static paze_status_t tls12_derive_master(paze_tls_t *tls,
|
||||
const uint8_t *pms, size_t pms_len) {
|
||||
uint8_t seed[64];
|
||||
memcpy(seed, tls->client_random, 32);
|
||||
memcpy(seed + 32, tls->server_random, 32);
|
||||
paze_status_t st = paze_tls12_prf(paze_hash_sha256(), pms, pms_len,
|
||||
"master secret", seed, 64, tls->ms12, 48);
|
||||
if (st != PAZE_OK) return st;
|
||||
tls->ms12_valid = 1;
|
||||
|
||||
/* key_block: [client_MAC][server_MAC][client_key][server_key][client_IV][server_IV]
|
||||
* AEAD: MAC 0, key klen, IV 4 */
|
||||
size_t klen = (tls->neg.aead == PAZE_AEAD_AES_128_GCM) ? 16 : 32;
|
||||
size_t kb_len = 2 * klen + 8;
|
||||
uint8_t kb[80];
|
||||
uint8_t seed2[64];
|
||||
memcpy(seed2, tls->server_random, 32);
|
||||
memcpy(seed2 + 32, tls->client_random, 32);
|
||||
st = paze_tls12_prf(paze_hash_sha256(), tls->ms12, 48,
|
||||
"key expansion", seed2, 64, kb, kb_len);
|
||||
if (st != PAZE_OK) { paze_memzero(kb, sizeof(kb)); return st; }
|
||||
|
||||
paze_tls_aead_kind_t aead = tls->neg.aead;
|
||||
if (tls->role == PAZE_TLS_CLIENT) {
|
||||
tls12_set_traffic_key(&tls->handshake_write, aead, kb, klen, kb + 2 * klen);
|
||||
tls12_set_traffic_key(&tls->handshake_read, aead, kb + klen, klen, kb + 2 * klen + 4);
|
||||
} else {
|
||||
tls12_set_traffic_key(&tls->handshake_read, aead, kb, klen, kb + 2 * klen);
|
||||
tls12_set_traffic_key(&tls->handshake_write, aead, kb + klen, klen, kb + 2 * klen + 4);
|
||||
}
|
||||
paze_memzero(kb, sizeof(kb));
|
||||
paze_memzero(seed, sizeof(seed));
|
||||
paze_memzero(seed2, sizeof(seed2));
|
||||
return PAZE_OK;
|
||||
}
|
||||
|
||||
/* 判断 cipher 是否为 TLS 1.3 套件 */
|
||||
static int tls12_is_v13_cipher(uint16_t c) {
|
||||
return c == PAZE_TLS_AES_128_GCM_SHA256 ||
|
||||
c == PAZE_TLS_AES_256_GCM_SHA384 ||
|
||||
c == PAZE_TLS_CHACHA20_POLY1305_SHA256;
|
||||
}
|
||||
|
||||
/* 判断 cipher 是否 ECDHE(需 ServerKeyExchange) */
|
||||
static int tls12_is_ecdhe(uint16_t c) {
|
||||
return c == PAZE_TLS_ECDHE_RSA_AES128_GCM_SHA256 ||
|
||||
c == PAZE_TLS_ECDHE_ECDSA_AES128_GCM_SHA256 ||
|
||||
c == PAZE_TLS_ECDHE_RSA_CHACHA20_POLY1305;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* [客户端] 解析 ServerHello(存于 tls->sh1,含 4 字节头)
|
||||
* ============================================================ */
|
||||
static paze_status_t tls12_client_parse_sh(paze_tls_t *tls) {
|
||||
const uint8_t *body = tls->sh1 + 4;
|
||||
size_t blen = tls->sh1_len - 4;
|
||||
if (blen < 2 + 32 + 1 + 2 + 1) { paze_tls_set_err(tls, "short 1.2 SH"); return PAZE_ERR_FORMAT; }
|
||||
uint16_t leg = paze_load16_be(body);
|
||||
if (leg != PAZE_TLS_VERSION_1_2) {
|
||||
paze_tls_set_err(tls, "1.2 SH legacy_version %04x", leg);
|
||||
return PAZE_ERR_PROTOCOL;
|
||||
}
|
||||
memcpy(tls->server_random, body + 2, 32);
|
||||
const uint8_t *p = body + 2 + 32;
|
||||
uint8_t sid_len = *p++;
|
||||
if ((size_t)(p - body) + sid_len + 2 + 1 > blen) { paze_tls_set_err(tls, "1.2 SH sid"); return PAZE_ERR_FORMAT; }
|
||||
p += sid_len;
|
||||
uint16_t cipher = paze_load16_be(p); p += 2;
|
||||
p++; /* compression_method */
|
||||
if (tls12_is_v13_cipher(cipher)) {
|
||||
paze_tls_set_err(tls, "server selected 1.3 cipher in 1.2 SH");
|
||||
return PAZE_ERR_PROTOCOL;
|
||||
}
|
||||
tls->neg.cipher = cipher;
|
||||
paze_tls_cipher_params(cipher, &tls->neg.aead, &tls->neg.hash,
|
||||
&tls->neg.key_len, &tls->neg.hash_len);
|
||||
if (tls->neg.aead == PAZE_AEAD_NONE) {
|
||||
paze_tls_set_err(tls, "1.2 cipher %04x not supported", cipher);
|
||||
return PAZE_ERR_NOTSUP;
|
||||
}
|
||||
tls->neg.version = PAZE_TLS_VERSION_1_2;
|
||||
return PAZE_OK;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* [客户端] 解析 Certificate(TLS 1.2 格式)
|
||||
* ============================================================ */
|
||||
static paze_status_t tls12_client_parse_cert(paze_tls_t *tls,
|
||||
const uint8_t *body, size_t blen) {
|
||||
if (blen < 3) return PAZE_ERR_FORMAT;
|
||||
size_t total = ((size_t)body[0] << 16) | ((size_t)body[1] << 8) | body[2];
|
||||
if (total != blen - 3) return PAZE_ERR_FORMAT;
|
||||
size_t p = 3, i = 0;
|
||||
tls->peer_chain_len = 0;
|
||||
while (p < blen && i < PAZE_TLS_MAX_CERTS) {
|
||||
if (blen < p + 3) return PAZE_ERR_FORMAT;
|
||||
size_t cl = ((size_t)body[p] << 16) | ((size_t)body[p+1] << 8) | body[p+2];
|
||||
p += 3;
|
||||
if (blen < p + cl) return PAZE_ERR_FORMAT;
|
||||
paze_status_t st = paze_x509_parse_der(&tls->peer_chain[i], body + p, cl);
|
||||
if (st != PAZE_OK) return PAZE_ERR_FORMAT;
|
||||
tls->peer_chain_len++; i++; p += cl;
|
||||
}
|
||||
return PAZE_OK;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* [客户端] 解析 ServerKeyExchange(ECDHE):params + 签名验证
|
||||
* ============================================================ */
|
||||
static paze_status_t tls12_client_parse_ske(paze_tls_t *tls,
|
||||
const uint8_t *body, size_t blen) {
|
||||
if (blen < 4) return PAZE_ERR_FORMAT;
|
||||
uint8_t curve_type = body[0];
|
||||
if (curve_type != 0x03) { paze_tls_set_err(tls, "unsupported curve_type"); return PAZE_ERR_NOTSUP; }
|
||||
uint16_t nc = paze_load16_be(body + 1);
|
||||
uint8_t plen = body[3];
|
||||
if (blen < (size_t)(4 + plen + 4)) return PAZE_ERR_FORMAT;
|
||||
size_t params_len = 4 + plen;
|
||||
const uint8_t *params = body;
|
||||
uint16_t sig_alg = paze_load16_be(body + params_len);
|
||||
uint16_t sig_len = paze_load16_be(body + params_len + 2);
|
||||
if (blen != params_len + 4 + sig_len) return PAZE_ERR_FORMAT;
|
||||
const uint8_t *sig = body + params_len + 4;
|
||||
|
||||
tls->neg.group = nc;
|
||||
if (plen > sizeof(tls->ske_pub)) return PAZE_ERR_FORMAT;
|
||||
memcpy(tls->ske_pub, body + 4, plen);
|
||||
tls->ske_pub_len = plen;
|
||||
|
||||
/* 签名数据 = client_random || server_random || params */
|
||||
uint8_t sd[32 + 32 + 70];
|
||||
memcpy(sd, tls->client_random, 32);
|
||||
memcpy(sd + 32, tls->server_random, 32);
|
||||
memcpy(sd + 64, params, params_len);
|
||||
size_t sd_len = 64 + params_len;
|
||||
|
||||
uint8_t h[64]; size_t hl; paze_rsa_hash_t rh;
|
||||
switch (sig_alg) {
|
||||
case PAZE_TLS_SIG_RSA_PKCS1_SHA256:
|
||||
case PAZE_TLS_SIG_RSA_PSS_RSAE_SHA256:
|
||||
rh = PAZE_RSA_HASH_SHA256; hl = 32; break;
|
||||
case PAZE_TLS_SIG_RSA_PKCS1_SHA384:
|
||||
case PAZE_TLS_SIG_RSA_PSS_RSAE_SHA384:
|
||||
rh = PAZE_RSA_HASH_SHA384; hl = 48; break;
|
||||
case PAZE_TLS_SIG_ECDSA_SECP256R1_SHA256:
|
||||
rh = PAZE_RSA_HASH_SHA256; hl = 32; break;
|
||||
default:
|
||||
paze_tls_set_err(tls, "1.2 SKE sig alg %04x", sig_alg);
|
||||
return PAZE_ERR_NOTSUP;
|
||||
}
|
||||
if (rh == PAZE_RSA_HASH_SHA256) paze_sha256(sd, sd_len, h);
|
||||
else paze_sha384(sd, sd_len, h);
|
||||
|
||||
const paze_x509_cert_t *leaf = &tls->peer_chain[0];
|
||||
paze_status_t st;
|
||||
if (leaf->pk_kind == PAZE_X509_PK_RSA) {
|
||||
if (sig_alg == PAZE_TLS_SIG_RSA_PSS_RSAE_SHA256 || sig_alg == PAZE_TLS_SIG_RSA_PSS_RSAE_SHA384)
|
||||
st = paze_rsa_pss_verify(&leaf->rsa_pub, rh, h, hl, sig, sig_len);
|
||||
else
|
||||
st = paze_rsa_pkcs1_verify(&leaf->rsa_pub, rh, h, hl, sig, sig_len);
|
||||
} else if (leaf->pk_kind == PAZE_X509_PK_ECDSA_P256 && sig_alg == PAZE_TLS_SIG_ECDSA_SECP256R1_SHA256) {
|
||||
st = paze_ecdsa_verify(&leaf->ecdsa_pub, rh, h, hl, sig, sig_len);
|
||||
} else {
|
||||
st = PAZE_ERR_VERIFY;
|
||||
}
|
||||
if (st != PAZE_OK) {
|
||||
paze_tls_set_err(tls, "1.2 SKE signature verify failed");
|
||||
return PAZE_ERR_VERIFY;
|
||||
}
|
||||
return PAZE_OK;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* [客户端] 证书链验证(与 1.3 客户端一致)
|
||||
* ============================================================ */
|
||||
static paze_status_t tls12_client_verify_cert(paze_tls_t *tls) {
|
||||
paze_tls_config_t *cfg = tls->cfg_owned;
|
||||
if (cfg->verify_mode == PAZE_TLS_VERIFY_NONE || tls->peer_chain_len == 0) return PAZE_OK;
|
||||
paze_status_t vs = PAZE_OK;
|
||||
if (cfg->anchors && cfg->anchor_count > 0) {
|
||||
vs = paze_x509_verify_chain(tls->peer_chain, tls->peer_chain_len,
|
||||
cfg->anchors, cfg->anchor_count,
|
||||
(int64_t)time(NULL));
|
||||
} else if (cfg->verify_mode == PAZE_TLS_VERIFY_REQUIRED) {
|
||||
vs = PAZE_ERR_VERIFY;
|
||||
}
|
||||
if (cfg->sni[0] && tls->peer_chain_len > 0) {
|
||||
if (!paze_x509_match_host(&tls->peer_chain[0], cfg->sni)) {
|
||||
if (vs == PAZE_OK) vs = PAZE_ERR_VERIFY;
|
||||
}
|
||||
}
|
||||
if (cfg->verify_cb) {
|
||||
if (!cfg->verify_cb(cfg->verify_ctx, tls->peer_chain, tls->peer_chain_len, vs))
|
||||
return PAZE_ERR_VERIFY;
|
||||
} else if (cfg->verify_mode == PAZE_TLS_VERIFY_REQUIRED && vs != PAZE_OK) {
|
||||
paze_tls_set_err(tls, "cert verification failed (%d)", vs);
|
||||
return PAZE_ERR_VERIFY;
|
||||
}
|
||||
return PAZE_OK;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* [客户端] 构造 ClientKeyExchange 并缓存 pre_master_secret
|
||||
* ============================================================ */
|
||||
static paze_status_t tls12_client_build_cke(paze_tls_t *tls,
|
||||
uint8_t *out, size_t *outlen) {
|
||||
const paze_x509_cert_t *leaf = &tls->peer_chain[0];
|
||||
if (tls12_is_ecdhe(tls->neg.cipher)) {
|
||||
const uint8_t *pub; size_t plen;
|
||||
if (tls->neg.group == PAZE_TLS_GROUP_X25519) { pub = tls->kex.x25519_pub; plen = 32; }
|
||||
else if (tls->neg.group == PAZE_TLS_GROUP_SECP256R1) { pub = tls->kex.p256_pub; plen = 65; }
|
||||
else { paze_tls_set_err(tls, "1.2 group %04x not supported", tls->neg.group); return PAZE_ERR_NOTSUP; }
|
||||
if (1 + plen > 128) return PAZE_ERR_OVERFLOW;
|
||||
out[0] = (uint8_t)plen; /* RFC 5246 §7.4.7:ECDHE point 用 1 字节长度 */
|
||||
memcpy(out + 1, pub, plen);
|
||||
*outlen = 1 + plen;
|
||||
|
||||
uint8_t shared[64]; size_t shared_len = 0;
|
||||
paze_status_t st = paze_tls_kex_ecdh(&tls->kex, tls->neg.group,
|
||||
tls->ske_pub, tls->ske_pub_len,
|
||||
shared, &shared_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
memcpy(tls->pms12, shared, shared_len);
|
||||
tls->pms12_len = shared_len;
|
||||
paze_memzero(shared, sizeof(shared));
|
||||
return PAZE_OK;
|
||||
}
|
||||
/* 静态 RSA */
|
||||
if (leaf->pk_kind != PAZE_X509_PK_RSA) {
|
||||
paze_tls_set_err(tls, "RSA cipher but no RSA cert");
|
||||
return PAZE_ERR_VERIFY;
|
||||
}
|
||||
uint8_t pms[48];
|
||||
pms[0] = 0x03; pms[1] = 0x03;
|
||||
if (paze_random(pms + 2, 44) != PAZE_OK) return PAZE_ERR_PLATFORM;
|
||||
size_t k = paze_rsa_modulus_len(&leaf->rsa_pub.n);
|
||||
uint8_t enc[512];
|
||||
paze_status_t st = paze_rsa_pkcs1_encrypt(&leaf->rsa_pub, pms, 46, enc, k);
|
||||
if (st != PAZE_OK) return st;
|
||||
paze_store16_be(out, (uint16_t)k);
|
||||
memcpy(out + 2, enc, k);
|
||||
*outlen = 2 + k;
|
||||
memcpy(tls->pms12, pms, 46);
|
||||
tls->pms12_len = 46;
|
||||
paze_memzero(pms, sizeof(pms));
|
||||
paze_memzero(enc, sizeof(enc));
|
||||
return PAZE_OK;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* [客户端] 主入口
|
||||
* ============================================================ */
|
||||
paze_status_t paze_tls12_client_handshake(paze_tls_t *tls) {
|
||||
(void)tls;
|
||||
paze_set_err("TLS 1.2 handshake not implemented yet");
|
||||
return PAZE_ERR_NOTSUP;
|
||||
paze_status_t st;
|
||||
|
||||
/* ---- 阶段 A:发送 ClientHello / 等待并解析 ServerHello ---- */
|
||||
if (tls->hs_state == PAZE_HS_INIT) {
|
||||
st = paze_tls_kex_generate(&tls->kex, 0);
|
||||
if (st != PAZE_OK) return st;
|
||||
paze_tls_tr_init(&tls->transcript, paze_hash_sha256());
|
||||
uint8_t ch[PAZE_TLS_MAX_RECORD]; size_t ch_len = 0;
|
||||
st = paze_tls13_build_ch(tls, ch, &ch_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
free(tls->ch1);
|
||||
tls->ch1 = (uint8_t *)malloc(ch_len);
|
||||
if (!tls->ch1) return PAZE_ERR_PLATFORM;
|
||||
memcpy(tls->ch1, ch, ch_len);
|
||||
tls->ch1_len = ch_len;
|
||||
paze_tls_tr_update(&tls->transcript, ch, ch_len);
|
||||
paze_tls_record_send_plain(tls, PAZE_TLS_CT_HANDSHAKE, ch, ch_len);
|
||||
tls->hs_state = PAZE_HS_CL12_WAIT_SHD;
|
||||
}
|
||||
if (tls->hs_state == PAZE_HS_CL12_WAIT_SHD || tls->hs_state == PAZE_HS_CL_WAIT_SH) {
|
||||
/* 从 1.3 回退时 sh1 已保存;独立模式需读取 */
|
||||
if (!tls->sh1) {
|
||||
const uint8_t *body, *raw; size_t blen, raw_len; uint8_t type; int ccs;
|
||||
st = tls12_recv_hs(tls, &body, &blen, &type, &ccs, &raw, &raw_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (ccs || type != PAZE_TLS_HS_SERVER_HELLO) {
|
||||
paze_tls_set_err(tls, "expected 1.2 ServerHello, got %d", type);
|
||||
return PAZE_ERR_PROTOCOL;
|
||||
}
|
||||
tls->sh1 = (uint8_t *)malloc(raw_len);
|
||||
if (!tls->sh1) return PAZE_ERR_PLATFORM;
|
||||
memcpy(tls->sh1, raw, raw_len);
|
||||
tls->sh1_len = raw_len;
|
||||
}
|
||||
/* 记录 client_random(CH body 偏移:头 4 + legacy_ver 2) */
|
||||
if (tls->ch1 && tls->ch1_len >= 4 + 34) {
|
||||
memcpy(tls->client_random, tls->ch1 + 4 + 2, 32);
|
||||
}
|
||||
st = tls12_client_parse_sh(tls);
|
||||
if (st != PAZE_OK) return st;
|
||||
tls->hs_state = PAZE_HS_CL12_WAIT_CERT_SKE_SHD;
|
||||
}
|
||||
|
||||
/* ---- 阶段 B:Certificate / ServerKeyExchange / ServerHelloDone ---- */
|
||||
while (tls->hs_state == PAZE_HS_CL12_WAIT_CERT_SKE_SHD) {
|
||||
const uint8_t *body; size_t blen; uint8_t type; int ccs;
|
||||
st = tls12_recv_hs(tls, &body, &blen, &type, &ccs, NULL, NULL);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (ccs) { paze_tls_set_err(tls, "unexpected CCS before SHD"); return PAZE_ERR_PROTOCOL; }
|
||||
if (type == PAZE_TLS_HS_CERTIFICATE_REQUEST) continue; /* 客户端证书请求:忽略 */
|
||||
if (type == PAZE_TLS_HS_CERTIFICATE) {
|
||||
st = tls12_client_parse_cert(tls, body, blen);
|
||||
if (st != PAZE_OK) return st;
|
||||
continue;
|
||||
}
|
||||
if (type == PAZE_TLS_HS_SERVER_KEY_EXCHANGE) {
|
||||
st = tls12_client_parse_ske(tls, body, blen);
|
||||
if (st != PAZE_OK) return st;
|
||||
continue;
|
||||
}
|
||||
if (type == PAZE_TLS_HS_SERVER_HELLO_DONE) break;
|
||||
paze_tls_set_err(tls, "unexpected hs %d in 1.2", type);
|
||||
return PAZE_ERR_PROTOCOL;
|
||||
}
|
||||
if (tls->hs_state == PAZE_HS_CL12_WAIT_CERT_SKE_SHD) {
|
||||
if (tls->peer_chain_len == 0) {
|
||||
paze_tls_set_err(tls, "no server certificate in 1.2");
|
||||
return PAZE_ERR_PROTOCOL;
|
||||
}
|
||||
st = tls12_client_verify_cert(tls);
|
||||
if (st != PAZE_OK) return st;
|
||||
/* 构造 CKE(缓存 pre-master secret) */
|
||||
uint8_t cke[600]; size_t cke_len = 0;
|
||||
st = tls12_client_build_cke(tls, cke, &cke_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
st = tls12_send_hs(tls, PAZE_TLS_HS_CLIENT_KEY_EXCHANGE, cke, cke_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
paze_memzero(cke, sizeof(cke));
|
||||
tls->hs_state = PAZE_HS_CL12_SEND_CKE;
|
||||
}
|
||||
|
||||
/* ---- 阶段 C:发送 CCS + Finished ---- */
|
||||
if (tls->hs_state == PAZE_HS_CL12_SEND_CKE) {
|
||||
st = tls12_derive_master(tls, tls->pms12, tls->pms12_len);
|
||||
paze_memzero(tls->pms12, sizeof(tls->pms12));
|
||||
tls->pms12_len = 0;
|
||||
if (st != PAZE_OK) return st;
|
||||
|
||||
uint8_t ccs[1] = { 0x01 };
|
||||
st = paze_tls_record_send_plain(tls, PAZE_TLS_CT_CHANGE_CIPHER_SPEC, ccs, 1);
|
||||
if (st != PAZE_OK) return st;
|
||||
tls->enc_write = 1;
|
||||
tls->write_key = &tls->handshake_write;
|
||||
|
||||
uint8_t vd[12];
|
||||
st = tls12_finished(tls, "client finished", vd);
|
||||
if (st != PAZE_OK) return st;
|
||||
st = tls12_send_hs(tls, PAZE_TLS_HS_FINISHED, vd, 12);
|
||||
if (st != PAZE_OK) return st;
|
||||
paze_memzero(vd, sizeof(vd));
|
||||
tls->hs_state = PAZE_HS_CL12_WAIT_CCS_FIN;
|
||||
}
|
||||
|
||||
/* ---- 阶段 D:等 CCS + Finished,验证 ---- */
|
||||
while (tls->hs_state == PAZE_HS_CL12_WAIT_CCS_FIN) {
|
||||
st = tls12_wait_ccs(tls, 1);
|
||||
if (st != PAZE_OK) return st;
|
||||
const uint8_t *body; size_t blen; uint8_t type; int ccs;
|
||||
st = tls12_recv_hs(tls, &body, &blen, &type, &ccs, NULL, NULL);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (ccs) { paze_tls_set_err(tls, "duplicate CCS"); return PAZE_ERR_PROTOCOL; }
|
||||
if (type == PAZE_TLS_HS_NEW_SESSION_TICKET) continue;
|
||||
if (type != PAZE_TLS_HS_FINISHED) {
|
||||
paze_tls_set_err(tls, "expected server Finished, got %d", type);
|
||||
return PAZE_ERR_PROTOCOL;
|
||||
}
|
||||
if (blen != 12) { paze_tls_set_err(tls, "bad 1.2 Finished len"); return PAZE_ERR_VERIFY; }
|
||||
uint8_t exp[12];
|
||||
st = tls12_finished(tls, "server finished", exp);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (paze_ct_equal(exp, body, 12) != 0) {
|
||||
paze_tls_set_err(tls, "server 1.2 Finished verify failed");
|
||||
return PAZE_ERR_VERIFY;
|
||||
}
|
||||
paze_memzero(exp, sizeof(exp));
|
||||
tls->handshake_done = 1;
|
||||
tls->hs_state = PAZE_HS_CL_DONE;
|
||||
}
|
||||
return PAZE_OK;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* [服务端] ClientHello 解析(存于 tls->ch1,含 4 字节头)
|
||||
* ============================================================ */
|
||||
typedef struct {
|
||||
uint16_t group; /* supported_groups 首选 */
|
||||
char sni[PAZE_TLS_SNI_MAX + 1];
|
||||
int have_sni;
|
||||
int have_renego;
|
||||
} ch12_ext_ctx_t;
|
||||
|
||||
static int ch12_ext_cb(uint16_t tp, const uint8_t *b, size_t l, void *x) {
|
||||
ch12_ext_ctx_t *s = (ch12_ext_ctx_t *)x;
|
||||
if (tp == PAZE_TLS_EXT_SUPPORTED_GROUPS) {
|
||||
s->group = paze_tls_ext_parse_supported_groups(b, l);
|
||||
} else if (tp == PAZE_TLS_EXT_SERVER_NAME) {
|
||||
if (paze_tls_ext_parse_sni(b, l, s->sni)) s->have_sni = 1;
|
||||
} else if (tp == PAZE_TLS_EXT_RENEGOTIATION_INFO) {
|
||||
s->have_renego = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static paze_status_t tls12_server_parse_ch(paze_tls_t *tls,
|
||||
uint16_t *client_suites, size_t *n_suites) {
|
||||
const uint8_t *body = tls->ch1 + 4;
|
||||
size_t blen = tls->ch1_len - 4;
|
||||
if (blen < 2 + 32 + 1 + 2) return PAZE_ERR_FORMAT;
|
||||
memcpy(tls->client_random, body + 2, 32);
|
||||
const uint8_t *p = body + 2 + 32;
|
||||
uint8_t sid_len = *p++;
|
||||
if ((size_t)(p - body) + sid_len + 2 > blen) return PAZE_ERR_FORMAT;
|
||||
p += sid_len; /* sid 原样回显(阶段 B 直接从 ch1 读取) */
|
||||
size_t slen = paze_load16_be(p); p += 2;
|
||||
if ((size_t)(p - body) + slen > blen) return PAZE_ERR_FORMAT;
|
||||
if (slen & 1) return PAZE_ERR_FORMAT;
|
||||
*n_suites = slen / 2;
|
||||
if (*n_suites > 64) return PAZE_ERR_FORMAT;
|
||||
for (size_t i = 0; i < *n_suites; i++)
|
||||
client_suites[i] = paze_load16_be(p + 2 * i);
|
||||
p += slen;
|
||||
if ((size_t)(p - body) + 1 > blen) return PAZE_ERR_FORMAT;
|
||||
uint8_t comp_len = *p++;
|
||||
if ((size_t)(p - body) + comp_len > blen) return PAZE_ERR_FORMAT;
|
||||
p += comp_len;
|
||||
return PAZE_OK;
|
||||
}
|
||||
|
||||
/* 选择 1.2 cipher:按服务端偏好,过滤私钥类型 */
|
||||
static uint16_t tls12_pick_cipher(const uint16_t *list, size_t n, int priv_is_ecdsa) {
|
||||
static const uint16_t pref_rsa[] = {
|
||||
PAZE_TLS_ECDHE_RSA_AES128_GCM_SHA256,
|
||||
PAZE_TLS_ECDHE_RSA_CHACHA20_POLY1305,
|
||||
PAZE_TLS_RSA_AES128_GCM_SHA256
|
||||
};
|
||||
static const uint16_t pref_ecdsa[] = {
|
||||
PAZE_TLS_ECDHE_ECDSA_AES128_GCM_SHA256
|
||||
};
|
||||
const uint16_t *pref = priv_is_ecdsa ? pref_ecdsa : pref_rsa;
|
||||
size_t pn = priv_is_ecdsa ? 1 : 3;
|
||||
for (size_t i = 0; i < pn; i++) {
|
||||
for (size_t j = 0; j < n; j++) {
|
||||
if (list[j] == pref[i]) return pref[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* [服务端] 构造 ServerKeyExchange 并签名
|
||||
* ============================================================ */
|
||||
static paze_status_t tls12_server_build_ske(paze_tls_t *tls,
|
||||
uint8_t *out, size_t *outlen) {
|
||||
paze_tls_config_t *cfg = tls->cfg_owned;
|
||||
uint16_t nc = tls->neg.group;
|
||||
const uint8_t *pub; size_t plen;
|
||||
if (nc == PAZE_TLS_GROUP_X25519) { pub = tls->kex.x25519_pub; plen = 32; }
|
||||
else if (nc == PAZE_TLS_GROUP_SECP256R1) { pub = tls->kex.p256_pub; plen = 65; }
|
||||
else return PAZE_ERR_NOTSUP;
|
||||
|
||||
out[0] = 0x03;
|
||||
paze_store16_be(out + 1, nc);
|
||||
out[3] = (uint8_t)plen;
|
||||
memcpy(out + 4, pub, plen);
|
||||
size_t params_len = 4 + plen;
|
||||
|
||||
/* 签名数据 = client_random || server_random || params */
|
||||
uint8_t sd[32 + 32 + 70];
|
||||
memcpy(sd, tls->client_random, 32);
|
||||
memcpy(sd + 32, tls->server_random, 32);
|
||||
memcpy(sd + 64, out, params_len);
|
||||
size_t sd_len = 64 + params_len;
|
||||
|
||||
size_t sig_cap = 512; /* RSA-2048 签名 256 字节;ECDSA P-256 签名 ≤ 70 */
|
||||
uint8_t *sig = out + params_len + 4;
|
||||
size_t sig_len = sig_cap;
|
||||
uint16_t sig_alg;
|
||||
uint8_t h[64];
|
||||
if (cfg->privkey.kind == PAZE_X509_PK_RSA) {
|
||||
sig_alg = PAZE_TLS_SIG_RSA_PKCS1_SHA256;
|
||||
paze_sha256(sd, sd_len, h);
|
||||
paze_status_t st = paze_rsa_pkcs1_sign(&cfg->privkey.rsa, PAZE_RSA_HASH_SHA256,
|
||||
h, 32, sig, sig_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
sig_len = paze_rsa_modulus_len(&cfg->privkey.rsa.n);
|
||||
} else if (cfg->privkey.kind == PAZE_X509_PK_ECDSA_P256) {
|
||||
sig_alg = PAZE_TLS_SIG_ECDSA_SECP256R1_SHA256;
|
||||
paze_sha256(sd, sd_len, h);
|
||||
paze_ecdsa_priv_t ek;
|
||||
paze_ecdsa_priv_set_d(&ek, cfg->privkey.ecdsa_d);
|
||||
paze_status_t st = paze_ecdsa_sign(&ek, PAZE_RSA_HASH_SHA256, h, 32, sig, &sig_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
} else {
|
||||
return PAZE_ERR_NOTSUP;
|
||||
}
|
||||
paze_store16_be(out + params_len, sig_alg);
|
||||
paze_store16_be(out + params_len + 2, (uint16_t)sig_len);
|
||||
*outlen = params_len + 4 + sig_len;
|
||||
paze_memzero(sd, sizeof(sd));
|
||||
return PAZE_OK;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* [服务端] 解析 ClientKeyExchange 得 pre-master secret
|
||||
* ============================================================ */
|
||||
static paze_status_t tls12_server_parse_cke(paze_tls_t *tls,
|
||||
const uint8_t *body, size_t blen) {
|
||||
paze_tls_config_t *cfg = tls->cfg_owned;
|
||||
if (tls12_is_ecdhe(tls->neg.cipher)) {
|
||||
if (blen < 1) return PAZE_ERR_FORMAT;
|
||||
size_t plen = body[0]; /* RFC 5246 §7.4.7:ECDHE point 用 1 字节长度 */
|
||||
if (blen != 1 + plen) return PAZE_ERR_FORMAT;
|
||||
uint8_t shared[64]; size_t shared_len = 0;
|
||||
paze_status_t st = paze_tls_kex_ecdh(&tls->kex, tls->neg.group,
|
||||
body + 1, plen, shared, &shared_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
memcpy(tls->pms12, shared, shared_len);
|
||||
tls->pms12_len = shared_len;
|
||||
paze_memzero(shared, sizeof(shared));
|
||||
return PAZE_OK;
|
||||
}
|
||||
/* 静态 RSA:解密 pre-master secret */
|
||||
if (cfg->privkey.kind != PAZE_X509_PK_RSA) return PAZE_ERR_VERIFY;
|
||||
if (blen < 2) return PAZE_ERR_FORMAT;
|
||||
size_t k = paze_load16_be(body);
|
||||
if (blen != 2 + k) return PAZE_ERR_FORMAT;
|
||||
uint8_t pms[48]; size_t pms_len = sizeof(pms);
|
||||
paze_status_t st = paze_rsa_pkcs1_decrypt(&cfg->privkey.rsa, body + 2, k,
|
||||
pms, &pms_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (pms_len != 46 || pms[0] != 0x03 || pms[1] != 0x03) return PAZE_ERR_VERIFY;
|
||||
memcpy(tls->pms12, pms, 46);
|
||||
tls->pms12_len = 46;
|
||||
paze_memzero(pms, sizeof(pms));
|
||||
return PAZE_OK;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* [服务端] 主入口
|
||||
* ============================================================ */
|
||||
paze_status_t paze_tls12_server_handshake(paze_tls_t *tls) {
|
||||
(void)tls;
|
||||
paze_set_err("TLS 1.2 server handshake not implemented yet");
|
||||
return PAZE_ERR_NOTSUP;
|
||||
paze_tls_config_t *cfg = tls->cfg_owned;
|
||||
paze_status_t st;
|
||||
uint16_t client_suites[64]; size_t n_suites = 0;
|
||||
|
||||
/* ---- 阶段 A:解析 ClientHello、选择参数 ---- */
|
||||
if (tls->hs_state == PAZE_HS_INIT) {
|
||||
int ch_was_present = (tls->ch1 != NULL);
|
||||
/* 必须先初始化 transcript,再收 ClientHello(recv 会将其计入) */
|
||||
paze_tls_tr_init(&tls->transcript, paze_hash_sha256());
|
||||
if (!tls->ch1) {
|
||||
const uint8_t *body, *raw; size_t blen, raw_len; uint8_t type; int ccs;
|
||||
st = tls12_recv_hs(tls, &body, &blen, &type, &ccs, &raw, &raw_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (ccs || type != PAZE_TLS_HS_CLIENT_HELLO) {
|
||||
paze_tls_set_err(tls, "expected ClientHello, got %d", type);
|
||||
return PAZE_ERR_PROTOCOL;
|
||||
}
|
||||
tls->ch1 = (uint8_t *)malloc(raw_len);
|
||||
if (!tls->ch1) return PAZE_ERR_PLATFORM;
|
||||
memcpy(tls->ch1, raw, raw_len);
|
||||
tls->ch1_len = raw_len;
|
||||
}
|
||||
/* 仅从 1.3 回退(ch1 已存在)时需要重放:recv_hs 已把 CH 计入旧
|
||||
* transcript,重建后必须重放;独立模式 recv_hs 已计入新 transcript。 */
|
||||
if (ch_was_present)
|
||||
paze_tls_tr_update(&tls->transcript, tls->ch1, tls->ch1_len);
|
||||
|
||||
st = tls12_server_parse_ch(tls, client_suites, &n_suites);
|
||||
if (st != PAZE_OK) return st;
|
||||
|
||||
/* 选 cipher */
|
||||
int priv_is_ecdsa = (cfg->privkey.kind == PAZE_X509_PK_ECDSA_P256);
|
||||
uint16_t cipher = tls12_pick_cipher(client_suites, n_suites, priv_is_ecdsa);
|
||||
if (!cipher) {
|
||||
paze_tls_set_err(tls, "no common 1.2 cipher suite");
|
||||
return PAZE_ERR_NOTSUP;
|
||||
}
|
||||
tls->neg.cipher = cipher;
|
||||
paze_tls_cipher_params(cipher, &tls->neg.aead, &tls->neg.hash,
|
||||
&tls->neg.key_len, &tls->neg.hash_len);
|
||||
tls->neg.version = PAZE_TLS_VERSION_1_2;
|
||||
|
||||
/* 选 group(仅 ECDHE):客户端 supported_groups 优先,缺省 P-256 */
|
||||
ch12_ext_ctx_t ex; memset(&ex, 0, sizeof(ex));
|
||||
if (tls->ch1_len >= 4 + 2) {
|
||||
const uint8_t *body = tls->ch1 + 4;
|
||||
size_t blen = tls->ch1_len - 4;
|
||||
const uint8_t *p = body + 2 + 32;
|
||||
uint8_t sl = *p++;
|
||||
p += sl;
|
||||
size_t csl = paze_load16_be(p); p += 2;
|
||||
p += csl;
|
||||
uint8_t cl = *p++;
|
||||
p += cl;
|
||||
if ((size_t)(p - body) + 2 <= blen) {
|
||||
size_t ext_total = paze_load16_be(p); p += 2;
|
||||
if ((size_t)(p - body) + ext_total <= blen)
|
||||
paze_tls_extensions_foreach(p - 2, 2 + ext_total, ch12_ext_cb, &ex);
|
||||
}
|
||||
}
|
||||
tls->neg.group = ex.group ? ex.group : PAZE_TLS_GROUP_SECP256R1;
|
||||
if (tls12_is_ecdhe(cipher)) {
|
||||
st = paze_tls_kex_generate(&tls->kex, tls->neg.group == PAZE_TLS_GROUP_SECP256R1);
|
||||
if (st != PAZE_OK) return st;
|
||||
}
|
||||
|
||||
if (paze_random(tls->server_random, 32) != PAZE_OK) return PAZE_ERR_PLATFORM;
|
||||
tls->hs_state = PAZE_HS_SV12_SEND_SH_CERT_SKE_SHD;
|
||||
}
|
||||
|
||||
/* ---- 阶段 B:ServerHello + Certificate [+ SKE] + ServerHelloDone ---- */
|
||||
while (tls->hs_state == PAZE_HS_SV12_SEND_SH_CERT_SKE_SHD) {
|
||||
/* ServerHello */
|
||||
uint8_t sh[1024];
|
||||
uint8_t *p = sh + 4;
|
||||
paze_store16_be(p, PAZE_TLS_LEGACY_VERSION); p += 2;
|
||||
memcpy(p, tls->server_random, 32); p += 32;
|
||||
const uint8_t *chp = tls->ch1 + 4 + 2 + 32;
|
||||
uint8_t ch_sid_len = *chp++;
|
||||
*p++ = ch_sid_len;
|
||||
memcpy(p, chp, ch_sid_len); p += ch_sid_len;
|
||||
paze_store16_be(p, tls->neg.cipher); p += 2;
|
||||
*p++ = 0x00; /* compression_method = null */
|
||||
uint8_t *extp = p; p += 2;
|
||||
/* renegotiation_info(空)回显 */
|
||||
size_t n = paze_tls_ext_write_renegotiation_info(p); p += n;
|
||||
paze_store16_be(extp, (uint16_t)(p - extp - 2));
|
||||
size_t body_len = (size_t)(p - (sh + 4));
|
||||
paze_tls_hs_put_header(sh, PAZE_TLS_HS_SERVER_HELLO, body_len);
|
||||
st = paze_tls_record_send_plain(tls, PAZE_TLS_CT_HANDSHAKE, sh, 4 + body_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
paze_tls_tr_update(&tls->transcript, sh, 4 + body_len);
|
||||
|
||||
/* Certificate(TLS 1.2 格式) */
|
||||
if (!cfg->cert_der || cfg->cert_der_len == 0) {
|
||||
paze_tls_set_err(tls, "no server certificate configured");
|
||||
return PAZE_ERR_INVALID;
|
||||
}
|
||||
if (cfg->cert_der_len > 0xFFFFFF) return PAZE_ERR_OVERFLOW;
|
||||
uint8_t cert_body[8192];
|
||||
cert_body[0] = (uint8_t)(cfg->cert_der_len >> 16);
|
||||
cert_body[1] = (uint8_t)(cfg->cert_der_len >> 8);
|
||||
cert_body[2] = (uint8_t)(cfg->cert_der_len);
|
||||
if (3 + cfg->cert_der_len > sizeof(cert_body)) return PAZE_ERR_OVERFLOW;
|
||||
memcpy(cert_body + 3, cfg->cert_der, cfg->cert_der_len);
|
||||
st = tls12_send_hs(tls, PAZE_TLS_HS_CERTIFICATE, cert_body, 3 + cfg->cert_der_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
|
||||
/* ServerKeyExchange(ECDHE 时) */
|
||||
if (tls12_is_ecdhe(tls->neg.cipher)) {
|
||||
uint8_t ske[1024]; size_t ske_len = sizeof(ske);
|
||||
st = tls12_server_build_ske(tls, ske, &ske_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
st = tls12_send_hs(tls, PAZE_TLS_HS_SERVER_KEY_EXCHANGE, ske, ske_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
}
|
||||
|
||||
/* ServerHelloDone */
|
||||
st = tls12_send_hs(tls, PAZE_TLS_HS_SERVER_HELLO_DONE, NULL, 0);
|
||||
if (st != PAZE_OK) return st;
|
||||
tls->hs_state = PAZE_HS_SV12_WAIT_CKE;
|
||||
}
|
||||
|
||||
/* ---- 阶段 C:ClientKeyExchange ---- */
|
||||
while (tls->hs_state == PAZE_HS_SV12_WAIT_CKE) {
|
||||
const uint8_t *body; size_t blen; uint8_t type; int ccs;
|
||||
st = tls12_recv_hs(tls, &body, &blen, &type, &ccs, NULL, NULL);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (ccs) { paze_tls_set_err(tls, "unexpected CCS before CKE"); return PAZE_ERR_PROTOCOL; }
|
||||
if (type != PAZE_TLS_HS_CLIENT_KEY_EXCHANGE) {
|
||||
paze_tls_set_err(tls, "expected CKE, got hs %d", type);
|
||||
return PAZE_ERR_PROTOCOL;
|
||||
}
|
||||
st = tls12_server_parse_cke(tls, body, blen);
|
||||
if (st != PAZE_OK) return st;
|
||||
st = tls12_derive_master(tls, tls->pms12, tls->pms12_len);
|
||||
paze_memzero(tls->pms12, sizeof(tls->pms12));
|
||||
tls->pms12_len = 0;
|
||||
if (st != PAZE_OK) return st;
|
||||
tls->hs_state = PAZE_HS_SV12_WAIT_CCS_FIN;
|
||||
}
|
||||
|
||||
/* ---- 阶段 D:等客户端 CCS + Finished,验证 ---- */
|
||||
while (tls->hs_state == PAZE_HS_SV12_WAIT_CCS_FIN) {
|
||||
st = tls12_wait_ccs(tls, 1);
|
||||
if (st != PAZE_OK) return st;
|
||||
const uint8_t *body, *raw; size_t blen, raw_len; uint8_t type; int ccs;
|
||||
st = tls12_recv_hs(tls, &body, &blen, &type, &ccs, &raw, &raw_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (ccs) { paze_tls_set_err(tls, "duplicate CCS"); return PAZE_ERR_PROTOCOL; }
|
||||
if (type == PAZE_TLS_HS_NEW_SESSION_TICKET) continue;
|
||||
if (type != PAZE_TLS_HS_FINISHED) {
|
||||
paze_tls_set_err(tls, "expected client Finished, got %d", type);
|
||||
return PAZE_ERR_PROTOCOL;
|
||||
}
|
||||
if (blen != 12) { paze_tls_set_err(tls, "bad 1.2 Finished len"); return PAZE_ERR_VERIFY; }
|
||||
uint8_t exp[12];
|
||||
st = tls12_finished(tls, "client finished", exp);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (paze_ct_equal(exp, body, 12) != 0) {
|
||||
paze_tls_set_err(tls, "client 1.2 Finished verify failed");
|
||||
return PAZE_ERR_VERIFY;
|
||||
}
|
||||
paze_memzero(exp, sizeof(exp));
|
||||
/* RFC 5246 §7.4.9: 服务端自身的 Finished 需把客户端的 Finished 计入
|
||||
* transcript;而 tls12_recv_hs 收 Finished 时不自动计入,此处显式补上。 */
|
||||
paze_tls_tr_update(&tls->transcript, raw, raw_len);
|
||||
tls->hs_state = PAZE_HS_SV12_SEND_CCS_FIN;
|
||||
}
|
||||
|
||||
/* ---- 阶段 E:发 CCS + Finished ---- */
|
||||
if (tls->hs_state == PAZE_HS_SV12_SEND_CCS_FIN) {
|
||||
uint8_t ccs[1] = { 0x01 };
|
||||
st = paze_tls_record_send_plain(tls, PAZE_TLS_CT_CHANGE_CIPHER_SPEC, ccs, 1);
|
||||
if (st != PAZE_OK) return st;
|
||||
tls->enc_write = 1;
|
||||
tls->write_key = &tls->handshake_write;
|
||||
|
||||
uint8_t vd[12];
|
||||
st = tls12_finished(tls, "server finished", vd);
|
||||
if (st != PAZE_OK) return st;
|
||||
st = tls12_send_hs(tls, PAZE_TLS_HS_FINISHED, vd, 12);
|
||||
if (st != PAZE_OK) return st;
|
||||
paze_memzero(vd, sizeof(vd));
|
||||
tls->handshake_done = 1;
|
||||
tls->hs_state = PAZE_HS_SV_DONE;
|
||||
}
|
||||
return PAZE_OK;
|
||||
}
|
||||
|
||||
+84
-21
@@ -76,7 +76,8 @@ static paze_status_t send_hs(paze_tls_t *tls, uint8_t type, const uint8_t *body,
|
||||
}
|
||||
|
||||
static paze_status_t recv_hs(paze_tls_t *tls,
|
||||
const uint8_t **body, size_t *blen, uint8_t *type) {
|
||||
const uint8_t **body, size_t *blen, uint8_t *type,
|
||||
const uint8_t **raw, size_t *raw_len) {
|
||||
const uint8_t *buf; size_t n; uint8_t ct;
|
||||
for (;;) {
|
||||
paze_status_t st = paze_tls_record_read(tls, &buf, &n, &ct);
|
||||
@@ -92,6 +93,9 @@ static paze_status_t recv_hs(paze_tls_t *tls,
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (ct == PAZE_TLS_CT_CHANGE_CIPHER_SPEC) {
|
||||
continue; /* 兼容性 CCS(1.2 互操作):忽略 */
|
||||
}
|
||||
if (ct != PAZE_TLS_CT_HANDSHAKE) {
|
||||
if (!tls->handshake_done) {
|
||||
paze_tls_set_err(tls, "unexpected ct %d during hs", ct);
|
||||
@@ -105,7 +109,13 @@ static paze_status_t recv_hs(paze_tls_t *tls,
|
||||
size_t pl = ((size_t)buf[1] << 16) | ((size_t)buf[2] << 8) | buf[3];
|
||||
if (4 + pl > n) { paze_tls_set_err(tls, "hs len mismatch"); return PAZE_ERR_FORMAT; }
|
||||
*body = buf + 4; *blen = pl;
|
||||
paze_tls_tr_update(&tls->transcript, buf, 4 + pl);
|
||||
if (raw) { *raw = buf; *raw_len = 4 + pl; }
|
||||
/* RFC 8446 §4.4.3/§4.4.4:CV 的签名与 Finished 的 verify_data 都
|
||||
* 覆盖"到该消息为止(不含)"的 transcript;故不在此自动计入,
|
||||
* 由调用方先验证再把原始消息显式计入。 */
|
||||
if (buf[0] != PAZE_TLS_HS_CERTIFICATE_VERIFY &&
|
||||
buf[0] != PAZE_TLS_HS_FINISHED)
|
||||
paze_tls_tr_update(&tls->transcript, buf, 4 + pl);
|
||||
return PAZE_OK;
|
||||
}
|
||||
}
|
||||
@@ -113,7 +123,7 @@ static paze_status_t recv_hs(paze_tls_t *tls,
|
||||
/* ============================================================
|
||||
* [B] ClientHello 构造
|
||||
* ============================================================ */
|
||||
static paze_status_t tls13_build_ch(paze_tls_t *tls, uint8_t *out, size_t *outlen) {
|
||||
paze_status_t paze_tls13_build_ch(paze_tls_t *tls, uint8_t *out, size_t *outlen) {
|
||||
paze_tls_config_t *cfg = tls->cfg_owned ? tls->cfg_owned : (paze_tls_config_t *)tls->cfg;
|
||||
uint8_t *p = out;
|
||||
p += 4;
|
||||
@@ -347,8 +357,13 @@ paze_status_t paze_tls13_client_handshake(paze_tls_t *tls) {
|
||||
paze_tls_tr_init(&tls->transcript, paze_hash_sha256());
|
||||
|
||||
uint8_t ch[PAZE_TLS_MAX_RECORD]; size_t ch_len = 0;
|
||||
st = tls13_build_ch(tls, ch, &ch_len);
|
||||
st = paze_tls13_build_ch(tls, ch, &ch_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
/* 保存 ClientHello 原始消息:供 SHA-384 transcript 重放 */
|
||||
tls->ch1 = (uint8_t *)malloc(ch_len);
|
||||
if (!tls->ch1) return PAZE_ERR_PLATFORM;
|
||||
memcpy(tls->ch1, ch, ch_len);
|
||||
tls->ch1_len = ch_len;
|
||||
paze_tls_tr_update(&tls->transcript, ch, ch_len);
|
||||
paze_tls_record_send_plain(tls, PAZE_TLS_CT_HANDSHAKE, ch, ch_len);
|
||||
paze_tls_tr_snapshot(&tls->transcript);
|
||||
@@ -357,7 +372,8 @@ paze_status_t paze_tls13_client_handshake(paze_tls_t *tls) {
|
||||
|
||||
while (tls->hs_state == PAZE_HS_CL_WAIT_SH) {
|
||||
const uint8_t *body; size_t blen; uint8_t type;
|
||||
st = recv_hs(tls, &body, &blen, &type);
|
||||
const uint8_t *sh_raw = NULL; size_t sh_raw_len = 0;
|
||||
st = recv_hs(tls, &body, &blen, &type, &sh_raw, &sh_raw_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (type != PAZE_TLS_HS_SERVER_HELLO) {
|
||||
paze_tls_set_err(tls, "expected ServerHello, got %d", type);
|
||||
@@ -385,11 +401,25 @@ paze_status_t paze_tls13_client_handshake(paze_tls_t *tls) {
|
||||
case PAZE_TLS_CHACHA20_POLY1305_SHA256:
|
||||
tls->neg.version = PAZE_TLS_VERSION_1_3; break;
|
||||
default:
|
||||
tls->neg.version = PAZE_TLS_VERSION_1_2;
|
||||
/* TLS 1.2 ServerHello:保存原始消息供 tls_handshake12 使用,
|
||||
* 返回 NOTSUP 由 tls_conn 回退到 1.2。 */
|
||||
if (sh_raw && sh_raw_len) {
|
||||
free(tls->sh1);
|
||||
tls->sh1 = (uint8_t *)malloc(sh_raw_len);
|
||||
if (!tls->sh1) return PAZE_ERR_PLATFORM;
|
||||
memcpy(tls->sh1, sh_raw, sh_raw_len);
|
||||
tls->sh1_len = sh_raw_len;
|
||||
}
|
||||
return PAZE_ERR_NOTSUP;
|
||||
}
|
||||
|
||||
if (tls->neg.hash == paze_hash_sha384()) return PAZE_ERR_NOTSUP;
|
||||
/* 若协商 hash 为 SHA-384,transcript 需用 SHA-384 从头重算:
|
||||
* recv_hs 已把 CH+SH 算入 SHA-256 transcript,这里重建为 SHA-384(CH || SH) */
|
||||
if (tls->neg.hash == paze_hash_sha384()) {
|
||||
paze_tls_tr_init(&tls->transcript, paze_hash_sha384());
|
||||
if (tls->ch1) paze_tls_tr_update(&tls->transcript, tls->ch1, tls->ch1_len);
|
||||
if (sh_raw) paze_tls_tr_update(&tls->transcript, sh_raw, sh_raw_len);
|
||||
}
|
||||
|
||||
/* Parse SH extensions */
|
||||
int found_v = 0, found_ks = 0;
|
||||
@@ -415,7 +445,7 @@ paze_status_t paze_tls13_client_handshake(paze_tls_t *tls) {
|
||||
|
||||
while (tls->hs_state == PAZE_HS_CL_WAIT_EE) {
|
||||
const uint8_t *body; size_t blen; uint8_t type;
|
||||
st = recv_hs(tls, &body, &blen, &type);
|
||||
st = recv_hs(tls, &body, &blen, &type, NULL, NULL);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (type != PAZE_TLS_HS_ENCRYPTED_EXTENSIONS) {
|
||||
paze_tls_set_err(tls, "expected EncryptedExtensions got %d", type);
|
||||
@@ -440,7 +470,8 @@ paze_status_t paze_tls13_client_handshake(paze_tls_t *tls) {
|
||||
|
||||
while (tls->hs_state == PAZE_HS_CL_WAIT_CERT_CR_CV) {
|
||||
const uint8_t *body; size_t blen; uint8_t type;
|
||||
st = recv_hs(tls, &body, &blen, &type);
|
||||
const uint8_t *raw = NULL; size_t raw_len = 0;
|
||||
st = recv_hs(tls, &body, &blen, &type, &raw, &raw_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (type == PAZE_TLS_HS_CERTIFICATE_REQUEST) continue;
|
||||
|
||||
@@ -485,11 +516,15 @@ paze_status_t paze_tls13_client_handshake(paze_tls_t *tls) {
|
||||
if (blen != 4 + sig_len) return PAZE_ERR_FORMAT;
|
||||
st = verify_cv(tls, 1, alg, body + 4, sig_len, &tls->peer_chain[0]);
|
||||
if (st != PAZE_OK) { paze_tls_set_err(tls, "CertificateVerify failed"); return PAZE_ERR_VERIFY; }
|
||||
/* 验证通过后计入 transcript(recv_hs 不自动计入 CV) */
|
||||
if (raw && raw_len) paze_tls_tr_update(&tls->transcript, raw, raw_len);
|
||||
continue;
|
||||
}
|
||||
if (type == PAZE_TLS_HS_FINISHED) {
|
||||
st = verify_finished(tls, 1, body, blen);
|
||||
if (st != PAZE_OK) { paze_tls_set_err(tls, "server Finished verify failed"); return PAZE_ERR_VERIFY; }
|
||||
/* 验证通过后计入 transcript(recv_hs 不自动计入 Finished) */
|
||||
if (raw && raw_len) paze_tls_tr_update(&tls->transcript, raw, raw_len);
|
||||
tls->hs_state = PAZE_HS_CL_WAIT_FINISHED;
|
||||
break;
|
||||
}
|
||||
@@ -546,14 +581,22 @@ paze_status_t paze_tls13_server_handshake(paze_tls_t *tls) {
|
||||
|
||||
while (tls->hs_state == PAZE_HS_INIT) {
|
||||
const uint8_t *body; size_t blen; uint8_t type;
|
||||
const uint8_t *ch_raw = NULL; size_t ch_raw_len = 0;
|
||||
/* Initialize transcript BEFORE receiving ClientHello */
|
||||
paze_tls_tr_init(&tls->transcript, paze_hash_sha256());
|
||||
st = recv_hs(tls, &body, &blen, &type);
|
||||
st = recv_hs(tls, &body, &blen, &type, &ch_raw, &ch_raw_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (type != PAZE_TLS_HS_CLIENT_HELLO) {
|
||||
paze_tls_set_err(tls, "expected ClientHello, got %d", type);
|
||||
return PAZE_ERR_PROTOCOL;
|
||||
}
|
||||
/* 保存 ClientHello 原始消息(含 4 字节头):供协商 hash 重放 */
|
||||
if (!tls->ch1 && ch_raw && ch_raw_len > 0) {
|
||||
tls->ch1 = (uint8_t *)malloc(ch_raw_len);
|
||||
if (!tls->ch1) return PAZE_ERR_PLATFORM;
|
||||
memcpy(tls->ch1, ch_raw, ch_raw_len);
|
||||
tls->ch1_len = ch_raw_len;
|
||||
}
|
||||
if (blen < 2 + 32 + 1) return PAZE_ERR_FORMAT;
|
||||
const uint8_t *bp = body;
|
||||
bp += 2 + 32; /* leg_ver + random */
|
||||
@@ -570,11 +613,15 @@ paze_status_t paze_tls13_server_handshake(paze_tls_t *tls) {
|
||||
if (paze_load16_be(bp + j) == prefer[i]) { chosen_cipher = prefer[i]; break; }
|
||||
}
|
||||
}
|
||||
if (!chosen_cipher) return PAZE_ERR_PROTOCOL;
|
||||
if (!chosen_cipher) return PAZE_ERR_NOTSUP; /* 无 1.3 套件 → 回退 1.2 */
|
||||
tls->neg.cipher = chosen_cipher;
|
||||
tls->neg.version = PAZE_TLS_VERSION_1_3; /* 服务端协商为 1.3 */
|
||||
paze_tls_cipher_params(chosen_cipher, &tls->neg.aead, &tls->neg.hash,
|
||||
&tls->neg.key_len, &tls->neg.hash_len);
|
||||
/* 用协商 hash 重建 transcript;必须重放 ClientHello(recv_hs 已把它
|
||||
* 算入 SHA-256 transcript,而实际 transcript 用协商 hash 从头算) */
|
||||
paze_tls_tr_init(&tls->transcript, tls->neg.hash);
|
||||
if (tls->ch1) paze_tls_tr_update(&tls->transcript, tls->ch1, tls->ch1_len);
|
||||
|
||||
bp += slen + 1 + 1; /* skip suites + comp_len + comp */
|
||||
if ((size_t)(bp - body) + 2 > blen) return PAZE_ERR_FORMAT;
|
||||
@@ -673,21 +720,37 @@ paze_status_t paze_tls13_server_handshake(paze_tls_t *tls) {
|
||||
st = send_hs(tls, PAZE_TLS_HS_ENCRYPTED_EXTENSIONS, ee + 4, body_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
|
||||
/* Certificate */
|
||||
/* Certificate:cfg->cert_der 是链式 blob([3字节长][DER] 逐条),
|
||||
* 需转成 RFC 8446 certificate_list(每条目追加 2 字节空 extensions)。 */
|
||||
if (cfg->cert_der == NULL || cfg->cert_der_len == 0) {
|
||||
paze_tls_set_err(tls, "no server certificate configured");
|
||||
return PAZE_ERR_INVALID;
|
||||
}
|
||||
uint8_t cert_body[8192];
|
||||
uint8_t *cp = cert_body;
|
||||
*cp++ = 0x00;
|
||||
if (cfg->cert_der_len > 0xFFFFFF || 1 + 3 + cfg->cert_der_len > sizeof(cert_body))
|
||||
return PAZE_ERR_OVERFLOW;
|
||||
cp[0] = (uint8_t)(cfg->cert_der_len >> 16);
|
||||
cp[1] = (uint8_t)(cfg->cert_der_len >> 8);
|
||||
cp[2] = (uint8_t)(cfg->cert_der_len);
|
||||
cp += 3;
|
||||
memcpy(cp, cfg->cert_der, cfg->cert_der_len); cp += cfg->cert_der_len;
|
||||
*cp++ = 0x00; /* certificate_request_context 长度 */
|
||||
uint8_t *list_len_ptr = cp; cp += 3;
|
||||
size_t entries_len = 0;
|
||||
const uint8_t *cb = cfg->cert_der;
|
||||
size_t cbl = cfg->cert_der_len;
|
||||
while (cbl >= 3) {
|
||||
size_t cl = ((size_t)cb[0] << 16) | ((size_t)cb[1] << 8) | cb[2];
|
||||
cb += 3; cbl -= 3;
|
||||
if (cl > cbl || cl > 0xFFFF ||
|
||||
cp + 3 + cl + 2 > cert_body + sizeof(cert_body))
|
||||
return PAZE_ERR_OVERFLOW;
|
||||
cp[0] = (uint8_t)(cl >> 16);
|
||||
cp[1] = (uint8_t)(cl >> 8);
|
||||
cp[2] = (uint8_t)(cl);
|
||||
cp += 3;
|
||||
memcpy(cp, cb, cl); cp += cl; cb += cl; cbl -= cl;
|
||||
cp[0] = 0; cp[1] = 0; cp += 2; /* CertificateEntry extensions: 空 */
|
||||
entries_len += 3 + cl + 2;
|
||||
}
|
||||
if (entries_len > 0xFFFFFF) return PAZE_ERR_OVERFLOW;
|
||||
list_len_ptr[0] = (uint8_t)(entries_len >> 16);
|
||||
list_len_ptr[1] = (uint8_t)(entries_len >> 8);
|
||||
list_len_ptr[2] = (uint8_t)(entries_len);
|
||||
size_t cert_body_len = (size_t)(cp - cert_body);
|
||||
st = send_hs(tls, PAZE_TLS_HS_CERTIFICATE, cert_body, cert_body_len);
|
||||
if (st != PAZE_OK) return st;
|
||||
@@ -715,7 +778,7 @@ paze_status_t paze_tls13_server_handshake(paze_tls_t *tls) {
|
||||
|
||||
while (tls->hs_state == PAZE_HS_SV_WAIT_FINISHED) {
|
||||
const uint8_t *body; size_t blen; uint8_t type;
|
||||
st = recv_hs(tls, &body, &blen, &type);
|
||||
st = recv_hs(tls, &body, &blen, &type, NULL, NULL);
|
||||
if (st != PAZE_OK) return st;
|
||||
if (type == PAZE_TLS_HS_CERTIFICATE) continue;
|
||||
if (type == PAZE_TLS_HS_CERTIFICATE_VERIFY) continue;
|
||||
|
||||
+32
-1
@@ -133,12 +133,22 @@ typedef enum {
|
||||
PAZE_HS_CL_WAIT_CERT_CR_CV, /* Certificate/CertificateRequest/CertificateVerify */
|
||||
PAZE_HS_CL_WAIT_FINISHED,
|
||||
PAZE_HS_CL_DONE,
|
||||
/* client 1.2 */
|
||||
PAZE_HS_CL12_WAIT_SHD, /* 已发 CH,等 ServerHello */
|
||||
PAZE_HS_CL12_WAIT_CERT_SKE_SHD, /* 等 Certificate/SKE/ServerHelloDone */
|
||||
PAZE_HS_CL12_SEND_CKE, /* 发 CKE + CCS + Finished */
|
||||
PAZE_HS_CL12_WAIT_CCS_FIN, /* 等 CCS + Finished */
|
||||
/* server 1.3 */
|
||||
PAZE_HS_SV_WAIT_CH,
|
||||
PAZE_HS_SV_SEND_SH,
|
||||
PAZE_HS_SV_SEND_EE_CERT_CV_FIN,
|
||||
PAZE_HS_SV_WAIT_FINISHED,
|
||||
PAZE_HS_SV_DONE,
|
||||
/* server 1.2 */
|
||||
PAZE_HS_SV12_SEND_SH_CERT_SKE_SHD,
|
||||
PAZE_HS_SV12_WAIT_CKE,
|
||||
PAZE_HS_SV12_WAIT_CCS_FIN,
|
||||
PAZE_HS_SV12_SEND_CCS_FIN,
|
||||
/* 通用 */
|
||||
PAZE_HS_ERROR
|
||||
} paze_tls_hs_state_t;
|
||||
@@ -206,10 +216,26 @@ struct paze_tls {
|
||||
paze_tls_hs_state_t hs_state;
|
||||
int handshake_done;
|
||||
|
||||
/* 服务端:peer(客户端)的 ClientHello 原始(用于 HelloRetryRequest) */
|
||||
/* 服务端:peer(客户端)的 ClientHello 原始(用于 HelloRetryRequest / TLS 1.2 回退) */
|
||||
uint8_t *ch1; size_t ch1_len;
|
||||
int hello_retry;
|
||||
|
||||
/* 客户端:TLS 1.2 回退时保存的 ServerHello 原始消息 */
|
||||
uint8_t *sh1; size_t sh1_len;
|
||||
|
||||
/* TLS 1.2 握手随机数 */
|
||||
uint8_t client_random[32];
|
||||
uint8_t server_random[32];
|
||||
/* TLS 1.2 master secret */
|
||||
uint8_t ms12[48];
|
||||
int ms12_valid;
|
||||
/* TLS 1.2 pre-master secret(阶段间缓存) */
|
||||
uint8_t pms12[64];
|
||||
size_t pms12_len;
|
||||
/* TLS 1.2 ECDHE:对端(ServerKeyExchange)公钥 */
|
||||
uint8_t ske_pub[65];
|
||||
size_t ske_pub_len;
|
||||
|
||||
/* 对端证书链(已解析) */
|
||||
paze_x509_cert_t peer_chain[PAZE_TLS_MAX_CERTS];
|
||||
int peer_chain_len;
|
||||
@@ -339,6 +365,11 @@ paze_status_t paze_tls_kex_ecdh(paze_tls_kex_t *mine, uint16_t server_group,
|
||||
/* 1.3 握手 (由 tls_client/tls_server 调用) */
|
||||
paze_status_t paze_tls13_client_handshake(paze_tls_t *tls);
|
||||
paze_status_t paze_tls13_server_handshake(paze_tls_t *tls);
|
||||
/* 构造兼容 CH(TLS 1.3 + 1.2 套件/扩展)。TLS 1.2 独立模式复用。 */
|
||||
paze_status_t paze_tls13_build_ch(paze_tls_t *tls, uint8_t *out, size_t *outlen);
|
||||
/* TLS 1.2 握手 */
|
||||
paze_status_t paze_tls12_client_handshake(paze_tls_t *tls);
|
||||
paze_status_t paze_tls12_server_handshake(paze_tls_t *tls);
|
||||
|
||||
/* ============================================================
|
||||
* 便利
|
||||
|
||||
+23
-13
@@ -15,8 +15,8 @@
|
||||
* 解密后从末尾剥离 zeros 与 content_type 得真实 fragment。
|
||||
*
|
||||
* 1.2 AEAD-GCM (RFC 5288):
|
||||
* nonce = explicit_nonce(8) || fixed_iv(4)
|
||||
* aad = seq(8) || type(1) || version(2) || length(2) ; length = ct_len + 16
|
||||
* nonce = fixed_iv(4) || explicit_nonce(8) ; GCMNonce = salt || nonce_explicit
|
||||
* aad = seq(8) || type(1) || version(2) || length(2) ; length = 明文长度(ct_len)
|
||||
* record body = explicit_nonce(8) || ciphertext || tag(16)
|
||||
* fixed_iv 取自 key material 的 write_IV(4 字节),存于 traffic_key.iv[0..3]。
|
||||
*
|
||||
@@ -76,10 +76,14 @@ paze_status_t paze_tls_record_read(paze_tls_t *tls,
|
||||
uint8_t *body = hdr + 5;
|
||||
size_t total = 5 + rlen;
|
||||
|
||||
/* ChangeCipherSpec(1.3 兼容占位):直接消费并继续 */
|
||||
/* ChangeCipherSpec:1.2 需在此切换密钥;1.3 由握手层忽略。
|
||||
* 不在此静默跳过,返回给调用方(recv_hs / tls12 状态机处理)。 */
|
||||
if (rtype == PAZE_TLS_CT_CHANGE_CIPHER_SPEC) {
|
||||
*content_type = rtype;
|
||||
*out = body;
|
||||
*outlen = rlen;
|
||||
tls->in_off += total;
|
||||
continue;
|
||||
return PAZE_OK;
|
||||
}
|
||||
|
||||
/* 明文 record(握手初期 / Alert) */
|
||||
@@ -130,14 +134,16 @@ paze_status_t paze_tls_record_read(paze_tls_t *tls,
|
||||
if (rlen < 8 + 16) { paze_tls_set_err(tls, "short 1.2 record"); return PAZE_ERR_PROTOCOL; }
|
||||
size_t ctlen = rlen - 8 - 16;
|
||||
uint8_t nonce[12];
|
||||
memcpy(nonce, body, 8);
|
||||
memcpy(nonce + 8, k->iv, 4); /* fixed_iv 在 iv 前 4 字节 */
|
||||
/* aad = seq(8) || type(1) || ver(2) || length(2) ; length = ctlen + 16 */
|
||||
/* RFC 5288 §3 GCMNonce = salt(4) || nonce_explicit(8),salt 在前 */
|
||||
memcpy(nonce, k->iv, 4); /* fixed_iv = client/server_write_IV */
|
||||
memcpy(nonce + 4, body, 8);
|
||||
/* aad = seq(8) || type(1) || ver(2) || length(2) ; length = TLSCompressed.length = ctlen
|
||||
* (RFC 5246 §6.2.3.3 / RFC 5288 §3:明文长度,非 ct+tag;1.3 才是 ct+16) */
|
||||
uint8_t aad[13];
|
||||
uint64_t seq = k->seq;
|
||||
for (int j = 0; j < 8; j++) aad[j] = (uint8_t)(seq >> (56 - 8 * j));
|
||||
aad[8] = rtype; aad[9] = 0x03; aad[10] = 0x03;
|
||||
size_t inner_len = ctlen + 16;
|
||||
size_t inner_len = ctlen;
|
||||
aad[11] = (uint8_t)(inner_len >> 8); aad[12] = (uint8_t)(inner_len);
|
||||
uint8_t tag[16];
|
||||
memcpy(tag, body + 8 + ctlen, 16);
|
||||
@@ -149,7 +155,10 @@ paze_status_t paze_tls_record_read(paze_tls_t *tls,
|
||||
rs = paze_chacha20poly1305_decrypt(k->key, nonce, aad, 13,
|
||||
body + 8, ctlen, tag, tls->app_buf);
|
||||
}
|
||||
if (rs != PAZE_OK) { paze_tls_set_err(tls, "bad 1.2 record MAC"); return PAZE_ERR_VERIFY; }
|
||||
if (rs != PAZE_OK) {
|
||||
paze_tls_set_err(tls, "bad 1.2 record MAC");
|
||||
return PAZE_ERR_VERIFY;
|
||||
}
|
||||
k->seq++;
|
||||
*content_type = rtype;
|
||||
*out = tls->app_buf;
|
||||
@@ -221,13 +230,14 @@ paze_status_t paze_tls_record_send(paze_tls_t *tls, uint8_t content_type,
|
||||
for (int j = 0; j < 8; j++) enonce[j] = (uint8_t)(seq >> (56 - 8 * j));
|
||||
memcpy(o + off, enonce, 8); off += 8;
|
||||
uint8_t nonce[12];
|
||||
memcpy(nonce, enonce, 8);
|
||||
memcpy(nonce + 8, k->iv, 4); /* fixed_iv */
|
||||
size_t inner_len = ctlen + 16;
|
||||
/* RFC 5288 §3 GCMNonce = salt(4) || nonce_explicit(8),salt 在前 */
|
||||
memcpy(nonce, k->iv, 4); /* fixed_iv */
|
||||
memcpy(nonce + 4, enonce, 8);
|
||||
/* aad length = TLSCompressed.length = 明文长度(RFC 5246 §6.2.3.3) */
|
||||
uint8_t aad[13];
|
||||
for (int j = 0; j < 8; j++) aad[j] = (uint8_t)(seq >> (56 - 8 * j));
|
||||
aad[8] = content_type; aad[9] = 0x03; aad[10] = 0x03;
|
||||
aad[11] = (uint8_t)(inner_len >> 8); aad[12] = (uint8_t)(inner_len);
|
||||
aad[11] = (uint8_t)(ctlen >> 8); aad[12] = (uint8_t)(ctlen);
|
||||
uint8_t tag[16];
|
||||
paze_status_t st;
|
||||
if (k->kind == PAZE_AEAD_AES_128_GCM || k->kind == PAZE_AEAD_AES_256_GCM) {
|
||||
|
||||
在新工单中引用
屏蔽一个用户