实现 TLS 1.3 PSK 会话恢复,构建输出移至 bin/ 并新增 standalone/psftp 命令

TLS 1.3 PSK 会话恢复: NewSessionTicket 签发/解析、ticket+binder 校验、selected_identity 回选、恢复握手免证书(pazessl -sess_in/-sess_out 端到端验证); 构建脚本输出 bin/ 并支持独立命令与 psftp; 修复 SFTP 二进制传输与 copy_id -P; 补充 tests/ 调试与 verify_tls
这个提交包含在:
JGZYES
2026-08-13 19:30:07 +08:00
父节点 6da27731b5
当前提交 3e8a5442fa
修改 33 个文件,包含 2565 行新增72 行删除
+2
查看文件
@@ -38,3 +38,5 @@ cmake --build build -j
## 状态
工程量巨大,按层推进。当前完成度见 `STATUS.md`
TLS 1.3 已覆盖:完整握手(ECDHE + X25519/P-256)、1.3↔1.2 版本回退、**PSK 会话恢复**(NewSessionTicket 签发/解析、binder 校验、恢复握手免证书),并通过 OpenSSL `s_server`/`s_client` 双向互操作对测。
+3 -2
查看文件
@@ -24,11 +24,12 @@
## PazeSSL — TLS(🔶 部分完成)
- ✅ TLS 1.3 客户端/服务端状态机(record、handshake、key schedule、扩展)
- ✅ TLS 1.3 **PSK 会话恢复**RFC 8446 §4.2.11 / §4.6.1):NewSessionTicket 签发与解析、ticket+binder 校验、selected_identity 回选、恢复握手跳过证书;`pazessl -sess_in/-sess_out` 端到端验证通过
- ✅ TLS 1.2 客户端/服务端状态机 + 1.3↔1.2 版本回退(ECDHE-RSA/ECDSA/RSA AES-GCM、CHACHA20-POLY1305
- ✅ OpenSSL 互操作对测 4/4s_server↔s_client 双向 TLS 1.3 / TLS 1.2`build/run_openssl_interop.ps1`
- ✅ 证书链与信任锚加载、RSA/ECDSA 私钥 PEM 解析
- ⬜ TLS 1.3 服务端 SHA-384 密码套件握手重放(SHA-256 已覆盖)
- PSK / 0-RTT / HRR 实际数据路径
- ⬜ 0-RTT / HRR 数据路径
## PazeSSH — SSH-2(✅ 完成)
@@ -68,4 +69,4 @@
## 遗留项(已知)
- X.509 ECDSA 证书解析仍失败(P-256 曲线点验证)
- TLS 1.3 SHA-384 服务端握手重放、PSK/0-RTT 未覆盖
- TLS 1.3 SHA-384 服务端握手重放、0-RTT / HRR 未覆盖
+2 -2
查看文件
@@ -103,7 +103,7 @@ int pssh_cmd_copyid(int argc, char **argv) {
for (int i = 1; i < argc; i++) {
if ((strcmp(argv[i], "-i") == 0) && i + 1 < argc) keyfile = argv[++i];
else if ((strcmp(argv[i], "-p") == 0) && i + 1 < argc) port = atoi(argv[++i]);
else if ((strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "-P") == 0) && i + 1 < argc) port = atoi(argv[++i]);
else if (strcmp(argv[i], "-pwd") == 0 && i + 1 < argc) password_arg = argv[++i];
else if (argv[i][0] == '-') continue;
else {
@@ -118,7 +118,7 @@ int pssh_cmd_copyid(int argc, char **argv) {
}
}
if (!host) {
fprintf(stderr, "Usage: paze-copy-id [-i keyfile] [-p port] [-pwd password] [user@]host\n");
fprintf(stderr, "Usage: paze-copy-id [-i keyfile] [-p/-P port] [-pwd password] [user@]host\n");
return 1;
}
if (!user) {
+90 -33
查看文件
@@ -97,6 +97,22 @@ static uint8_t *read_file(const char *path, size_t *out_len) {
return buf;
}
/* TLS 1.3 session 持久化(二进制 dump,自用格式) */
static int session_write_file(const char *path, const paze_tls_session_t *s) {
FILE *f = fopen(path, "wb");
if (!f) return -1;
fwrite(s, 1, sizeof(*s), f);
fclose(f);
return 0;
}
static int session_read_file(const char *path, paze_tls_session_t *s) {
FILE *f = fopen(path, "rb");
if (!f) return -1;
size_t n = fread(s, 1, sizeof(*s), f);
fclose(f);
return n == sizeof(*s) ? 0 : -1;
}
int main(int argc, char **argv) {
if (argc < 2) return usage();
const char *cmd = argv[1];
@@ -106,6 +122,8 @@ int main(int argc, char **argv) {
const char *host = NULL; int port = 443;
const char *cafile = NULL;
const char *sni = NULL;
const char *sess_in = NULL, *sess_out = NULL;
const char *msg = NULL;
int only_12 = 0;
for (int i = 2; i < argc; i++) {
if (strcmp(argv[i], "-connect") == 0 && i + 1 < argc) {
@@ -117,6 +135,12 @@ 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], "-sess_in") == 0 && i + 1 < argc) {
sess_in = argv[++i];
} else if (strcmp(argv[i], "-sess_out") == 0 && i + 1 < argc) {
sess_out = argv[++i];
} else if (strcmp(argv[i], "-msg") == 0 && i + 1 < argc) {
msg = argv[++i];
} else if (strcmp(argv[i], "-tls1_2") == 0) {
only_12 = 1;
}
@@ -136,6 +160,11 @@ int main(int argc, char **argv) {
free(cdata);
}
}
if (sess_in) {
paze_tls_session_t ss;
if (session_read_file(sess_in, &ss) == 0 && ss.valid)
paze_tls_config_set_session(cfg, &ss);
}
sock_t sock = tcp_connect(host, port);
if (sock == SOCK_INVALID) { fprintf(stderr, "connect failed\n"); return 1; }
@@ -154,16 +183,32 @@ int main(int argc, char **argv) {
paze_tls_free(tls); closesocket(sock); paze_tls_config_free(cfg);
return 1;
}
fprintf(stderr, "Connected (TLS %04x, cipher %04x)\n",
fprintf(stderr, "Connected (TLS %04x, cipher %04x, resumed %s)\n",
(unsigned)paze_tls_get_version(tls),
(unsigned)paze_tls_get_cipher_suite(tls));
(unsigned)paze_tls_get_cipher_suite(tls),
paze_tls_session_resumed(tls) ? "YES" : "no");
if (sess_out) {
paze_tls_session_t ss;
if (paze_tls_get_session(tls, &ss) == PAZE_OK && ss.valid) {
session_write_file(sess_out, &ss);
fprintf(stderr, "Session saved -> %s\n", sess_out);
} else {
fprintf(stderr, "No session received\n");
}
}
/* Simple stdio relay */
uint8_t buf[16384];
for (;;) {
if (msg) {
paze_tls_write(tls, (const uint8_t *)msg, strlen(msg));
int n = paze_tls_read(tls, buf, sizeof(buf));
if (n > 0) fwrite(buf, 1, (size_t)n, stdout);
if (n <= 0) break;
if (n > 0) { fwrite(buf, 1, (size_t)n, stdout); fprintf(stderr, "\n"); }
} else {
/* Simple stdio relay */
for (;;) {
int n = paze_tls_read(tls, buf, sizeof(buf));
if (n > 0) fwrite(buf, 1, (size_t)n, stdout);
if (n <= 0) break;
}
}
paze_tls_close(tls);
paze_tls_free(tls);
@@ -303,10 +348,12 @@ int main(int argc, char **argv) {
int port = 4433;
const char *certf = NULL; const char *keyf = NULL;
int only_12 = 0;
int reuse = 1;
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], "-reuse") == 0 && i + 1 < argc) reuse = atoi(argv[++i]);
else if (strcmp(argv[i], "-tls1_2") == 0) only_12 = 1;
}
if (!certf || !keyf) {
@@ -344,44 +391,54 @@ int main(int argc, char **argv) {
fprintf(stderr, "bind port %d failed\n", port);
closesocket(lsock); return 1;
}
if (listen(lsock, 1) < 0) {
if (listen(lsock, 4) < 0) {
fprintf(stderr, "listen failed\n");
closesocket(lsock); return 1;
}
fprintf(stderr, "Listening on port %d...\n", port);
fprintf(stderr, "Listening on port %d (-reuse %d)...\n", port, reuse);
struct sockaddr_in client;
socklen_t cl = sizeof(client);
sock_t conn = accept(lsock, (struct sockaddr *)&client, &cl);
if (conn == SOCK_INVALID) { closesocket(lsock); return 1; }
fprintf(stderr, "Client connected\n");
int overall = 0;
for (int conn_idx = 0; conn_idx < reuse; conn_idx++) {
struct sockaddr_in client;
socklen_t cl = sizeof(client);
sock_t conn = accept(lsock, (struct sockaddr *)&client, &cl);
if (conn == SOCK_INVALID) { closesocket(lsock); return 1; }
fprintf(stderr, "Client #%d connected\n", conn_idx + 1);
paze_tls_t *tls = paze_tls_new(cfg);
paze_tls_bio_t bio = { &conn, sock_read, sock_write };
paze_tls_set_bio(tls, bio);
paze_tls_t *tls = paze_tls_new(cfg);
paze_tls_bio_t bio = { &conn, sock_read, sock_write };
paze_tls_set_bio(tls, bio);
paze_status_t st = paze_tls_handshake(tls);
if (st != PAZE_OK) {
fprintf(stderr, "TLS handshake failed: %s\n", paze_tls_last_error(tls));
} else {
fprintf(stderr, "TLS handshake OK (version %04x, cipher %04x)\n",
(unsigned)paze_tls_get_version(tls),
(unsigned)paze_tls_get_cipher_suite(tls));
/* Echo loop */
uint8_t buf[4096];
for (int i = 0; i < 100; i++) {
int n = paze_tls_read(tls, buf, sizeof(buf));
if (n <= 0) break;
paze_tls_write(tls, buf, (size_t)n);
paze_status_t st = paze_tls_handshake(tls);
if (st != PAZE_OK) {
fprintf(stderr, "TLS handshake failed: %s\n", paze_tls_last_error(tls));
} else {
fprintf(stderr, "TLS handshake OK (version %04x, cipher %04x, resumed %s)\n",
(unsigned)paze_tls_get_version(tls),
(unsigned)paze_tls_get_cipher_suite(tls),
paze_tls_session_resumed(tls) ? "YES" : "no");
/* 跨连接共享 session ticket key,使后续连接可恢复会话 */
uint8_t tk[32]; int tk_ready = 0;
paze_tls_get_session_ticket_key(tls, tk, &tk_ready);
if (tk_ready) paze_tls_config_set_session_ticket_key(cfg, tk);
/* Echo loop */
uint8_t buf[4096];
for (int i = 0; i < 100; i++) {
int n = paze_tls_read(tls, buf, sizeof(buf));
if (n <= 0) break;
paze_tls_write(tls, buf, (size_t)n);
}
overall = 0;
}
paze_tls_close(tls);
paze_tls_free(tls);
closesocket(conn);
if (st != PAZE_OK) overall = 1;
}
paze_tls_close(tls);
paze_tls_free(tls);
closesocket(conn);
closesocket(lsock);
paze_tls_config_free(cfg);
free(cdata); free(kdata);
return st == PAZE_OK ? 0 : 1;
return overall;
}
fprintf(stderr, "pazessl %s: not yet implemented\n", cmd);
+709
查看文件
@@ -0,0 +1,709 @@
/* psftp —— 独立 SFTP 客户端 (平替 OpenSSH sftp)
*
* :
* psftp [] [user@]host[:]
* -P <port> ( 22)
* -pwd <pw>
* -i <key> (publickey )
* -o <opt> StrictHostKeyChecking=no|accept-new|yes
* -b <file> :退
*
* :
* bye/exit/quit 退
* pwd/lpwd /
* cd/lcd <path> /
* ls [path]
* lls [path]
* get [-r] <remote> [local] ()
* put [-r] <local> [remote] ()
* rm <path>
* mkdir <path>
* rmdir <path>
* rename <old> <new>
* help/?
*/
#include "paze/ssh.h"
#include "paze/ssh_agent.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#ifndef S_ISDIR
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
/* SFTP attrs 权限值中的文件类型掩码(与 POSIX st_mode 高 4 位一致) */
#define SSH_S_IFMT 0170000
#define SSH_S_IFDIR 0040000
#ifdef _WIN32
#include <windows.h>
#include <conio.h>
#include <direct.h>
#define GETCH _getch
#define IS_SEP(c) ((c) == '/' || (c) == '\\')
static int local_mkdir(const char *p) { return _mkdir(p); }
#else
#include <unistd.h>
#include <dirent.h>
#define IS_SEP(c) ((c) == '/')
static int local_mkdir(const char *p) { return mkdir(p, 0755); }
#endif
#define SFTP_BUF 32768
static void path_join_local(char *out, size_t n, const char *a, const char *b) {
size_t al = strlen(a);
int need = (al > 0 && !IS_SEP(a[al - 1])) ? 1 : 0;
#ifdef _WIN32
snprintf(out, n, need ? "%s\\%s" : "%s%s", a, b);
#else
snprintf(out, n, need ? "%s/%s" : "%s%s", a, b);
#endif
}
static void rjoin(char *out, size_t n, const char *base, const char *name) {
size_t bl = strlen(base);
int need = (bl > 0 && base[bl - 1] != '/') ? 1 : 0;
snprintf(out, n, need ? "%s/%s" : "%s%s", base, name);
}
static const char *path_basename(const char *p) {
const char *last = p;
size_t len = strlen(p);
while (len > 1 && IS_SEP(p[len - 1])) len--;
for (size_t i = 0; i < len; i++)
if (IS_SEP(p[i])) last = p + i + 1;
return last;
}
typedef struct { char name[512]; } ldir_ent_t;
static int local_dir_enum(const char *dir, ldir_ent_t *ents, int max) {
int n = 0;
#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
snprintf(pat, sizeof(pat), "%s\\*", dir);
WIN32_FIND_DATAA fd;
HANDLE h = FindFirstFileA(pat, &fd);
if (h == INVALID_HANDLE_VALUE) return -1;
do {
if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0)
continue;
if (n < max) { snprintf(ents[n].name, sizeof(ents[n].name), "%s", fd.cFileName); n++; }
} while (FindNextFileA(h, &fd));
FindClose(h);
#else
DIR *d = opendir(dir);
if (!d) return -1;
struct dirent *e;
while ((e = readdir(d)) != NULL) {
if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0)
continue;
if (n < max) { snprintf(ents[n].name, sizeof(ents[n].name), "%s", e->d_name); n++; }
}
closedir(d);
#endif
return n;
}
/* ============================================================
* +
* ============================================================ */
typedef struct {
int port;
const char *user, *password_arg, *keyfile;
int hostkey_mode;
} cli_cfg_t;
static ssh_session_t *connect_session(const cli_cfg_t *cfg,
const char *host, const char *ruser) {
const char *user = ruser ? ruser : cfg->user;
if (!user) {
const char *envu = getenv("USER");
if (!envu) envu = getenv("USERNAME");
user = envu ? envu : "root";
}
char pw_buf[256];
const char *password = cfg->password_arg;
if (!password) {
fprintf(stderr, "Password: ");
int idx = 0, ch;
while (idx < (int)sizeof(pw_buf) - 1) {
ch = GETCH();
if (ch == '\r' || ch == '\n') break;
if (ch == '\b' || ch == 0x7f) { if (idx > 0) { idx--; fprintf(stderr, "\b \b"); } continue; }
if (ch < 32) continue;
pw_buf[idx++] = (char)ch;
fprintf(stderr, "*");
}
pw_buf[idx] = '\0';
fprintf(stderr, "\n");
password = pw_buf;
}
ssh_session_t *s = ssh_session_new(0);
if (!s) { fprintf(stderr, "psftp: 会话分配失败\n"); return NULL; }
ssh_session_set_hostkey_check(s, cfg->hostkey_mode);
if (ssh_client_connect(s, host, (uint16_t)cfg->port) < 0) {
fprintf(stderr, "psftp: 连接 %s 失败\n", host);
ssh_session_free(s);
return NULL;
}
uint8_t *keydata = NULL; size_t keylen = 0;
if (cfg->keyfile) {
FILE *kf = fopen(cfg->keyfile, "rb");
if (kf) {
fseek(kf, 0, SEEK_END);
long sz = ftell(kf);
rewind(kf);
if (sz > 0 && sz < 4 * 1024 * 1024) {
keydata = (uint8_t *)malloc((size_t)sz);
if (keydata && fread(keydata, 1, (size_t)sz, kf) != (size_t)sz) {
free(keydata); keydata = NULL;
} else {
keylen = (size_t)sz;
}
}
fclose(kf);
}
}
int auth_ok = 0;
if (keydata) {
auth_ok = ssh_auth_client_loop(s, user, password, keydata, keylen) == 0;
free(keydata);
} else {
ssh_agent_t *agent = NULL;
if (ssh_agent_connect(&agent) == 0) {
auth_ok = ssh_auth_client_agent_loop(s, user, password, agent) == 0;
ssh_agent_close(agent);
}
if (!auth_ok)
auth_ok = ssh_auth_client_loop(s, user, password, NULL, 0) == 0;
}
if (!auth_ok) {
fprintf(stderr, "psftp: 认证失败\n");
ssh_session_free(s);
return NULL;
}
return s;
}
/* ============================================================
* ( / )
* ============================================================ */
static int sftp_get_file(ssh_sftp_t *sf, const char *remote,
const char *local) {
uint8_t handle[256];
size_t hlen = 0;
if (ssh_sftp_open(sf, remote, SSH_FXF_READ, handle, &hlen) < 0) {
fprintf(stderr, "psftp: 无法打开远端 %s\n", remote);
return -1;
}
FILE *f = fopen(local, "wb");
if (!f) {
fprintf(stderr, "psftp: 无法创建本地 %s\n", local);
ssh_sftp_close(sf, handle, hlen);
return -1;
}
uint64_t off = 0;
int code = 0, rc = 0;
for (;;) {
uint8_t buf[SFTP_BUF];
int r = ssh_sftp_read(sf, handle, hlen, off, buf, sizeof(buf), &code);
if (r < 0) { fprintf(stderr, "psftp: 读取 %s 失败 (code=%d)\n", remote, code); rc = -1; break; }
if (r == 0) break;
if (fwrite(buf, 1, (size_t)r, f) != (size_t)r) { rc = -1; break; }
off += (uint64_t)r;
}
fclose(f);
ssh_sftp_close(sf, handle, hlen);
return rc;
}
static int sftp_put_file(ssh_sftp_t *sf, const char *local,
const char *remote) {
FILE *f = fopen(local, "rb");
if (!f) { fprintf(stderr, "psftp: 无法打开本地 %s\n", local); return -1; }
uint8_t handle[256];
size_t hlen = 0;
if (ssh_sftp_open(sf, remote, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC,
handle, &hlen) < 0) {
fprintf(stderr, "psftp: 无法在远端创建 %s\n", remote);
fclose(f);
return -1;
}
uint64_t off = 0;
uint8_t buf[SFTP_BUF];
size_t n;
int rc = 0;
while ((n = fread(buf, 1, sizeof(buf), f)) > 0) {
size_t done = 0;
while (done < n) {
int w = ssh_sftp_write(sf, handle, hlen, off + done, buf + done,
(uint32_t)(n - done), NULL);
if (w <= 0) { rc = -1; break; }
done += (size_t)w;
}
if (rc < 0) break;
off += n;
}
fclose(f);
ssh_sftp_close(sf, handle, hlen);
return rc;
}
static void remote_mkdir_p(ssh_sftp_t *sf, const char *path) {
char tmp[2048];
size_t plen = strlen(path);
if (plen >= sizeof(tmp)) return;
const char *last = strrchr(path, '/');
if (last && last != path) {
size_t pl = (size_t)(last - path);
if (pl >= sizeof(tmp)) return;
memcpy(tmp, path, pl);
tmp[pl] = '\0';
remote_mkdir_p(sf, tmp);
}
if (ssh_sftp_mkdir(sf, path, 0755) < 0) {
ssh_sftp_ent_t e;
if (ssh_sftp_stat(sf, path, &e) == 0 &&
(e.perms & SSH_S_IFMT) == SSH_S_IFDIR)
return;
}
}
static int sftp_get_dir(ssh_sftp_t *sf, const char *remote,
const char *local) {
local_mkdir(local);
uint8_t handle[256];
size_t hlen = 0;
if (ssh_sftp_opendir(sf, remote, handle, &hlen) < 0) {
fprintf(stderr, "psftp: 无法打开远端目录 %s\n", remote);
return -1;
}
ssh_sftp_ent_t ent;
int rc = 0;
while (ssh_sftp_readdir(sf, handle, hlen, &ent) > 0) {
if (strcmp(ent.name, ".") == 0 || strcmp(ent.name, "..") == 0)
continue;
char lp[1024], rp[2048];
path_join_local(lp, sizeof(lp), local, ent.name);
rjoin(rp, sizeof(rp), remote, ent.name);
if ((ent.perms & SSH_S_IFMT) == SSH_S_IFDIR) {
if (sftp_get_dir(sf, rp, lp) < 0) rc = -1;
} else if (sftp_get_file(sf, rp, lp) < 0) {
rc = -1;
}
}
ssh_sftp_close(sf, handle, hlen);
return rc;
}
static int sftp_put_dir(ssh_sftp_t *sf, const char *local,
const char *remote) {
remote_mkdir_p(sf, remote);
ldir_ent_t *ents = (ldir_ent_t *)malloc(1024 * sizeof(ldir_ent_t));
if (!ents) return -1;
int n = local_dir_enum(local, ents, 1024);
if (n < 0) { free(ents); fprintf(stderr, "psftp: 无法读取本地目录 %s\n", local); return -1; }
int rc = 0;
for (int i = 0; i < n; i++) {
char lp[1024], rp[2048];
path_join_local(lp, sizeof(lp), local, ents[i].name);
rjoin(rp, sizeof(rp), remote, ents[i].name);
struct stat st;
if (stat(lp, &st) != 0) { rc = -1; continue; }
if (S_ISDIR(st.st_mode)) {
if (sftp_put_dir(sf, lp, rp) < 0) rc = -1;
} else if (sftp_put_file(sf, lp, rp) < 0) {
rc = -1;
}
}
free(ents);
return rc;
}
/* ============================================================
*
* ============================================================ */
typedef struct {
ssh_sftp_t *sf;
char rdir[2048];
char ldir[1024];
} psftp_t;
static void cmd_pwd(psftp_t *p) { printf("远端当前目录: %s\n", p->rdir); }
static void cmd_lpwd(psftp_t *p) {
#ifdef _WIN32
if (GetCurrentDirectoryA(sizeof(p->ldir), p->ldir)) printf("本地当前目录: %s\n", p->ldir);
#else
if (getcwd(p->ldir, sizeof(p->ldir))) printf("本地当前目录: %s\n", p->ldir);
#endif
}
static void cmd_cd(psftp_t *p, const char *path) {
char out[2048];
if (ssh_sftp_realpath(p->sf, path, out, sizeof(out)) < 0) {
fprintf(stderr, "psftp: cd: %s: 无此目录\n", path);
return;
}
ssh_sftp_ent_t e;
if (ssh_sftp_stat(p->sf, out, &e) < 0 ||
(e.perms & SSH_S_IFMT) != SSH_S_IFDIR) {
fprintf(stderr, "psftp: cd: %s: 无此目录\n", path);
return;
}
snprintf(p->rdir, sizeof(p->rdir), "%s", out);
}
static void cmd_lcd(psftp_t *p, const char *path) {
#ifdef _WIN32
if (SetCurrentDirectoryA(path)) {
GetCurrentDirectoryA(sizeof(p->ldir), p->ldir);
printf("本地现在使用: %s\n", p->ldir);
} else fprintf(stderr, "psftp: lcd: %s: 无此目录\n", path);
#else
if (chdir(path) == 0) {
getcwd(p->ldir, sizeof(p->ldir));
printf("本地现在使用: %s\n", p->ldir);
} else fprintf(stderr, "psftp: lcd: %s: 无此目录\n", path);
#endif
}
static void cmd_ls(psftp_t *p, const char *path) {
char rp[2048];
if (!path || !path[0]) {
snprintf(rp, sizeof(rp), "%s", p->rdir);
} else if (path[0] == '/') {
snprintf(rp, sizeof(rp), "%s", path);
} else {
rjoin(rp, sizeof(rp), p->rdir, path);
}
uint8_t handle[256];
size_t hlen = 0;
if (ssh_sftp_opendir(p->sf, rp, handle, &hlen) < 0) {
fprintf(stderr, "psftp: ls: %s: 无法打开\n", path ? path : ".");
return;
}
ssh_sftp_ent_t ent;
while (ssh_sftp_readdir(p->sf, handle, hlen, &ent) > 0) {
char t = ((ent.perms & SSH_S_IFMT) == SSH_S_IFDIR) ? 'd' : '-';
printf("%c %10llu %s\n", t, (unsigned long long)ent.size, ent.name);
}
ssh_sftp_close(p->sf, handle, hlen);
}
static void cmd_lls(psftp_t *p, const char *path) {
char lp[1024];
if (!path || !path[0]) {
snprintf(lp, sizeof(lp), "%s", p->ldir);
} else if (IS_SEP(path[0])) {
snprintf(lp, sizeof(lp), "%s", path);
} else {
path_join_local(lp, sizeof(lp), p->ldir, path);
}
ldir_ent_t *ents = (ldir_ent_t *)malloc(1024 * sizeof(ldir_ent_t));
if (!ents) return;
int n = local_dir_enum(lp, ents, 1024);
for (int i = 0; i < n; i++) printf("%s\n", ents[i].name);
free(ents);
}
static void cmd_get(psftp_t *p, int recursive, const char *remote,
const char *local) {
char rp[2048];
if (remote[0] == '/') snprintf(rp, sizeof(rp), "%s", remote);
else rjoin(rp, sizeof(rp), p->rdir, remote);
char lp[1024];
if (local && local[0]) {
if (IS_SEP(local[0])) snprintf(lp, sizeof(lp), "%s", local);
else path_join_local(lp, sizeof(lp), p->ldir, local);
} else {
path_join_local(lp, sizeof(lp), p->ldir, path_basename(rp));
}
ssh_sftp_ent_t e;
int is_dir = 0;
if (ssh_sftp_stat(p->sf, rp, &e) == 0)
is_dir = ((e.perms & SSH_S_IFMT) == SSH_S_IFDIR);
if (is_dir) {
if (!recursive) { fprintf(stderr, "psftp: %s 是目录,需要 -r\n", rp); return; }
if (sftp_get_dir(p->sf, rp, lp) < 0) fprintf(stderr, "psftp: get 目录失败\n");
} else {
if (sftp_get_file(p->sf, rp, lp) < 0) fprintf(stderr, "psftp: get 失败\n");
}
}
static void cmd_put(psftp_t *p, int recursive, const char *local,
const char *remote) {
char lp[1024];
if (IS_SEP(local[0])) snprintf(lp, sizeof(lp), "%s", local);
else path_join_local(lp, sizeof(lp), p->ldir, local);
char rp[2048];
if (remote && remote[0]) {
if (remote[0] == '/') snprintf(rp, sizeof(rp), "%s", remote);
else rjoin(rp, sizeof(rp), p->rdir, remote);
} else {
rjoin(rp, sizeof(rp), p->rdir, path_basename(lp));
}
struct stat st;
if (stat(lp, &st) != 0) { fprintf(stderr, "psftp: put: %s: 无此文件\n", lp); return; }
if (S_ISDIR(st.st_mode)) {
if (!recursive) { fprintf(stderr, "psftp: %s 是目录,需要 -r\n", lp); return; }
if (sftp_put_dir(p->sf, lp, rp) < 0) fprintf(stderr, "psftp: put 目录失败\n");
} else {
if (sftp_put_file(p->sf, lp, rp) < 0) fprintf(stderr, "psftp: put 失败\n");
}
}
static void cmd_rm(psftp_t *p, const char *path) {
char rp[2048];
if (path[0] == '/') snprintf(rp, sizeof(rp), "%s", path);
else rjoin(rp, sizeof(rp), p->rdir, path);
if (ssh_sftp_remove(p->sf, rp) < 0)
fprintf(stderr, "psftp: rm: %s 失败\n", rp);
}
static void cmd_mkdir(psftp_t *p, const char *path) {
char rp[2048];
if (path[0] == '/') snprintf(rp, sizeof(rp), "%s", path);
else rjoin(rp, sizeof(rp), p->rdir, path);
if (ssh_sftp_mkdir(p->sf, rp, 0755) < 0)
fprintf(stderr, "psftp: mkdir: %s 失败\n", rp);
}
static void cmd_rmdir(psftp_t *p, const char *path) {
char rp[2048];
if (path[0] == '/') snprintf(rp, sizeof(rp), "%s", path);
else rjoin(rp, sizeof(rp), p->rdir, path);
if (ssh_sftp_rmdir(p->sf, rp) < 0)
fprintf(stderr, "psftp: rmdir: %s 失败\n", rp);
}
static void cmd_rename(psftp_t *p, const char *oldp, const char *newp) {
char o[2048], nw[2048];
if (oldp[0] == '/') snprintf(o, sizeof(o), "%s", oldp);
else rjoin(o, sizeof(o), p->rdir, oldp);
if (newp[0] == '/') snprintf(nw, sizeof(nw), "%s", newp);
else rjoin(nw, sizeof(nw), p->rdir, newp);
if (ssh_sftp_rename(p->sf, o, nw) < 0)
fprintf(stderr, "psftp: rename: %s -> %s 失败\n", oldp, newp);
}
static void help(void) {
printf("可用命令:\n"
" bye, exit, quit 退出 psftp\n"
" pwd / lpwd 显示远端 / 本地当前目录\n"
" cd / lcd <path> 切换远端 / 本地目录\n"
" ls [path] 列出远端目录\n"
" lls [path] 列出本地目录\n"
" get [-r] <remote> [local] 下载(文件或目录)\n"
" put [-r] <local> [remote] 上传(文件或目录)\n"
" rm <path> 删除远端文件\n"
" mkdir <path> 创建远端目录\n"
" rmdir <path> 删除远端空目录\n"
" rename <old> <new> 远端重命名\n"
" help, ? 显示本帮助\n");
}
/* 执行单条命令;返回 1=退出 */
static int run_line(psftp_t *p, char *line) {
/* 去首尾空白 */
char *s = line;
while (*s == ' ' || *s == '\t') s++;
char *end = s + strlen(s);
while (end > s && (end[-1] == '\n' || end[-1] == '\r' || end[-1] == ' ' ||
end[-1] == '\t')) end--;
*end = '\0';
if (!*s) return 0;
char *args[8];
int nargs = 0;
char *tok = s;
while (tok && nargs < 8) {
while (*tok == ' ' || *tok == '\t') tok++;
if (!*tok) break;
args[nargs++] = tok;
char *sp = strchr(tok, ' ');
if (!sp) sp = strchr(tok, '\t');
if (!sp) break;
*sp = '\0';
tok = sp + 1;
}
const char *c = args[0];
if (strcmp(c, "bye") == 0 || strcmp(c, "exit") == 0 || strcmp(c, "quit") == 0)
return 1;
if (strcmp(c, "pwd") == 0) { cmd_pwd(p); return 0; }
if (strcmp(c, "lpwd") == 0) { cmd_lpwd(p); return 0; }
if (strcmp(c, "cd") == 0) { if (nargs > 1) cmd_cd(p, args[1]); return 0; }
if (strcmp(c, "lcd") == 0) { if (nargs > 1) cmd_lcd(p, args[1]); return 0; }
if (strcmp(c, "ls") == 0) { cmd_ls(p, nargs > 1 ? args[1] : NULL); return 0; }
if (strcmp(c, "lls") == 0) { cmd_lls(p, nargs > 1 ? args[1] : NULL); return 0; }
if (strcmp(c, "get") == 0) {
int rec = 0;
int i = 1;
if (nargs > 1 && strcmp(args[1], "-r") == 0) { rec = 1; i++; }
if (nargs > i) cmd_get(p, rec, args[i], nargs > i + 1 ? args[i + 1] : NULL);
return 0;
}
if (strcmp(c, "put") == 0) {
int rec = 0;
int i = 1;
if (nargs > 1 && strcmp(args[1], "-r") == 0) { rec = 1; i++; }
if (nargs > i) cmd_put(p, rec, args[i], nargs > i + 1 ? args[i + 1] : NULL);
return 0;
}
if (strcmp(c, "rm") == 0) { if (nargs > 1) cmd_rm(p, args[1]); return 0; }
if (strcmp(c, "mkdir") == 0) { if (nargs > 1) cmd_mkdir(p, args[1]); return 0; }
if (strcmp(c, "rmdir") == 0) { if (nargs > 1) cmd_rmdir(p, args[1]); return 0; }
if (strcmp(c, "rename") == 0) { if (nargs > 2) cmd_rename(p, args[1], args[2]); return 0; }
if (strcmp(c, "help") == 0 || strcmp(c, "?") == 0) { help(); return 0; }
fprintf(stderr, "psftp: 未知命令 \"%s\" (输入 help 查看)\n", c);
return 0;
}
/* ============================================================
*
* ============================================================ */
static void usage(void) {
fprintf(stderr,
"psftp - SFTP 客户端 (与 OpenSSH sftp 兼容)\n"
"用法:\n"
" psftp [选项] [user@]host[:初始路径]\n"
"选项:\n"
" -P <port> 远程端口 (默认 22)\n"
" -pwd <pw> 密码\n"
" -i <key> 私钥文件 (publickey 认证)\n"
" -o <opt> StrictHostKeyChecking=no|accept-new|yes\n"
" -b <file> 批处理模式:逐行执行命令后退出\n"
"示例:\n"
" psftp -P 6666 -pwd pass root@host\n"
" psftp root@host:/var/log -b batch.txt\n");
}
int main(int argc, char **argv) {
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
#endif
cli_cfg_t cfg = { 22, NULL, NULL, NULL, 0 };
const char *batch = NULL;
const char *target = NULL;
int i = 1;
for (; i < argc; i++) {
const char *a = argv[i];
if (strcmp(a, "-P") == 0 && i + 1 < argc) { cfg.port = atoi(argv[++i]); }
else if (strncmp(a, "-P", 2) == 0 && strlen(a) > 2) { cfg.port = atoi(a + 2); }
else if (strcmp(a, "-pwd") == 0 && i + 1 < argc) { cfg.password_arg = argv[++i]; }
else if (strncmp(a, "-pwd=", 5) == 0) { cfg.password_arg = a + 5; }
else if (strcmp(a, "-i") == 0 && i + 1 < argc) { cfg.keyfile = argv[++i]; }
else if (strcmp(a, "-o") == 0 && i + 1 < argc) {
const char *kv = argv[++i];
if (strncmp(kv, "StrictHostKeyChecking=", 22) == 0) {
const char *v = kv + 22;
if (strcmp(v, "no") == 0 || strcmp(v, "off") == 0) cfg.hostkey_mode = 2;
else if (strcmp(v, "accept-new") == 0) cfg.hostkey_mode = 1;
}
} else if (strcmp(a, "-b") == 0 && i + 1 < argc) { batch = argv[++i]; }
else if (a[0] == '-' && strlen(a) > 1) { continue; }
else { target = a; }
}
if (!target) { usage(); return 1; }
/* 拆分 [user@]host[:path] */
char host[512], ruser[128], start_path[1024];
const char *colon = strchr(target, ':');
const char *at = strchr(target, '@');
if (colon) {
size_t hl = (size_t)(colon - target);
if (at && at < colon) {
size_t ul = (size_t)(at - target);
if (ul < sizeof(ruser)) { memcpy(ruser, target, ul); ruser[ul] = '\0'; }
hl = (size_t)(colon - (at + 1));
if (hl < sizeof(host)) { memcpy(host, at + 1, hl); host[hl] = '\0'; }
} else {
if (hl < sizeof(host)) { memcpy(host, target, hl); host[hl] = '\0'; }
}
snprintf(start_path, sizeof(start_path), "%s", colon + 1);
} else {
size_t tl = strlen(target);
size_t hl = tl;
if (at && (size_t)(at - target) < tl) {
size_t ul = (size_t)(at - target);
if (ul < sizeof(ruser)) { memcpy(ruser, target, ul); ruser[ul] = '\0'; }
hl = tl - ul - 1;
if (hl < sizeof(host)) { memcpy(host, at + 1, hl); host[hl] = '\0'; }
} else {
if (hl < sizeof(host)) { memcpy(host, target, hl); host[hl] = '\0'; }
}
start_path[0] = '\0';
}
ssh_session_t *s = connect_session(&cfg, host, ruser[0] ? ruser : NULL);
if (!s) return 1;
uint32_t ch = 0;
if (ssh_channel_open(s, &ch, "session", 0, 0) < 0) {
fprintf(stderr, "psftp: 通道打开失败\n");
ssh_session_free(s);
return 1;
}
ssh_sftp_t *sf = NULL;
if (ssh_sftp_start(&sf, s, ch) != 0) {
fprintf(stderr, "psftp: 远端不支持 SFTP 子系统\n");
ssh_channel_close(s, ch);
ssh_session_free(s);
return 1;
}
psftp_t p = { sf, ".", "" };
cmd_lpwd(&p);
if (start_path[0]) cmd_cd(&p, start_path);
else cmd_pwd(&p);
int rc = 0;
if (batch) {
FILE *bf = fopen(batch, "r");
if (!bf) { fprintf(stderr, "psftp: 无法打开批处理文件 %s\n", batch); rc = 1; }
else {
char line[4096];
int first = 1;
while (fgets(line, sizeof(line), bf)) {
if (first) {
first = 0;
/* 跳过 UTF-8 BOM(EF BB BF),否则首行命令会带上 BOM 无法识别 */
uint8_t *lp = (uint8_t *)line;
if (lp[0] == 0xEF && lp[1] == 0xBB && lp[2] == 0xBF)
memmove(line, line + 3, strlen(line + 3) + 1);
}
if (run_line(&p, line)) break;
}
fclose(bf);
}
} else {
char line[4096];
for (;;) {
fprintf(stderr, "psftp> ");
fflush(stderr);
if (!fgets(line, sizeof(line), stdin)) break;
if (run_line(&p, line)) break;
}
}
ssh_sftp_free(sf);
ssh_channel_close(s, ch);
ssh_session_free(s);
return rc;
}
+65
查看文件
@@ -0,0 +1,65 @@
/* standalone.c —— 独立命令入口 (pscp/pkeygen/pkeyscan/pcopy-id/pagent/padd/psshd)
*
* : 'p' (pscp scp, pcopy-id copy-id),
* pssh (pssh_cmd_*)pssh.exe
*/
#include <string.h>
#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#endif
int pssh_cmd_ssh(int argc, char **argv);
int pssh_cmd_keygen(int argc, char **argv);
int pssh_cmd_keyscan(int argc, char **argv);
int pssh_cmd_copyid(int argc, char **argv);
int pssh_cmd_agent(int argc, char **argv);
int pssh_cmd_add(int argc, char **argv);
int pssh_cmd_scp(int argc, char **argv);
int pssh_cmd_sshd(int argc, char **argv);
static const char *exe_base(const char *p) {
const char *last = p;
for (const char *q = p; *q; q++)
if (*q == '/' || *q == '\\') last = q + 1;
return last;
}
int main(int argc, char **argv) {
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
#endif
const char *exe = exe_base(argv[0]);
size_t n = strlen(exe);
if (n > 4 && strcmp(exe + n - 4, ".exe") == 0) n -= 4;
if (n < 2 || n >= sizeof(char[64])) {
fprintf(stderr, "无法识别命令名\n");
return 1;
}
/* pXxx → xxx */
char cmd[64];
if (exe[0] == 'p' || exe[0] == 'P') {
memcpy(cmd, exe + 1, n - 1);
cmd[n - 1] = '\0';
} else {
memcpy(cmd, exe, n);
cmd[n] = '\0';
}
const char *av[64];
int cnt = argc < 63 ? argc : 63;
for (int i = 0; i < cnt; i++) av[i] = argv[i];
av[0] = cmd;
if (strcmp(cmd, "scp") == 0) return pssh_cmd_scp(cnt, (char **)av);
if (strcmp(cmd, "keygen") == 0) return pssh_cmd_keygen(cnt, (char **)av);
if (strcmp(cmd, "keyscan") == 0) return pssh_cmd_keyscan(cnt, (char **)av);
if (strcmp(cmd, "copy-id") == 0) return pssh_cmd_copyid(cnt, (char **)av);
if (strcmp(cmd, "agent") == 0) return pssh_cmd_agent(cnt, (char **)av);
if (strcmp(cmd, "add") == 0) return pssh_cmd_add(cnt, (char **)av);
if (strcmp(cmd, "sshd") == 0) return pssh_cmd_sshd(cnt, (char **)av);
if (strcmp(cmd, "ssh") == 0) return pssh_cmd_ssh(cnt, (char **)av);
fprintf(stderr, "未知命令: p%s\n", cmd);
return 1;
}
+23 -4
查看文件
@@ -1,5 +1,6 @@
# build.ps1 —— 无 CMake 环境下的源码构建脚本
# 编译三个静态库(paze_crypto/paze_ssl/paze_ssh)与 CLI 应用(pssh/pazessl)
# 发布 exe 全部输出到 bin/ 文件夹,中间产物(obj/静态库)留在 build/
# 用法: .\build.ps1 [-Clean] [-Debug]
param(
[switch]$Clean,
@@ -9,10 +10,11 @@ param(
$ErrorActionPreference = "Continue"
$Root = $PSScriptRoot
$Build = Join-Path $Root "build"
$Bin = Join-Path $Root "bin"
$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
New-Item -ItemType Directory -Force -Path $Build, $Obj, $Bin | Out-Null
$cc = "gcc"
$opt = if ($Debug) { "-O0", "-g" } else { "-O2" }
@@ -53,7 +55,7 @@ $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)
& $cc @cflags $Main @Libs -lws2_32 -ladvapi32 -o (Join-Path $Bin $Out)
if ($LASTEXITCODE -ne 0) { throw "link app failed: $Out" }
}
@@ -63,7 +65,7 @@ $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" })) {
foreach ($ad in (Get-ChildItem (Join-Path $Root "apps") -Directory | Where-Object { $_.Name -ne "pazessl" -and $_.Name -ne "pssh" -and $_.Name -ne "psftp" })) {
$am = Join-Path $ad.FullName "main.c"
if (-not (Test-Path $am)) { continue }
$o = Join-Path $Obj ("app_" + $ad.Name + ".o")
@@ -73,9 +75,26 @@ foreach ($ad in (Get-ChildItem (Join-Path $Root "apps") -Directory | Where-Objec
}
$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")
& $cc @cflags $appObjs @($sshA, $cryptoA) -lws2_32 -ladvapi32 -o (Join-Path $Bin "pssh.exe")
if ($LASTEXITCODE -ne 0) { throw "link pssh failed" }
# 独立命令: pscp/pkeygen/pkeyscan/pcopy-id/pagent/padd/psshd
# (standalone.c 按 exe 名分发到对应 pssh_cmd_*;链接各 app 实现,但排除 pssh main)
Write-Host "== standalone =="
$standaloneObj = Join-Path $Obj "app_standalone.o"
& $cc @cflags -c (Join-Path $Root "apps\pssh\standalone.c") -o $standaloneObj
if ($LASTEXITCODE -ne 0) { throw "compile standalone failed" }
$standaloneLibs = ($appObjs | Where-Object { $_ -notlike "*app_pssh_main*" }) + @($sshA, $cryptoA)
foreach ($scmd in @("pscp", "pkeygen", "pkeyscan", "pcopy-id", "pagent", "padd", "psshd")) {
& $cc @cflags $standaloneObj $standaloneLibs -lws2_32 -ladvapi32 -o (Join-Path $Bin "$scmd.exe")
if ($LASTEXITCODE -ne 0) { throw "link $scmd failed" }
}
# psftp: 独立 SFTP 客户端(自有 main,不并入 pssh)
if (Test-Path (Join-Path $Root "apps\psftp\main.c")) {
Link-App (Join-Path $Root "apps\psftp\main.c") @($sshA, $cryptoA) "psftp.exe"
}
Link-App (Join-Path $Root "apps\pazessl\main.c") @($sslA, $cryptoA) "pazessl.exe"
Write-Host "== done =="
+36 -1
查看文件
@@ -1,7 +1,7 @@
# TLS 1.2/1.3 自测脚本(一次性)
$ErrorActionPreference = "Continue"
$Root = "F:\TLS-SSH"
$exe = Join-Path $Root "build\pazessl.exe"
$exe = Join-Path $Root "bin\pazessl.exe"
$key = Join-Path $Root "build\key.pem"
$cert = Join-Path $Root "build\cert.pem"
@@ -57,3 +57,38 @@ Test-Conn "fallback-cli" "14435" @("s_server","-accept","14435","-cert",$cert,"-
# 回退场景 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")
# TLS 1.3 PSK 会话恢复:同一服务端进程内两连接,第二连接用第一连接的 session
function Test-PskResume {
$port = 14437
$sess = Join-Path $Root "build\session_resume.bin"
Remove-Item -ErrorAction SilentlyContinue $sess
$serr = Join-Path $Root "build\srv_psk.err.log"
$sout = Join-Path $Root "build\srv_psk.out.log"
Remove-Item -ErrorAction SilentlyContinue $serr, $sout
$srv = Start-Process -FilePath $exe -ArgumentList @("s_server","-accept","$port","-cert",$cert,"-key",$key,"-reuse","2") `
-NoNewWindow -PassThru -RedirectStandardOutput $sout -RedirectStandardError $serr
Start-Sleep -Milliseconds 1200
# 连接 #1: 全握手,保存 session
& $exe s_client -connect "127.0.0.1:$port" -msg hi -sess_out $sess 2>&1 | Out-Null
Start-Sleep -Milliseconds 500
# 连接 #2: 用 session 恢复
$c2out = Join-Path $Root "build\cli_psk2.err.log"
Remove-Item -ErrorAction SilentlyContinue $c2out
$c2 = Start-Process -FilePath $exe -ArgumentList @("s_client","-connect","127.0.0.1:$port","-msg","hi","-sess_in",$sess) `
-NoNewWindow -PassThru -RedirectStandardError $c2out
$resumed = $false
for ($i = 0; $i -lt 20; $i++) {
Start-Sleep -Milliseconds 300
$t = Get-Content $c2out -Raw -ErrorAction SilentlyContinue
if ($t -match "resumed YES") { $resumed = $true; break }
if ($c2.HasExited) { break }
}
if (-not $c2.HasExited) { Stop-Process -Id $c2.Id -Force }
if (-not $srv.HasExited) { Stop-Process -Id $srv.Id -Force }
Write-Host "== psk-resume =="
Write-Host "session-file=$([bool](Test-Path $sess)) resumed=$resumed"
if (Test-Path $serr) { Get-Content $serr | ForEach-Object { Write-Host "S: $_" } }
if (Test-Path $c2out) { Get-Content $c2out | ForEach-Object { Write-Host "C2: $_" } }
}
Test-PskResume
+4
查看文件
@@ -0,0 +1,4 @@
hello-psftp
+31
查看文件
@@ -171,6 +171,24 @@ typedef enum {
PAZE_TLS_VERIFY_REQUIRED = 2 /* 默认:服务端必须可信 */
} paze_tls_verify_mode_t;
/* ============================================================
* (TLS 1.3 PSK )
* ============================================================ */
typedef struct {
uint8_t ticket[512]; /* NewSessionTicket 的 ticket 字段 */
size_t ticket_len;
uint8_t psk[64]; /* 由 resumption_master_secret 派生的 PSK */
size_t psk_len;
uint32_t age_add; /* ticket_age_add */
int32_t recv_time; /* 收到 ticket 的 unix 时间(用于 obfuscated age) */
int hash; /* 关联 hash: 0=SHA-256, 1=SHA-384 */
int valid;
} paze_tls_session_t;
/* 客户端:设置要用于会话恢复的 PSK 会话(来自上一次 paze_tls_get_session) */
paze_status_t paze_tls_config_set_session(paze_tls_config_t *cfg,
const paze_tls_session_t *sess);
paze_tls_config_t *paze_tls_config_new(paze_tls_role_t role);
void paze_tls_config_free(paze_tls_config_t *cfg);
@@ -203,6 +221,19 @@ paze_status_t paze_tls_config_set_versions(paze_tls_config_t *cfg,
* ============================================================ */
typedef struct paze_tls paze_tls_t;
/* 握手后获取服务端下发的会话(客户端收到 NewSessionTicket 后);
* ticket 1.3 valid=0 */
paze_status_t paze_tls_get_session(const paze_tls_t *tls, paze_tls_session_t *sess);
/* 本次握手是否通过 PSK 会话恢复(客户端/服务端均适用) */
int paze_tls_session_resumed(const paze_tls_t *tls);
/* 服务端:导出本次连接使用的 session ticket key(供多连接共享以实现恢复);
* ready 0 */
paze_status_t paze_tls_get_session_ticket_key(const paze_tls_t *tls,
uint8_t key[32], int *ready);
/* 服务端:显式设置 session ticket key(使所有连接用同一密钥) */
paze_status_t paze_tls_config_set_session_ticket_key(paze_tls_config_t *cfg,
const uint8_t key[32]);
paze_tls_t *paze_tls_new(const paze_tls_config_t *cfg);
void paze_tls_free(paze_tls_t *tls);
+1 -1
查看文件
@@ -472,7 +472,7 @@ static int pubkey_eq(const paze_x509_cert_t *a, const paze_x509_cert_t *b) {
uint8_t ab[65], bb[65];
if (paze_p256_to_affine(&a->ecdsa_pub.Q, ab, 65) != PAZE_OK) return 0;
if (paze_p256_to_affine(&b->ecdsa_pub.Q, bb, 65) != PAZE_OK) return 0;
return paze_ct_equal(ab, bb, 65);
return paze_ct_equal(ab, bb, 65) == 0; /* ct_equal:0=相等 */
}
return 0;
}
+1
查看文件
@@ -66,6 +66,7 @@ static int srv_open_file(const char *path, uint32_t pflags) {
if (pflags & SSH_FXF_TRUNC) of |= O_TRUNC;
if (pflags & SSH_FXF_EXCL) of |= O_EXCL;
#ifdef _WIN32
of |= O_BINARY; /* 二进制模式:否则 _write 会把 \n 转成 \r\n,损坏上传内容 */
wchar_t w[2048];
if (utf8_to_wide(path, w, 2048) < 0) return -1;
return _wopen(w, of, _S_IREAD | _S_IWRITE);
+57
查看文件
@@ -213,6 +213,63 @@ paze_status_t paze_tls_config_set_versions(paze_tls_config_t *cfg,
return PAZE_OK;
}
/* ============================================================
* TLS 1.3 PSK API
* ============================================================ */
paze_status_t paze_tls_config_set_session(paze_tls_config_t *cfg,
const paze_tls_session_t *sess) {
if (!cfg || !sess) return PAZE_ERR_INVALID;
if (sess->valid && (sess->ticket_len == 0 || sess->ticket_len > sizeof(sess->ticket) ||
sess->psk_len == 0 || sess->psk_len > sizeof(sess->psk)))
return PAZE_ERR_INVALID;
cfg->session = *sess; /* 值拷贝 */
return PAZE_OK;
}
/* 握手后获取服务端下发的会话(客户端在收到 NewSessionTicket 后有效)。
* ticket 1.3 valid=0 */
paze_status_t paze_tls_get_session(const paze_tls_t *tls, paze_tls_session_t *sess) {
if (!tls || !sess) return PAZE_ERR_INVALID;
memset(sess, 0, sizeof(*sess));
if (tls->neg.version != PAZE_TLS_VERSION_1_3 || !tls->have_ticket ||
tls->recv_ticket_len == 0 || tls->psk_len == 0)
return PAZE_OK;
if (tls->recv_ticket_len > sizeof(sess->ticket) || tls->psk_len > sizeof(sess->psk))
return PAZE_OK;
memcpy(sess->ticket, tls->recv_ticket, tls->recv_ticket_len);
sess->ticket_len = tls->recv_ticket_len;
memcpy(sess->psk, tls->psk, tls->psk_len);
sess->psk_len = tls->psk_len;
sess->age_add = tls->ticket_age_add;
sess->recv_time = tls->ticket_recv_time;
sess->hash = tls->psk_hash;
sess->valid = 1;
return PAZE_OK;
}
/* 本次握手是否通过 PSK 会话恢复(客户端/服务端均适用) */
int paze_tls_session_resumed(const paze_tls_t *tls) {
return tls ? (tls->psk_mode && tls->handshake_done) : 0;
}
/* 服务端:导出 session ticket key(位于 cfg,cfg_owned 在 paze_tls_new 时拷贝) */
paze_status_t paze_tls_get_session_ticket_key(const paze_tls_t *tls,
uint8_t key[32], int *ready) {
if (!tls || !key || !ready) return PAZE_ERR_INVALID;
paze_tls_config_t *cfg = tls->cfg_owned ? tls->cfg_owned : (paze_tls_config_t *)tls->cfg;
*ready = cfg->ticket_key_ready;
if (cfg->ticket_key_ready) memcpy(key, cfg->ticket_key, 32);
return PAZE_OK;
}
paze_status_t paze_tls_config_set_session_ticket_key(paze_tls_config_t *cfg,
const uint8_t key[32]) {
if (!cfg || !key) return PAZE_ERR_INVALID;
memcpy(cfg->ticket_key, key, 32);
cfg->ticket_key_ready = 1;
return PAZE_OK;
}
/* ============================================================
*
* ============================================================ */
+103
查看文件
@@ -279,6 +279,109 @@ size_t paze_tls_ext_write_renegotiation_info(uint8_t *buf) {
return put_ext_header(buf, PAZE_TLS_EXT_RENEGOTIATION_INFO, body) + body;
}
/* ============================================================
* PSK (RFC 8446 §4.2.11)
* ============================================================ */
/* psk_key_exchange_modes(客户端): modes = psk_ke(0) | psk_dhe_ke(1)
* body = [1][0x00][0x01] */
size_t paze_tls_ext_write_psk_key_exchange_modes(uint8_t *buf) {
uint8_t *p = buf + 4;
p[0] = 0x02; p[1] = 0x00; p[2] = 0x01; /* len=2, psk_ke, psk_dhe_ke */
size_t body = 3;
return put_ext_header(buf, PAZE_TLS_EXT_PSK_KEY_EXCHANGE_MODES, body) + body;
}
/* pre_shared_key(客户端):
* body = identities: 2 || PskIdentity ×N
* binders: 2 || PskBinderEntry ×N
* PskIdentity = identity(2 + bytes) || obfuscated_ticket_age(4) */
size_t paze_tls_ext_write_pre_shared_key(uint8_t *buf,
const uint8_t *identity, size_t id_len,
uint32_t obfuscated_age,
const uint8_t *binder, size_t binder_len) {
uint8_t *p = buf + 4;
uint8_t *ident_len_p = p; p += 2;
paze_store16_be(p, (uint16_t)id_len); p += 2;
memcpy(p, identity, id_len); p += id_len;
paze_store32_be(p, obfuscated_age); p += 4;
size_t ident_total = (size_t)(p - ident_len_p);
paze_store16_be(ident_len_p, (uint16_t)(ident_total - 2));
uint8_t *binder_len_p = p; p += 2;
paze_store16_be(p, (uint16_t)binder_len); p += 2;
if (binder_len) {
memcpy(p, binder, binder_len); p += binder_len;
}
size_t binder_total = (size_t)(p - binder_len_p);
paze_store16_be(binder_len_p, (uint16_t)(binder_total - 2));
size_t body = (size_t)(p - (buf + 4));
return put_ext_header(buf, PAZE_TLS_EXT_PRE_SHARED_KEY, body) + body;
}
/* ServerHello 的 pre_shared_key: selected_identity(2 字节) */
size_t paze_tls_ext_write_psk_selected(uint8_t *buf, uint16_t selected) {
uint8_t *p = buf + 4;
paze_store16_be(p, selected);
size_t body = 2;
return put_ext_header(buf, PAZE_TLS_EXT_PRE_SHARED_KEY, body) + body;
}
/* 解析 pre_shared_key:取第一个 identity 与 binder
* body = identities: len(2) || (identity_len(2)+identity || age(4))×N
* binders: len(2) || (binder_len(1)+binder)×N */
int paze_tls_ext_parse_pre_shared_key(const uint8_t *data, size_t len,
const uint8_t **id, size_t *idlen,
uint32_t *obfuscated_age,
const uint8_t **binder, size_t *binderlen) {
if (len < 4) return 0;
size_t il = paze_load16_be(data);
if (len < 2 + il + 2) return 0;
size_t off = 2;
if (il >= 6) {
size_t idl = paze_load16_be(data + off);
if (2 + idl + 4 <= il) {
*id = data + off + 2; *idlen = idl;
*obfuscated_age = paze_load32_be(data + off + 2 + idl);
} else {
return 0;
}
}
off += il;
if (off + 2 > len) return 0;
size_t bl = paze_load16_be(data + off);
if (off + 2 + bl > len) return 0;
off += 2;
if (bl >= 1) {
size_t bdl = data[off];
if (1 + bdl <= bl && bdl > 0) {
*binder = data + off + 1; *binderlen = bdl;
} else {
*binder = NULL; *binderlen = 0;
}
} else {
*binder = NULL; *binderlen = 0;
}
return 1;
}
/* 解析 ServerHello pre_shared_key */
int paze_tls_ext_parse_psk_selected(const uint8_t *data, size_t len,
uint16_t *selected) {
if (len != 2) return 0;
*selected = paze_load16_be(data);
return 1;
}
/* psk_key_exchange_modes: body = len(1) || modes */
int paze_tls_ext_parse_psk_ke_modes(const uint8_t *data, size_t len) {
if (len < 1) return 0;
size_t ml = data[0];
if (len != 1 + ml) return 0;
for (size_t i = 0; i < ml; i++) if (data[1 + i] == 0x00) return 1; /* psk_ke */
return 0;
}
/* ============================================================
* :
* extensions ( 2 ext_total_len) cb
+332 -25
查看文件
@@ -1,8 +1,10 @@
/* tls_handshake13.c —— TLS 1.3 (RFC 8446) 完整握手状态机
*
* ( PSK, 0-RTT):
* ( 0-RTT):
* Client: ClientHello ServerHello EncryptedExtensions Certificate/CertificateVerify/Finished Finished
* Server: ClientHello ServerHello EncryptedExtensions/Certificate/CertificateVerify/Finished Finished
* PSK : CH pre_shared_key, ticket+binder
* SH selected_identity,; NewSessionTicket
*
* C++ lambda C
*/
@@ -125,6 +127,7 @@ static paze_status_t recv_hs(paze_tls_t *tls,
* ============================================================ */
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;
paze_status_t st = PAZE_OK;
uint8_t *p = out;
p += 4;
@@ -165,6 +168,57 @@ paze_status_t paze_tls13_build_ch(paze_tls_t *tls, uint8_t *out, size_t *outlen)
n = paze_tls_ext_write_alpn(p, cfg->alpn, cfg->alpn_count); p += n;
n = paze_tls_ext_write_renegotiation_info(p); p += n;
/* PSK 会话恢复:psk_key_exchange_modes + pre_shared_key(必须最后)。
* pre_shared_key :binder binder transcript hash,
* identities + binders , hash, binder */
if (tls->offered_psk && cfg->session.valid) {
PAZE_LOGD("CH offering PSK (hash=%d psklen=%zu ticketlen=%zu)",
tls->psk_hash, cfg->session.psk_len, cfg->session.ticket_len);
const paze_tls_session_t *ss = &cfg->session;
n = paze_tls_ext_write_psk_key_exchange_modes(p); p += n;
uint32_t age_ms = (uint32_t)((int32_t)time(NULL) - ss->recv_time) * 1000;
uint32_t obf = ss->age_add + age_ms;
uint8_t *psk_hdr = p; p += 4; /* pre_shared_key 扩展头占位 */
uint8_t *ident_len_p = p; p += 2; /* identities 总长 */
paze_store16_be(p, (uint16_t)cfg->session.ticket_len); p += 2;
memcpy(p, cfg->session.ticket, cfg->session.ticket_len); p += cfg->session.ticket_len;
paze_store32_be(p, obf); p += 4;
size_t il = (size_t)(p - ident_len_p);
paze_store16_be(ident_len_p, (uint16_t)(il - 2));
uint8_t *binders_total_p = p; p += 2; /* binders 总长(先占位) */
size_t hl = (tls->psk_hash == 1) ? 48 : 32;
paze_store16_be(binders_total_p, (uint16_t)(1 + hl));
/* binder 的 transcript hash 覆盖到 binders 长度字段为止,其中包含
* ext_total pre_shared_key ;binder
* (1+hl), binder ,使
* ClientHello */
paze_tls_hs_put_header(out, PAZE_TLS_HS_CLIENT_HELLO,
(size_t)(p - (out + 4)) + 1 + hl);
paze_store16_be(ext_total_len_ptr,
(uint16_t)((size_t)(p - (ext_total_len_ptr + 2)) + 1 + hl));
paze_store16_be(psk_hdr + 0, PAZE_TLS_EXT_PRE_SHARED_KEY);
paze_store16_be(psk_hdr + 2,
(uint16_t)((size_t)(p - (psk_hdr + 4)) + 1 + hl));
size_t ch_partial = (size_t)(p - out); /* binder 计算点 */
uint8_t tr_h[64];
if (tls->psk_hash == 1) paze_sha384(out, ch_partial, tr_h);
else paze_sha256(out, ch_partial, tr_h);
uint8_t binder[64];
st = paze_tls13_compute_binder(tls, tr_h, hl, binder);
if (st != PAZE_OK) return st;
*p++ = (uint8_t)hl;
memcpy(p, binder, hl); p += hl;
size_t psk_body = (size_t)(p - (psk_hdr + 4));
paze_store16_be(psk_hdr + 0, PAZE_TLS_EXT_PRE_SHARED_KEY);
paze_store16_be(psk_hdr + 2, (uint16_t)psk_body);
}
size_t ext_total = (size_t)(p - (ext_total_len_ptr + 2));
paze_store16_be(ext_total_len_ptr, (uint16_t)ext_total);
@@ -284,6 +338,8 @@ typedef struct {
uint16_t *g;
const uint8_t **pub;
size_t *plen;
uint16_t *sel_psk; /* ServerHello pre_shared_key selected_identity */
int *fpsk; /* ServerHello 含 pre_shared_key */
} sh_ext_ctx_t;
static int sh_ext_cb(uint16_t tp, const uint8_t *b, size_t l, void *x) {
@@ -294,6 +350,8 @@ static int sh_ext_cb(uint16_t tp, const uint8_t *b, size_t l, void *x) {
} else if (tp == PAZE_TLS_EXT_KEY_SHARE) {
*c->g = paze_tls_ext_parse_key_share_server(b, l, c->pub, c->plen);
if (*c->g) *c->fks = 1;
} else if (tp == PAZE_TLS_EXT_PRE_SHARED_KEY) {
if (paze_tls_ext_parse_psk_selected(b, l, c->sel_psk)) *c->fpsk = 1;
}
return 0;
}
@@ -347,6 +405,43 @@ static int ks_lookup_cb(uint16_t tp, const uint8_t *b, size_t l, void *x) {
/* ============================================================
* [D]
* ============================================================ */
/* 握手完成后(post-handshake)接收 NewSessionTicket(RFC 8446 §4.6.1):
* ticket PSK, tls->recv_ticket / tls->psk
* ,, */
static void client_recv_new_session_ticket(paze_tls_t *tls) {
/* resumption_master_secret 用完整握手 transcript(CH...client Finished);
* recv_hs , recv_hs ticket transcript */
if (paze_tls13_derive_resumption_master(tls) != PAZE_OK) return;
const uint8_t *body; size_t blen; uint8_t type;
if (recv_hs(tls, &body, &blen, &type, NULL, NULL) != PAZE_OK) return;
if (type != PAZE_TLS_HS_NEW_SESSION_TICKET) return;
if (blen < 4 + 4 + 1 + 2) return;
size_t p = 0;
tls->ticket_lifetime = paze_load32_be(body + p); p += 4;
tls->ticket_age_add = paze_load32_be(body + p); p += 4;
size_t nlen = body[p]; p += 1;
if (blen < p + nlen + 2) return;
if (nlen > sizeof(tls->ticket_nonce)) nlen = sizeof(tls->ticket_nonce);
memcpy(tls->ticket_nonce, body + p, nlen);
tls->ticket_nonce_len = nlen; p += nlen;
size_t tlen = paze_load16_be(body + p); p += 2;
if (blen != p + tlen) return;
if (tlen == 0 || tlen > sizeof(tls->recv_ticket)) return;
memcpy(tls->recv_ticket, body + p, tlen);
tls->recv_ticket_len = tlen;
tls->ticket_recv_time = (int32_t)time(NULL);
tls->have_ticket = 1;
/* PSK = HKDF-Expand-Label(resumption_master_secret, "resumption", nonce, Hash.length) */
tls->psk_hash = (tls->neg.hash == paze_hash_sha384()) ? 1 : 0;
size_t plen = sizeof(tls->psk);
if (paze_tls13_derive_resumption_psk(tls, tls->ticket_nonce, tls->ticket_nonce_len,
tls->psk, &plen) == PAZE_OK)
tls->psk_len = plen;
}
paze_status_t paze_tls13_client_handshake(paze_tls_t *tls) {
paze_tls_config_t *cfg = tls->cfg_owned ? tls->cfg_owned : (paze_tls_config_t *)tls->cfg;
paze_status_t st;
@@ -356,6 +451,19 @@ paze_status_t paze_tls13_client_handshake(paze_tls_t *tls) {
if (st != PAZE_OK) return st;
paze_tls_tr_init(&tls->transcript, paze_hash_sha256());
/* PSK 会话恢复准备:若配置了可用会话,设置 offered_psk 并派生
* binder_key( ClientHello binder)binder hash PSK
* hash, cipher */
if (cfg->session.valid && cfg->session.psk_len > 0 && cfg->session.ticket_len > 0) {
tls->offered_psk = 1;
tls->psk_hash = cfg->session.hash;
tls->psk_len = cfg->session.psk_len;
memcpy(tls->psk, cfg->session.psk, cfg->session.psk_len);
st = paze_tls13_psk_early_secret(tls, cfg->session.psk,
cfg->session.psk_len, cfg->session.hash);
if (st != PAZE_OK) return st;
}
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;
@@ -425,14 +533,31 @@ paze_status_t paze_tls13_client_handshake(paze_tls_t *tls) {
int found_v = 0, found_ks = 0;
uint16_t group = 0;
const uint8_t *ks_pub = NULL; size_t ks_publen = 0;
sh_ext_ctx_t sx = { &found_v, &found_ks, &group, &ks_pub, &ks_publen };
uint16_t sel_psk = 0; int fpsk = 0;
sh_ext_ctx_t sx = { &found_v, &found_ks, &group, &ks_pub, &ks_publen,
&sel_psk, &fpsk };
paze_tls_extensions_foreach(exts, 2 + ext_total, sh_ext_cb, &sx);
if (!found_v || !found_ks) { paze_tls_set_err(tls, "SH missing required ext"); return PAZE_ERR_PROTOCOL; }
if (!found_v) { paze_tls_set_err(tls, "SH missing required ext"); return PAZE_ERR_PROTOCOL; }
/* 服务端接受 PSK:selected_identity=0(客户端只提供 1 个 identity) */
if (fpsk && sel_psk == 0 && tls->offered_psk) {
tls->psk_mode = 1;
}
if (!found_ks) {
/* psk_ke 模式:服务端不返回 key_share(仅当接受 PSK 且无需 ECDHE) */
if (!tls->psk_mode) {
paze_tls_set_err(tls, "SH missing key share");
return PAZE_ERR_PROTOCOL;
}
}
tls->neg.group = group;
uint8_t shared[64]; size_t shared_len = 0;
st = paze_tls_kex_ecdh(&tls->kex, group, ks_pub, ks_publen, shared, &shared_len);
if (st != PAZE_OK) return st;
if (found_ks) {
st = paze_tls_kex_ecdh(&tls->kex, group, ks_pub, ks_publen, shared, &shared_len);
if (st != PAZE_OK) return st;
} else {
shared_len = 0; /* psk_ke: 无 ECDHE,handshake_secret IKM 用 0 */
}
st = paze_tls13_derive_handshake_keys(tls, shared, shared_len);
paze_memzero(shared, shared_len);
@@ -568,6 +693,9 @@ paze_status_t paze_tls13_client_handshake(paze_tls_t *tls) {
tls->write_key = &tls->app_write;
tls->handshake_done = 1;
tls->hs_state = PAZE_HS_CL_DONE;
/* 读取服务端的 NewSessionTicket(post-handshake,用 app keys 加密) */
client_recv_new_session_ticket(tls);
}
return PAZE_OK;
}
@@ -575,6 +703,154 @@ paze_status_t paze_tls13_client_handshake(paze_tls_t *tls) {
/* ============================================================
* [E]
* ============================================================ */
/* ticket 有效期(秒)与内部格式:
* [ver(1)][hash(1)][psk_len(1)][psk][lifetime(4)][mac(16)]
* mac = HMAC-SHA256(ticket_key, ticket[0:len-16]) 16 */
#define PAZE_TLS_TICKET_LIFETIME (7 * 24 * 3600)
#define PAZE_TLS_TICKET_MAC_LEN 16
#define PAZE_TLS_TICKET_VER 1
/* 服务端:解析 CH 的 pre_shared_key,验证 ticket 认证码与 PSK binder。
* 1, tls->psk / psk_len / psk_hash( psk_mode)
* exts CH ( 2 );ch_raw ClientHello( 4 ) */
static int server_try_psk(paze_tls_t *tls, const uint8_t *exts, size_t exts_len,
const uint8_t *ch_raw, size_t ch_raw_len) {
/* 定位 pre_shared_key 扩展(客户端保证其为最后一个扩展) */
const uint8_t *psk_body = NULL; size_t psk_body_len = 0;
size_t total = paze_load16_be(exts);
if (exts_len != 2 + total) { PAZE_LOGD("psk: ext total mismatch"); return 0; }
size_t off = 2;
while (off + 4 <= 2 + total) {
uint16_t tp = paze_load16_be(exts + off);
size_t el = paze_load16_be(exts + off + 2);
if (off + 4 + el > 2 + total) return 0;
if (tp == PAZE_TLS_EXT_PRE_SHARED_KEY) {
psk_body = exts + off + 4;
psk_body_len = el;
break;
}
off += 4 + el;
}
if (!psk_body) { PAZE_LOGD("psk: no pre_shared_key ext"); return 0; }
const uint8_t *id = NULL, *binder = NULL;
size_t idlen = 0, binderlen = 0;
uint32_t obf = 0;
if (!paze_tls_ext_parse_pre_shared_key(psk_body, psk_body_len, &id, &idlen,
&obf, &binder, &binderlen)) {
PAZE_LOGD("psk: parse psk body failed (len=%zu)", psk_body_len);
return 0;
}
/* 校验 ticket 内部格式与 MAC */
paze_tls_config_t *cfgc = tls->cfg_owned ? tls->cfg_owned : (paze_tls_config_t *)tls->cfg;
if (idlen < 3 + 1 + 4 + PAZE_TLS_TICKET_MAC_LEN) { PAZE_LOGD("psk: id too short %zu", idlen); return 0; }
if (id[0] != PAZE_TLS_TICKET_VER) { PAZE_LOGD("psk: bad ver %d", id[0]); return 0; }
size_t pl = id[2];
if (idlen != 3 + pl + 4 + PAZE_TLS_TICKET_MAC_LEN) { PAZE_LOGD("psk: id len mismatch %zu vs %zu", idlen, 3 + pl + 4 + PAZE_TLS_TICKET_MAC_LEN); return 0; }
if (pl == 0 || pl > sizeof(tls->psk)) { PAZE_LOGD("psk: bad pl %zu", pl); return 0; }
if (!cfgc->ticket_key_ready) { PAZE_LOGD("psk: ticket_key not ready"); return 0; }
uint8_t mac[32];
paze_hmac(paze_hash_sha256(), cfgc->ticket_key, 32, id, 3 + pl + 4, mac);
if (paze_ct_equal(mac, id + 3 + pl + 4, PAZE_TLS_TICKET_MAC_LEN) != 0) {
PAZE_LOGD("psk: ticket MAC mismatch");
return 0;
}
uint32_t lifetime = paze_load32_be(id + 3 + pl);
if (lifetime == 0 || (uint32_t)(int32_t)time(NULL) - lifetime >= PAZE_TLS_TICKET_LIFETIME) {
PAZE_LOGD("psk: ticket expired (lifetime=%u now=%d)", lifetime, (int32_t)time(NULL));
return 0;
}
/* 提取 PSK 与关联 hash */
tls->psk_hash = id[1];
if (tls->psk_hash != 0 && tls->psk_hash != 1) { PAZE_LOGD("psk: bad hash %d", tls->psk_hash); return 0; }
tls->psk_len = pl;
memcpy(tls->psk, id + 3, pl);
/* 验证 binder:
* binder = HMAC(binder_key, Hash(CH binders ))
* CH "到 binders 长度字段为止" hash;
* binders = bl( pre_shared_key body binders ) */
const paze_hash_alg_t *H = (tls->psk_hash == 1) ? paze_hash_sha384() : paze_hash_sha256();
if (binderlen != H->digest_len) { PAZE_LOGD("psk: binder len %zu != %zu", binderlen, H->digest_len); return 0; }
size_t il = paze_load16_be(psk_body);
if (psk_body_len < 2 + il + 2) { PAZE_LOGD("psk: body too short"); return 0; }
size_t bl = paze_load16_be(psk_body + 2 + il);
if (ch_raw_len < bl) { PAZE_LOGD("psk: ch too short %zu < %zu", ch_raw_len, bl); return 0; }
size_t partial_len = ch_raw_len - bl; /* CH 去掉 binder 列表(保留 binders 长度字段) */
uint8_t ph[64];
if (tls->psk_hash == 1) paze_sha384(ch_raw, partial_len, ph);
else paze_sha256(ch_raw, partial_len, ph);
if (paze_tls13_psk_early_secret(tls, tls->psk, tls->psk_len, tls->psk_hash) != PAZE_OK) {
PAZE_LOGD("psk: early secret failed");
return 0;
}
uint8_t exp_binder[64];
if (paze_tls13_compute_binder(tls, ph, H->digest_len, exp_binder) != PAZE_OK) {
PAZE_LOGD("psk: compute binder failed");
return 0;
}
if (paze_ct_equal(exp_binder, binder, binderlen) != 0) {
PAZE_LOGD("psk: binder mismatch");
return 0;
}
PAZE_LOGD("psk: PSK accepted (hash=%d psklen=%zu)", tls->psk_hash, tls->psk_len);
return 1;
}
/* 服务端:握手完成后下发 NewSessionTicket(RFC 8446 §4.6.1)。
* resumption_master_secret; app traffic keys */
static paze_status_t send_new_session_ticket(paze_tls_t *tls) {
uint8_t nonce[8];
if (paze_random(nonce, sizeof(nonce)) != PAZE_OK) return PAZE_ERR_PLATFORM;
uint8_t psk[64]; size_t psk_len = sizeof(psk);
paze_status_t st = paze_tls13_derive_resumption_psk(tls, nonce, sizeof(nonce),
psk, &psk_len);
if (st != PAZE_OK) return st;
/* 构造 ticket(认证封装)。ticket_key 存于 cfg:同一 cfg 的多连接共享,
* */
paze_tls_config_t *cfgc = tls->cfg_owned ? tls->cfg_owned : (paze_tls_config_t *)tls->cfg;
if (!cfgc->ticket_key_ready) {
if (paze_random(cfgc->ticket_key, 32) != PAZE_OK) return PAZE_ERR_PLATFORM;
cfgc->ticket_key_ready = 1;
}
uint8_t body[512]; size_t bp = 0;
body[bp++] = PAZE_TLS_TICKET_VER;
body[bp++] = (uint8_t)tls->psk_hash;
body[bp++] = (uint8_t)psk_len;
memcpy(body + bp, psk, psk_len); bp += psk_len;
paze_store32_be(body + bp, (uint32_t)time(NULL)); bp += 4; /* 签发时间 */
uint8_t mac[32];
paze_hmac(paze_hash_sha256(), cfgc->ticket_key, 32, body, bp, mac);
memcpy(body + bp, mac, PAZE_TLS_TICKET_MAC_LEN); bp += PAZE_TLS_TICKET_MAC_LEN;
size_t tlen = bp;
paze_memzero(psk, sizeof(psk));
/* NewSessionTicket 消息体:
* ticket_lifetime(4) ticket_age_add(4) ticket_nonce<0..255> ticket<1..2^16-1> */
uint8_t msg[700];
uint8_t *mp = msg;
paze_store32_be(mp, PAZE_TLS_TICKET_LIFETIME); mp += 4;
uint32_t age_add = 0;
if (paze_random((uint8_t *)&age_add, 4) != PAZE_OK) return PAZE_ERR_PLATFORM;
paze_store32_be(mp, age_add); mp += 4;
*mp++ = (uint8_t)sizeof(nonce);
memcpy(mp, nonce, sizeof(nonce)); mp += sizeof(nonce);
paze_store16_be(mp, (uint16_t)tlen); mp += 2;
memcpy(mp, body, tlen); mp += tlen;
size_t mlen = (size_t)(mp - msg);
/* post-handshake 消息:不计入 transcript */
uint8_t rec[700 + 4];
paze_tls_hs_put_header(rec, PAZE_TLS_HS_NEW_SESSION_TICKET, mlen);
memcpy(rec + 4, msg, mlen);
return paze_tls_record_send(tls, PAZE_TLS_CT_HANDSHAKE, rec, 4 + mlen);
}
paze_status_t paze_tls13_server_handshake(paze_tls_t *tls) {
paze_tls_config_t *cfg = tls->cfg_owned ? tls->cfg_owned : (paze_tls_config_t *)tls->cfg;
paze_status_t st;
@@ -638,6 +914,13 @@ paze_status_t paze_tls13_server_handshake(paze_tls_t *tls) {
if (!sx.g) return PAZE_ERR_PROTOCOL;
tls->neg.group = sx.g;
/* 尝试 PSK 会话恢复:验证 ticket 认证码与 binder。
* psk_mode=1,ServerHello selected_identity,
* Certificate/CertificateVerify */
tls->psk_mode = 0;
if (server_try_psk(tls, exts, 2 + ext_total, tls->ch1, tls->ch1_len))
tls->psk_mode = 1;
char sni_buf[PAZE_TLS_SNI_MAX + 1];
sni_buf[0] = 0;
if (sx.s) {
@@ -653,17 +936,19 @@ paze_status_t paze_tls13_server_handshake(paze_tls_t *tls) {
int have_share = 0;
ks_lookup_ctx_t ksl = { select_group, &ks_pub, &ks_publen, &have_share };
paze_tls_extensions_foreach(exts, 2 + ext_total, ks_lookup_cb, &ksl);
if (!have_share) {
uint8_t shared[64]; size_t shared_len = 0;
if (have_share) {
st = paze_tls_kex_generate(&tls->kex, (select_group == PAZE_TLS_GROUP_SECP256R1));
if (st != PAZE_OK) return st;
st = paze_tls_kex_ecdh(&tls->kex, select_group, ks_pub, ks_publen, shared, &shared_len);
if (st != PAZE_OK) return st;
} else if (!tls->psk_mode) {
/* 无 key_share:仅当纯 PSK 恢复(psk_ke)才允许 */
paze_tls_set_err(tls, "CH missing key share for group %d", select_group);
return PAZE_ERR_PROTOCOL;
}
st = paze_tls_kex_generate(&tls->kex, (select_group == PAZE_TLS_GROUP_SECP256R1));
if (st != PAZE_OK) return st;
uint8_t shared[64]; size_t shared_len = 0;
st = paze_tls_kex_ecdh(&tls->kex, select_group, ks_pub, ks_publen, shared, &shared_len);
if (st != PAZE_OK) return st;
/* psk_ke:shared_len 保持 0,handshake_secret IKM 用 0 */
/* Build ServerHello */
uint8_t sh[PAZE_TLS_MAX_RECORD];
@@ -681,11 +966,16 @@ paze_status_t paze_tls13_server_handshake(paze_tls_t *tls) {
uint8_t *extp = sp; sp += 2;
size_t n;
n = paze_tls_ext_write_supported_versions(sp, 0, 1, 0); sp += n;
if (select_group == PAZE_TLS_GROUP_X25519)
n = paze_tls_ext_write_key_share_server(sp, PAZE_TLS_GROUP_X25519, tls->kex.x25519_pub, 32);
else
n = paze_tls_ext_write_key_share_server(sp, PAZE_TLS_GROUP_SECP256R1, tls->kex.p256_pub, 65);
sp += n;
if (have_share) {
if (select_group == PAZE_TLS_GROUP_X25519)
n = paze_tls_ext_write_key_share_server(sp, PAZE_TLS_GROUP_X25519, tls->kex.x25519_pub, 32);
else
n = paze_tls_ext_write_key_share_server(sp, PAZE_TLS_GROUP_SECP256R1, tls->kex.p256_pub, 65);
sp += n;
}
if (tls->psk_mode) {
n = paze_tls_ext_write_psk_selected(sp, 0); sp += n;
}
paze_store16_be(extp, (uint16_t)(sp - extp - 2));
size_t sh_body_len = (size_t)(sp - (sh + 4));
paze_tls_hs_put_header(sh, PAZE_TLS_HS_SERVER_HELLO, sh_body_len);
@@ -720,12 +1010,15 @@ 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: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;
}
/* Certificate 与 CertificateVerify:PSK 恢复握手不发证书
* (RFC 8446 §4.2.11), */
if (!tls->psk_mode) {
/* 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; /* certificate_request_context 长度 */
@@ -765,6 +1058,7 @@ paze_status_t paze_tls13_server_handshake(paze_tls_t *tls) {
paze_store16_be(cv + 2, (uint16_t)sig_len);
st = send_hs(tls, PAZE_TLS_HS_CERTIFICATE_VERIFY, cv, 4 + sig_len);
if (st != PAZE_OK) return st;
} /* !psk_mode */
/* Finished */
uint8_t vd[64]; size_t vd_len = 0;
@@ -778,7 +1072,8 @@ 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, NULL, NULL);
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) continue;
if (type == PAZE_TLS_HS_CERTIFICATE_VERIFY) continue;
@@ -790,11 +1085,23 @@ paze_status_t paze_tls13_server_handshake(paze_tls_t *tls) {
st = verify_finished(tls, 0, body, blen);
if (st != PAZE_OK) { paze_tls_set_err(tls, "client Finished verify failed"); return PAZE_ERR_VERIFY; }
/* 先派生 app keys:其 transcript 为 CH...server Finished(不含 client Finished)。
* client Finished transcript, resumption_master_secret 使 */
st = paze_tls13_derive_app_keys(tls);
if (st != PAZE_OK) return st;
if (raw && raw_len) paze_tls_tr_update(&tls->transcript, raw, raw_len);
if (paze_tls13_derive_resumption_master(tls) != PAZE_OK) {
paze_tls_set_err(tls, "derive resumption master failed");
return PAZE_ERR_INVALID;
}
tls->read_key = &tls->app_read;
tls->write_key = &tls->app_write;
tls->handshake_done = 1;
/* 下发 NewSessionTicket(post-handshake,用 app keys 加密) */
st = send_new_session_ticket(tls);
if (st != PAZE_OK) return st;
tls->hs_state = PAZE_HS_SV_DONE;
}
return PAZE_OK;
+60
查看文件
@@ -120,6 +120,15 @@ struct paze_tls_config {
int enable_1_3;
int enable_1_2;
/* 客户端:要用于会话恢复的 PSK 会话(值拷贝) */
paze_tls_session_t session;
/* 服务端:session ticket 加密密钥(首次使用生成)。
* cfg( paze_tls_config_set_session_ticket_key)
* */
uint8_t ticket_key[32];
int ticket_key_ready;
};
/* ============================================================
@@ -202,6 +211,22 @@ struct paze_tls {
uint8_t client_finished_key[64];
uint8_t server_finished_key[64];
uint8_t binder_key[64];
uint8_t resumption_master_secret[64];
int resumption_master_valid;
/* PSK 状态 */
int psk_mode; /* 本次握手为 PSK 恢复(无 ECDHE) */
int psk_hash; /* PSK 关联 hash: 0=SHA-256, 1=SHA-384 */
uint8_t psk[64]; size_t psk_len; /* 本次使用的 PSK */
int offered_psk; /* 客户端 CH 中是否发送了 pre_shared_key */
/* 客户端收到的 NewSessionTicket */
uint8_t recv_ticket[512]; size_t recv_ticket_len;
uint8_t ticket_nonce[32]; size_t ticket_nonce_len;
uint32_t ticket_lifetime;
uint32_t ticket_age_add;
int32_t ticket_recv_time;
int have_ticket;
/* transcript */
paze_tls_transcript_t transcript;
@@ -314,6 +339,22 @@ paze_status_t paze_tls13_compute_finished(const paze_tls_t *tls,
const uint8_t *base_key, size_t bklen,
uint8_t *out_verify_data);
/* PSK / 会话恢复 (RFC 8446 §7.1/§7.2/§4.2.11) */
/* early_secret = HKDF-Extract(0, PSK); 并派生 binder_key */
paze_status_t paze_tls13_psk_early_secret(paze_tls_t *tls,
const uint8_t *psk, size_t psk_len,
int hash_id);
/* binder = HMAC(binder_key, transcript_hash) (transcript_hash 由调用方算好) */
paze_status_t paze_tls13_compute_binder(paze_tls_t *tls,
const uint8_t *tr_hash, size_t tr_hash_len,
uint8_t *binder_out);
/* resumption_master_secret = Derive-Secret(master_secret, "res master", tr) */
paze_status_t paze_tls13_derive_resumption_master(paze_tls_t *tls);
/* PSK = HKDF-Expand-Label(resumption_master_secret, "resumption", nonce, Hash.length) */
paze_status_t paze_tls13_derive_resumption_psk(paze_tls_t *tls,
const uint8_t *nonce, size_t nonce_len,
uint8_t *psk_out, size_t *psk_out_len);
/* TLS 1.2 PRF (HMAC-based) */
paze_status_t paze_tls12_prf(const paze_hash_alg_t *alg,
const uint8_t *secret, size_t slen,
@@ -334,6 +375,15 @@ size_t paze_tls_ext_write_renegotiation_info(uint8_t *buf);
/* 服务端:构造 key_share 扩展(单 group) */
size_t paze_tls_ext_write_key_share_server(uint8_t *buf, uint16_t group,
const uint8_t *pub, size_t publen);
/* psk_key_exchange_modes(客户端): psk_ke + psk_dhe_ke */
size_t paze_tls_ext_write_psk_key_exchange_modes(uint8_t *buf);
/* pre_shared_key(客户端): identities(1) + binder(1)。返回写入字节数。 */
size_t paze_tls_ext_write_pre_shared_key(uint8_t *buf,
const uint8_t *identity, size_t id_len,
uint32_t obfuscated_age,
const uint8_t *binder, size_t binder_len);
/* ServerHello 中的 pre_shared_key: selected_identity(2 字节) */
size_t paze_tls_ext_write_psk_selected(uint8_t *buf, uint16_t selected);
/* ---- extensions 解析 ---- */
int paze_tls_ext_parse_supported_versions(const uint8_t *data, size_t len, int *picked);
@@ -347,6 +397,16 @@ int paze_tls_ext_parse_sni(const uint8_t *data, size_t len,
char out[PAZE_TLS_SNI_MAX + 1]);
int paze_tls_ext_parse_alpn(const uint8_t *data, size_t len,
char out[PAZE_TLS_MAX_ALPN][256]);
/* 解析 pre_shared_key(服务端收 CH 用):取第一个 identity 与 binder */
int paze_tls_ext_parse_pre_shared_key(const uint8_t *data, size_t len,
const uint8_t **id, size_t *idlen,
uint32_t *obfuscated_age,
const uint8_t **binder, size_t *binderlen);
/* 解析 ServerHello pre_shared_key:selected_identity */
int paze_tls_ext_parse_psk_selected(const uint8_t *data, size_t len,
uint16_t *selected);
/* 解析 psk_key_exchange_modes:返回是否包含 psk_ke(0) */
int paze_tls_ext_parse_psk_ke_modes(const uint8_t *data, size_t len);
int paze_tls_extensions_foreach(const uint8_t *exts, size_t exts_len,
int (*cb)(uint16_t type, const uint8_t *body,
size_t blen, void *ctx),
+66 -4
查看文件
@@ -142,15 +142,25 @@ paze_status_t paze_tls13_derive_handshake_keys(paze_tls_t *tls,
size_t hl = H->digest_len;
uint8_t zero[64]; memset(zero, 0, sizeof(zero));
/* early_secret = HKDF-Extract(0, 0) (no PSK) */
paze_hkdf_extract(H, zero, hl, zero, hl, tls->early_secret);
/* early_secret = HKDF-Extract(0, PSK) 或 HKDF-Extract(0, 0) */
if (tls->psk_mode && tls->psk_len > 0) {
paze_hkdf_extract(H, zero, hl, tls->psk, tls->psk_len, tls->early_secret);
} else {
paze_hkdf_extract(H, zero, hl, zero, hl, tls->early_secret);
}
/* derived = Derive-Secret(early_secret, "derived", "") */
uint8_t derived[64];
paze_tls_derive_secret(H, tls->early_secret, hl, "derived", NULL, derived);
/* handshake_secret = HKDF-Extract(derived, ECDHE) */
paze_hkdf_extract(H, derived, hl, shared, shared_len, tls->handshake_secret);
/* handshake_secret = HKDF-Extract(derived, ECDHE)
* PSK-only (psk_ke, key_share): ECDHE, 0
* psk_dhe_ke : ECDHE shared() */
const uint8_t *ikm = shared;
size_t ikm_len = shared_len;
uint8_t zk[64]; memset(zk, 0, sizeof(zk));
if (tls->psk_mode && (!shared || shared_len == 0)) { ikm = zk; ikm_len = hl; }
paze_hkdf_extract(H, derived, hl, ikm, ikm_len, tls->handshake_secret);
/* client/server handshake traffic secrets */
uint8_t c_hs[64], s_hs[64];
@@ -217,6 +227,58 @@ paze_status_t paze_tls13_derive_app_keys(paze_tls_t *tls) {
return PAZE_OK;
}
/* ---- PSK / 会话恢复 (RFC 8446 §7.1/§7.2/§4.2.11) ---- */
static const paze_hash_alg_t *psk_hash_of(int hash_id) {
return hash_id == 1 ? paze_hash_sha384() : paze_hash_sha256();
}
/* early_secret = HKDF-Extract(0, PSK); binder_key = Derive-Secret(early_secret, "res binder", "") */
paze_status_t paze_tls13_psk_early_secret(paze_tls_t *tls,
const uint8_t *psk, size_t psk_len,
int hash_id) {
const paze_hash_alg_t *H = psk_hash_of(hash_id);
size_t hl = H->digest_len;
uint8_t zero[64]; memset(zero, 0, sizeof(zero));
paze_hkdf_extract(H, zero, hl, psk, psk_len, tls->early_secret);
return paze_tls_derive_secret(H, tls->early_secret, hl, "res binder", NULL, tls->binder_key);
}
/* 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,
uint8_t *binder_out) {
const paze_hash_alg_t *H = psk_hash_of(tls->psk_hash);
return paze_hmac(H, tls->binder_key, H->digest_len, tr_hash, tr_hash_len,
binder_out);
}
/* resumption_master_secret = Derive-Secret(master_secret, "res master", transcript)
* transcript transcript( Finished) */
paze_status_t paze_tls13_derive_resumption_master(paze_tls_t *tls) {
const paze_hash_alg_t *H = tls->neg.hash;
size_t hl = H->digest_len;
paze_status_t st = paze_tls_derive_secret(H, tls->master_secret, hl,
"res master", &tls->transcript,
tls->resumption_master_secret);
if (st == PAZE_OK) tls->resumption_master_valid = 1;
return st;
}
/* PSK = HKDF-Expand-Label(resumption_master_secret, "resumption", ticket_nonce, Hash.length) */
paze_status_t paze_tls13_derive_resumption_psk(paze_tls_t *tls,
const uint8_t *nonce, size_t nonce_len,
uint8_t *psk_out, size_t *psk_out_len) {
if (!tls->resumption_master_valid) return PAZE_ERR_INVALID;
const paze_hash_alg_t *H = tls->neg.hash;
size_t hl = H->digest_len;
if (*psk_out_len < hl) return PAZE_ERR_OVERFLOW;
paze_status_t st = paze_tls_hkdf_expand_label(H, tls->resumption_master_secret, hl,
"resumption", nonce, nonce_len,
psk_out, hl);
if (st == PAZE_OK) *psk_out_len = hl;
return st;
}
/* ---- Finished verify_data ---- */
paze_status_t paze_tls13_compute_finished(const paze_tls_t *tls,
const uint8_t *finished_key, size_t fklen,
+71
查看文件
@@ -0,0 +1,71 @@
/* dbg_curve.c —— 独立验证曲线方程 y^2 = x^3 - 3x + b (mod p)
* :, 2G y */
#include "paze/p256.h"
#include "paze/bignum.h"
#include <stdio.h>
#include <string.h>
static const uint8_t TWO_G_X[32] = {
0x7c,0xf2,0x7b,0x18,0x8d,0x03,0x4f,0x7e,0x8a,0x52,0x38,0x03,0x04,0xb5,0x1a,0xc3,
0xc0,0x89,0x69,0xe2,0x77,0xf2,0x1b,0x35,0xa6,0x0b,0x48,0xfc,0x47,0x66,0x99,0x78
};
/* 记忆中标准 2G y */
static const uint8_t TWO_G_Y_KNOWN[32] = {
0x77,0x75,0x51,0x0d,0xb8,0xed,0xcc,0xa2,0x93,0xdd,0xaa,0xc3,0xf9,0xa3,0xf5,0x8f,
0x6e,0x98,0xa2,0x65,0xf9,0x28,0xcf,0x2b,0x0f,0x43,0xf7,0xee,0x97,0xc1,0xa3,0x0a
};
/* 程序算出的 2G y */
static const uint8_t TWO_G_Y_GOT[32] = {
0x07,0x77,0x55,0x10,0xdb,0x8e,0xd0,0x40,0x29,0x3d,0x9a,0xc6,0x9f,0x74,0x30,0xdb,
0xba,0x7d,0xad,0xe6,0x3c,0xe9,0x82,0x29,0x9e,0x04,0xb7,0x9d,0x22,0x78,0x73,0xd1
};
/* 独立实现曲线方程(不经 p256_from_affine,避免同源 bug) */
static int on_curve(const uint8_t xb[32], const uint8_t yb[32]) {
paze_bn_t p, b, x, y, lhs, rhs, x3, three, t;
paze_p256_load_p(&p);
paze_bn_from_bytes(&b, (const uint8_t *)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32);
paze_bn_from_bytes(&x, xb, 32);
paze_bn_from_bytes(&y, yb, 32);
paze_bn_modmul(&lhs, &y, &y, &p);
paze_bn_modmul(&x3, &x, &x, &p);
paze_bn_modmul(&rhs, &x3, &x, &p);
paze_bn_set_u32(&three, 3);
paze_bn_modmul(&t, &three, &x, &p);
paze_bn_modsub(&rhs, &rhs, &t, &p);
paze_bn_modadd(&rhs, &rhs, &b, &p);
return paze_bn_cmp(&lhs, &rhs) == 0;
}
static void print_bn(const char *tag, const paze_bn_t *v) {
char hex[300];
paze_bn_to_hex(v, hex, sizeof(hex));
printf("%s: %s\n", tag, hex);
}
int main(void) {
printf("known 2G y on curve: %d\n", on_curve(TWO_G_X, TWO_G_Y_KNOWN));
printf("got 2G y on curve: %d\n", on_curve(TWO_G_X, TWO_G_Y_GOT));
/* 打印 x^3-3x+b 和 y^2 的具体值,便于对比 */
paze_bn_t p;
paze_p256_load_p(&p);
paze_bn_t x, y, lhs, rhs, x3, three, t, b;
paze_bn_from_bytes(&x, TWO_G_X, 32);
paze_bn_from_bytes(&y, TWO_G_Y_KNOWN, 32);
paze_bn_from_bytes(&b, (const uint8_t *)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32);
paze_bn_modmul(&lhs, &y, &y, &p);
paze_bn_modmul(&x3, &x, &x, &p);
paze_bn_modmul(&rhs, &x3, &x, &p);
paze_bn_set_u32(&three, 3);
paze_bn_modmul(&t, &three, &x, &p);
paze_bn_modsub(&rhs, &rhs, &t, &p);
paze_bn_modadd(&rhs, &rhs, &b, &p);
print_bn("known: y^2", &lhs);
print_bn("known: x^3-3x+b", &rhs);
paze_bn_from_bytes(&y, TWO_G_Y_GOT, 32);
paze_bn_modmul(&lhs, &y, &y, &p);
print_bn("got : y^2", &lhs);
return 0;
}
+70
查看文件
@@ -0,0 +1,70 @@
/* dbg_div2.c —— divmod 小案例与中间量定位 */
#include "paze/p256.h"
#include "paze/bignum.h"
#include <stdio.h>
#include <string.h>
static void chk(const char *tag, int cond) {
printf("%s: %s\n", tag, cond ? "PASS" : "FAIL");
}
static void hexbn(const char *tag, const paze_bn_t *x) {
char h[600];
paze_bn_to_hex(x, h, sizeof(h));
printf("%s = %s (n=%d)\n", tag, h, x->n);
}
int main(void) {
paze_bn_t q, r, a, b;
/* 小 case */
paze_bn_set_u32(&a, 10); paze_bn_set_u32(&b, 3);
paze_bn_divmod(&q, &r, &a, &b);
chk("10/3 q==3", paze_bn_cmp(&q, &(paze_bn_t){{},0})==0 || q.n==1 && q.d[0]==3);
chk("10/3 r==1", r.n==1 && r.d[0]==1);
/* 2^32 / 2 */
paze_bn_set_u32(&a, 0xFFFFFFFFu);
paze_bn_set_u64(&b, 2);
paze_bn_t a32;
paze_bn_set_u64(&a32, 1);
paze_bn_lshift(&a32, &a32, 32);
paze_bn_divmod(&q, &r, &a32, &b);
chk("2^32/2 q==2^31", q.n==2 && q.d[0]==0x80000000u);
chk("2^32/2 r==0", paze_bn_is_zero(&r));
/* (p+1)/p */
paze_bn_t p;
paze_p256_load_p(&p);
paze_bn_t p1;
paze_bn_set_u32(&p1, 1);
paze_bn_add(&p1, &p, &p1);
paze_bn_divmod(&q, &r, &p1, &p);
chk("(p+1)/p q==1 r==1", q.n==1 && q.d[0]==1 && r.n==1 && r.d[0]==1);
/* 2^256 / p == 1 (2^256 > p, < 2p) */
paze_bn_t b256;
paze_bn_set_u32(&b256, 1);
paze_bn_lshift(&b256, &b256, 256);
paze_bn_divmod(&q, &r, &b256, &p);
chk("2^256/p q==1", q.n==1 && q.d[0]==1);
{
hexbn("r(2^256 mod p)", &r);
}
/* 2^257 / p == 2 */
paze_bn_t b257;
paze_bn_set_u32(&b257, 1);
paze_bn_lshift(&b257, &b257, 257);
paze_bn_divmod(&q, &r, &b257, &p);
chk("2^257/p q==2", q.n==1 && q.d[0]==2);
/* 2^300 / p */
paze_bn_t b300;
paze_bn_set_u32(&b300, 1);
paze_bn_lshift(&b300, &b300, 300);
paze_bn_divmod(&q, &r, &b300, &p);
hexbn("q(2^300/p)", &q);
hexbn("r(2^300 mod p)", &r);
return 0;
}
+78
查看文件
@@ -0,0 +1,78 @@
/* dbg_div3.c —— divmod 恒等式自检: q*b + r == a 且 0 <= r < b */
#include "paze/p256.h"
#include "paze/bignum.h"
#include <stdio.h>
#include <string.h>
static uint32_t xr = 0x9e3779b9u;
static uint32_t rnd(void) { xr ^= xr << 13; xr ^= xr >> 17; xr ^= xr << 5; return xr; }
int main(void) {
paze_bn_t p;
paze_p256_load_p(&p);
/* case 1: a = (p-1)^2, b = p */
paze_bn_t pm1, a, b, q, r, chk, prod;
paze_bn_set_u32(&pm1, 1);
paze_bn_sub(&pm1, &p, &pm1);
paze_bn_mul(&a, &pm1, &pm1);
paze_bn_divmod(&q, &r, &a, &p);
paze_bn_mul(&prod, &q, &p);
paze_bn_add(&chk, &prod, &r);
printf("case1 q*b+r==a: %d (q.n=%d r.n=%d a.n=%d)\n",
paze_bn_cmp(&chk, &a) == 0, q.n, r.n, a.n);
{
char h[700];
paze_bn_to_hex(&q, h, sizeof(h)); printf("q = %s\n", h);
paze_bn_to_hex(&r, h, sizeof(h)); printf("r = %s\n", h);
/* 期望 q = p-2, r = 1 */
paze_bn_t pm2;
paze_bn_set_u32(&pm2, 2);
paze_bn_sub(&pm2, &p, &pm2);
paze_bn_to_hex(&pm2, h, sizeof(h)); printf("expect q = %s\n", h);
printf("q==p-2: %d r==1: %d\n", paze_bn_cmp(&q, &pm2) == 0,
paze_bn_is_one(&r));
}
/* case 2: 随机 a (8-16 limbs) 除以 p */
int fails = 0;
for (int iter = 0; iter < 200 && !fails; iter++) {
uint8_t ab[40];
for (int i = 0; i < 40; i++) ab[i] = (uint8_t)rnd();
paze_bn_from_bytes(&a, ab, 40); /* 320 位 */
paze_bn_divmod(&q, &r, &a, &p);
paze_bn_mul(&prod, &q, &p);
paze_bn_add(&chk, &prod, &r);
if (paze_bn_cmp(&chk, &a) != 0) {
printf("case2 FAIL iter=%d\n", iter);
fails++;
}
/* 0 <= r < p */
if (paze_bn_cmp(&r, &p) >= 0) {
printf("case2 r>=p FAIL iter=%d\n", iter);
fails++;
}
}
printf("case2 random 320-bit: fails=%d\n", fails);
/* case 3: 随机 512 位 a */
fails = 0;
for (int iter = 0; iter < 200 && !fails; iter++) {
uint8_t ab[64];
for (int i = 0; i < 64; i++) ab[i] = (uint8_t)rnd();
paze_bn_from_bytes(&a, ab, 64); /* 512 位 */
paze_bn_divmod(&q, &r, &a, &p);
paze_bn_mul(&prod, &q, &p);
paze_bn_add(&chk, &prod, &r);
if (paze_bn_cmp(&chk, &a) != 0) {
printf("case3 FAIL iter=%d\n", iter);
fails++;
}
if (paze_bn_cmp(&r, &p) >= 0) {
printf("case3 r>=p FAIL iter=%d\n", iter);
fails++;
}
}
printf("case3 random 512-bit: fails=%d\n", fails);
return 0;
}
+79
查看文件
@@ -0,0 +1,79 @@
/* dbg_div4.c —— 找出 divmod 最小失败案例 */
#include "paze/p256.h"
#include "paze/bignum.h"
#include <stdio.h>
#include <string.h>
static int check(const char *tag, const paze_bn_t *a, const paze_bn_t *b) {
paze_bn_t q, r, prod, chk;
paze_bn_divmod(&q, &r, a, b);
paze_bn_mul(&prod, &q, b);
paze_bn_add(&chk, &prod, &r);
if (paze_bn_cmp(&chk, a) != 0) {
printf("%s: FAIL\n", tag);
char h[800];
paze_bn_to_hex(&q, h, sizeof(h)); printf(" q=%s\n", h);
paze_bn_to_hex(&r, h, sizeof(h)); printf(" r=%s\n", h);
return 1;
}
if (paze_bn_cmp(&r, b) >= 0) {
printf("%s: r>=b FAIL\n", tag);
return 1;
}
return 0;
}
int main(void) {
paze_bn_t p, pm1, pm2, a, b, t;
paze_p256_load_p(&p);
paze_bn_set_u32(&pm1, 1); paze_bn_sub(&pm1, &p, &pm1);
paze_bn_set_u32(&pm2, 2); paze_bn_sub(&pm2, &p, &pm2);
/* k*p / p == k */
int fails = 0;
for (uint32_t k = 2; k <= 20; k++) {
paze_bn_set_u32(&t, k);
paze_bn_mul(&a, &t, &p);
paze_bn_divmod(&t, &b, &a, &p);
paze_bn_set_u32(&t, 0);
if (check("k*p/p", &a, &p)) { fails++; break; }
}
printf("k*p/p (k=2..20): %s\n", fails ? "FAIL" : "PASS");
/* (p-1)*k / p */
fails = 0;
for (uint32_t k = 2; k <= 10; k++) {
paze_bn_set_u32(&t, k);
paze_bn_mul(&a, &pm1, &t);
if (check("(p-1)*k/p", &a, &p)) { fails++; break; }
}
printf("(p-1)*k/p (k=2..10): %s\n", fails ? "FAIL" : "PASS");
/* (p-2)*p + 1 == (p-1)^2 结构 */
paze_bn_mul(&a, &pm2, &p);
paze_bn_set_u32(&t, 1);
paze_bn_add(&a, &a, &t); /* a = (p-2)*p + 1 */
if (check("(p-2)*p+1 / p", &a, &p)) { fails++; }
printf("(p-2)*p+1/p: %s\n", fails ? "FAIL" : "PASS");
/* a = p^2 (除以 p 应得 p) */
paze_bn_mul(&a, &p, &p);
fails = 0;
if (check("p^2/p", &a, &p)) { fails++; }
printf("p^2/p: %s\n", fails ? "FAIL" : "PASS");
/* a = p^2 + p + 1 */
paze_bn_add(&a, &a, &p);
paze_bn_set_u32(&t, 1);
paze_bn_add(&a, &a, &t);
if (check("(p^2+p+1)/p", &a, &p)) { fails++; }
printf("(p^2+p+1)/p: %s\n", fails ? "FAIL" : "PASS");
/* a = (p-1)^3 除以 p */
paze_bn_t a3;
paze_bn_mul(&a, &pm1, &pm1);
paze_bn_mul(&a3, &a, &pm1);
if (check("(p-1)^3/p", &a3, &p)) { fails++; }
printf("(p-1)^3/p: %s\n", fails ? "FAIL" : "PASS");
return 0;
}
+84
查看文件
@@ -0,0 +1,84 @@
/* dbg_divmod.c —— divmod/mod 针对性测试 */
#include "paze/p256.h"
#include "paze/bignum.h"
#include <stdio.h>
#include <string.h>
static void chk(const char *tag, int cond) {
printf("%s: %s\n", tag, cond ? "PASS" : "FAIL");
}
int main(void) {
paze_bn_t p, pm1, t, r, q;
paze_p256_load_p(&p);
paze_bn_set_u32(&pm1, 1);
paze_bn_sub(&pm1, &p, &pm1);
/* mod(p, p) == 0 */
paze_bn_mod(&r, &p, &p);
chk("p mod p == 0", paze_bn_is_zero(&r));
/* mod(p+1, p) == 1 */
paze_bn_t p1;
paze_bn_add(&p1, &p, &pm1); /* p + (p-1) 太大,换个: p+1 */
paze_bn_t one; paze_bn_set_u32(&one, 1);
paze_bn_add(&p1, &p, &one);
paze_bn_mod(&r, &p1, &p);
chk("(p+1) mod p == 1", paze_bn_is_one(&r));
/* mod(p^2, p) == 0 */
paze_bn_t p2;
paze_bn_mul(&p2, &p, &p);
paze_bn_mod(&r, &p2, &p);
chk("p^2 mod p == 0", paze_bn_is_zero(&r));
/* mod((p-1)^2, p) == 1 */
paze_bn_t pm1sq;
paze_bn_mul(&pm1sq, &pm1, &pm1);
paze_bn_mod(&r, &pm1sq, &p);
chk("(p-1)^2 mod p == 1", paze_bn_is_one(&r));
{
char h[300];
paze_bn_to_hex(&r, h, sizeof(h));
printf(" (p-1)^2 mod p = %s\n", h);
}
/* mod((p-1)^3, p) == p-1 */
paze_bn_t pm1cb;
paze_bn_mul(&pm1cb, &pm1sq, &pm1);
paze_bn_mod(&r, &pm1cb, &p);
chk("(p-1)^3 mod p == p-1", paze_bn_cmp(&r, &pm1) == 0);
/* divmod 商: (p^2) / p == p */
paze_bn_divmod(&q, &r, &p2, &p);
chk("p^2 / p == p", paze_bn_cmp(&q, &p) == 0);
/* (p-1)^2 / (p-1) == p-1 */
paze_bn_divmod(&q, &r, &pm1sq, &pm1);
chk("(p-1)^2 / (p-1) == p-1", paze_bn_cmp(&q, &pm1) == 0);
/* 随机大数乘法取模一致性 */
int fails = 0;
uint32_t xr = 7;
for (int i = 0; i < 50 && !fails; i++) {
paze_bn_t a, b;
uint8_t ab[32], bb[32];
for (int k = 0; k < 32; k++) {
xr ^= xr << 13; xr ^= xr >> 17; xr ^= xr << 5;
ab[k] = (uint8_t)xr;
xr ^= xr << 13; xr ^= xr >> 17; xr ^= xr << 5;
bb[k] = (uint8_t)xr;
}
ab[0] &= 0x7F; bb[0] &= 0x7F;
paze_bn_from_bytes(&a, ab, 32);
paze_bn_from_bytes(&b, bb, 32);
paze_bn_modmul(&r, &a, &b, &p);
paze_bn_modmul(&t, &b, &a, &p);
if (paze_bn_cmp(&r, &t) != 0) {
printf("modmul FAIL iter=%d\n", i);
fails++;
}
}
chk("random modmul commutative", fails == 0);
return 0;
}
+31
查看文件
@@ -0,0 +1,31 @@
/* dbg_ecdsa.c —— ECDSA sign/verify 单元定位 */
#include "paze/ecdsa.h"
#include "paze/bignum.h"
#include <stdio.h>
#include <string.h>
int main(void) {
paze_ecdsa_priv_t priv;
/* 用 gen 得到随机 d */
if (paze_ecdsa_gen(&priv) != PAZE_OK) { printf("gen failed\n"); return 1; }
paze_ecdsa_pub_t pub;
paze_ecdsa_pub_from_priv(&pub, priv.d);
uint8_t msg[32];
memset(msg, 0x42, 32);
uint8_t sig[128]; size_t siglen = sizeof(sig);
paze_status_t st = paze_ecdsa_sign(&priv, PAZE_RSA_HASH_SHA256, msg, 32, sig, &siglen);
printf("sign st=%d siglen=%zu\n", st, siglen);
printf("sig hex: "); for (size_t i = 0; i < siglen; i++) printf("%02x", sig[i]); printf("\n");
if (st != PAZE_OK) return 1;
paze_bn_t r, s;
st = paze_ecdsa_sig_from_der(&r, &s, sig, siglen);
printf("sig_from_der st=%d\n", st);
st = paze_ecdsa_verify(&pub, PAZE_RSA_HASH_SHA256, msg, 32, sig, siglen);
printf("verify st=%d (0=OK)\n", st);
/* 篡改消息应失败 */
msg[0] ^= 1;
st = paze_ecdsa_verify(&pub, PAZE_RSA_HASH_SHA256, msg, 32, sig, siglen);
printf("verify tampered st=%d (期望非0)\n", st);
return 0;
}
+81
查看文件
@@ -0,0 +1,81 @@
/* dbg_ecdsa_cert.c —— 定位 OpenSSL ECDSA 证书验签失败点 */
#include "paze/x509.h"
#include "paze/ecdsa.h"
#include "paze/hash.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
static uint8_t *read_file(const char *path, size_t *out_len) {
FILE *f = fopen(path, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
long flen = ftell(f);
fseek(f, 0, SEEK_SET);
if (flen <= 0) { fclose(f); return NULL; }
uint8_t *buf = (uint8_t *)malloc((size_t)flen + 1);
if (!buf) { fclose(f); return NULL; }
size_t n = fread(buf, 1, (size_t)flen, f);
fclose(f);
buf[n] = 0;
*out_len = n;
return buf;
}
int main(int argc, char **argv) {
if (argc < 2) { printf("usage: dbg_ecdsa_cert cert.pem\n"); return 1; }
size_t clen;
uint8_t *cd = read_file(argv[1], &clen);
if (!cd) { printf("read failed\n"); return 1; }
paze_x509_cert_t cert;
paze_status_t st = paze_x509_load_pem(&cert, (const char *)cd);
printf("parse st=%d\n", st);
if (st != PAZE_OK) return 1;
printf("sig_alg=%d pk_kind=%d\n", cert.sig_alg, cert.pk_kind);
printf("tbs_der_len=%zu signature_len=%zu\n", cert.tbs_der_len, cert.signature_len);
printf("self_signed=%d\n", paze_x509_is_self_signed(&cert));
/* tbs hash */
uint8_t h[64];
paze_sha256(cert.tbs_der, cert.tbs_der_len, h);
printf("sha256(tbs)=");
for (int i = 0; i < 32; i++) printf("%02x", h[i]);
printf("\n");
/* signature hex */
printf("signature=");
for (size_t i = 0; i < cert.signature_len; i++) printf("%02x", cert.signature[i]);
printf("\n");
/* ECDSA 直接验签 */
st = paze_ecdsa_verify(&cert.ecdsa_pub, PAZE_RSA_HASH_SHA256, h, 32,
cert.signature, cert.signature_len);
printf("ecdsa_verify st=%d\n", st);
/* 完整 verify_signature */
st = paze_x509_verify_signature(&cert, &cert);
printf("x509_verify_signature st=%d\n", st);
/* verify_chain:加载同一文件作为 anchor */
{
paze_x509_cert_t anchor;
st = paze_x509_load_pem(&anchor, (const char *)cd);
printf("anchor parse st=%d\n", st);
if (st == PAZE_OK) {
int64_t now = (int64_t)time(NULL);
printf("now=%lld not_before=%lld not_after=%lld\n",
(long long)now, (long long)cert.not_before, (long long)cert.not_after);
printf("validity st=%d\n", paze_x509_check_validity(&cert, now));
st = paze_x509_verify_chain(&cert, 1, &anchor, 1, now);
printf("x509_verify_chain st=%d\n", st);
paze_x509_free(&anchor);
}
}
paze_x509_free(&cert);
free(cd);
return 0;
}
+86
查看文件
@@ -0,0 +1,86 @@
/* dbg_mod.c —— 模运算一致性验证
* : (a-b mod p) + b a (mod p); (a*b mod p) /
* (xorshift) */
#include "paze/p256.h"
#include "paze/bignum.h"
#include <stdio.h>
#include <string.h>
static uint32_t xr = 0x12345678u;
static uint32_t rnd(void) {
xr ^= xr << 13; xr ^= xr >> 17; xr ^= xr << 5;
return xr;
}
int main(void) {
paze_bn_t p;
paze_p256_load_p(&p);
int fails = 0;
/* 构造随机数 a,b < p */
for (int iter = 0; iter < 200; iter++) {
paze_bn_t a, b, r, t, chk;
uint8_t ab[32], bb[32];
for (int i = 0; i < 32; i++) {
ab[i] = (uint8_t)rnd();
bb[i] = (uint8_t)rnd();
}
/* 截断确保 < p: 高 2 字节限制 */
ab[0] = (uint8_t)(ab[0] & 0x7F);
bb[0] = (uint8_t)(bb[0] & 0x7F);
paze_bn_from_bytes(&a, ab, 32);
paze_bn_from_bytes(&b, bb, 32);
/* modsub: r = a-b; 验证 r + b == a (mod p) */
paze_bn_modsub(&r, &a, &b, &p);
paze_bn_modadd(&chk, &r, &b, &p);
if (paze_bn_cmp(&chk, &a) != 0) {
printf("modsub fail iter=%d\n", iter);
fails++;
break;
}
/* modmul: a*b == b*a */
paze_bn_modmul(&r, &a, &b, &p);
paze_bn_modmul(&t, &b, &a, &p);
if (paze_bn_cmp(&r, &t) != 0) {
printf("modmul commut fail iter=%d\n", iter);
fails++;
break;
}
/* modmul 结合律: (a*b)*c == a*(b*c) */
paze_bn_t c;
uint8_t cb[32];
for (int i = 0; i < 32; i++) cb[i] = (uint8_t)rnd();
cb[0] &= 0x7F;
paze_bn_from_bytes(&c, cb, 32);
paze_bn_modmul(&t, &r, &c, &p);
paze_bn_modmul(&chk, &b, &c, &p);
paze_bn_modmul(&chk, &a, &chk, &p);
if (paze_bn_cmp(&r, &chk) != 0 && paze_bn_cmp(&t, &chk) != 0) {
/* 比较 (a*b)*c 与 a*(b*c) */
if (paze_bn_cmp(&t, &chk) != 0) {
printf("modmul assoc fail iter=%d\n", iter);
fails++;
break;
}
}
}
printf("mod tests fails=%d\n", fails);
/* 专门测试边界值 */
paze_bn_t a, b, r, chk;
paze_bn_set_u32(&a, 1);
paze_bn_set_u32(&b, 2);
paze_bn_modsub(&r, &a, &b, &p); /* 1-2 = p-1 */
paze_bn_t pm1;
paze_bn_set_u32(&pm1, 1);
paze_bn_sub(&pm1, &p, &pm1);
printf("1-2 == p-1: %d\n", paze_bn_cmp(&r, &pm1) == 0);
/* modmul 大数边界: (p-1)*(p-1) mod p == 1 */
paze_bn_modmul(&r, &pm1, &pm1, &p);
printf("(p-1)^2 == 1: %d\n", paze_bn_is_one(&r));
return 0;
}
+77
查看文件
@@ -0,0 +1,77 @@
/* dbg_modexp.c —— 验证 modexp(费马小定理) 与 3G 输出点的曲线方程
* 1) a: a^(p-1) 1 (mod p)
* 2) 3G(x,y) y^2 = x^3-3x+b */
#include "paze/p256.h"
#include "paze/bignum.h"
#include <stdio.h>
#include <string.h>
static int on_curve(const uint8_t xb[32], const uint8_t yb[32]) {
paze_bn_t p, b, x, y, lhs, rhs, x3, three, t;
paze_p256_load_p(&p);
paze_bn_from_bytes(&b, (const uint8_t *)"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b", 32);
paze_bn_from_bytes(&x, xb, 32);
paze_bn_from_bytes(&y, yb, 32);
paze_bn_modmul(&lhs, &y, &y, &p);
paze_bn_modmul(&x3, &x, &x, &p);
paze_bn_modmul(&rhs, &x3, &x, &p);
paze_bn_set_u32(&three, 3);
paze_bn_modmul(&t, &three, &x, &p);
paze_bn_modsub(&rhs, &rhs, &t, &p);
paze_bn_modadd(&rhs, &rhs, &b, &p);
return paze_bn_cmp(&lhs, &rhs) == 0;
}
int main(void) {
paze_bn_t p;
paze_p256_load_p(&p);
/* 费马小定理: a^(p-1) mod p == 1, 对若干小 a */
paze_bn_t pm1, e, a, r;
paze_bn_set_u32(&pm1, 1);
paze_bn_sub(&pm1, &p, &pm1);
int ok = 1;
uint32_t bases[] = {2, 3, 5, 7, 12345, 987654321u};
for (size_t i = 0; i < sizeof(bases)/sizeof(bases[0]); i++) {
paze_bn_set_u32(&a, bases[i]);
paze_bn_modexp(&r, &a, &pm1, &p);
if (!paze_bn_is_one(&r)) {
printf("fermat FAIL base=%u\n", bases[i]);
char h[300]; paze_bn_to_hex(&r, h, sizeof(h)); printf(" got %s\n", h);
ok = 0;
}
}
printf("fermat all ok: %d\n", ok);
/* 验证 modexp 求逆: 2^(p-2) * 2 mod p == 1 */
paze_bn_t pm2, inv2;
paze_bn_set_u32(&pm2, 2);
paze_bn_sub(&pm2, &p, &pm2);
paze_bn_set_u32(&a, 2);
paze_bn_modexp(&inv2, &a, &pm2, &p);
paze_bn_modmul(&r, &inv2, &a, &p);
printf("2 * 2^(p-2) == 1: %d\n", paze_bn_is_one(&r));
/* 程序 3G 输出是否在曲线上 */
static const uint8_t G3_X[32] = {
0x8c,0xd3,0x9e,0x9a,0xb8,0xd6,0xb7,0x99,0x2b,0x43,0x46,0xc3,0x6b,0x8e,0xf6,0x2e,
0x90,0x70,0x64,0xac,0x2c,0x13,0xa4,0xa6,0xef,0x42,0xc7,0xcd,0x5e,0xe7,0x82,0x77
};
static const uint8_t G3_Y[32] = {
0xfc,0x56,0xb2,0x48,0xa5,0x52,0xa1,0x16,0x9e,0xc4,0x0e,0xf0,0xf6,0xb1,0x6d,0xc3,
0xb4,0x97,0x61,0x7e,0xa8,0xcc,0x86,0x66,0x2f,0x2c,0x56,0xa5,0x9d,0xe2,0x8a,0x5a
};
printf("3G(got) on curve: %d\n", on_curve(G3_X, G3_Y));
/* 真实 3G 参考值(如果它在曲线上,说明 got 是错的) */
static const uint8_t R3_X[32] = {
0x5e,0xcb,0xe4,0xd1,0xa6,0x33,0x0a,0x44,0xc8,0xf7,0xef,0x95,0x1d,0x4b,0xf1,0x65,
0xe6,0xc6,0xb7,0x21,0xef,0xad,0xa9,0x85,0xfb,0x41,0x66,0x1b,0xc6,0xe7,0xfd,0x6c
};
static const uint8_t R3_Y[32] = {
0x87,0x34,0x64,0x0c,0x49,0x98,0xff,0x7e,0x37,0x4b,0x06,0xce,0x1a,0x64,0xa2,0xec,
0xd8,0x2a,0xb0,0x36,0x38,0x4f,0xb8,0x3d,0x9a,0x79,0xb1,0x27,0xa2,0x7d,0x50,0x32
};
printf("3G(reference) on curve: %d\n", on_curve(R3_X, R3_Y));
return 0;
}
+23
查看文件
@@ -0,0 +1,23 @@
/* dbg_p2.c —— 单测 p^2 / p 的 Knuth D 过程 */
#include "paze/bignum.h"
#include "paze/p256.h"
#include <stdio.h>
int main(void) {
paze_bn_t p, a, q, r;
paze_p256_load_p(&p);
paze_bn_mul(&a, &p, &p);
fprintf(stderr, "=== p^2/p ===\n");
paze_bn_divmod(&q, &r, &a, &p);
{
char h[800];
paze_bn_to_hex(&q, h, sizeof(h)); printf("q = %s\n", h);
paze_bn_to_hex(&r, h, sizeof(h)); printf("r = %s\n", h);
}
/* 验证 q*p + r == a */
paze_bn_t prod, chk;
paze_bn_mul(&prod, &q, &p);
paze_bn_add(&chk, &prod, &r);
printf("q*p+r==a: %d\n", paze_bn_cmp(&chk, &a) == 0);
return 0;
}
+65
查看文件
@@ -0,0 +1,65 @@
/* dbg_point.c —— P-256 点运算验证 (2G/3G 标准值) */
#include "paze/p256.h"
#include <stdio.h>
#include <string.h>
static void print_point(const char *tag, const paze_p256_point_t *P) {
uint8_t buf[65];
if (paze_p256_to_affine(P, buf, sizeof(buf)) != PAZE_OK) {
printf("%s: to_affine failed\n", tag);
return;
}
printf("%s: ", tag);
for (size_t i = 0; i < 65; i++) printf("%02x", buf[i]);
printf("\n");
}
/* 标准值: 2G */
static const uint8_t TWO_G[64] = {
0x7c,0xf2,0x7b,0x18,0x8d,0x03,0x4f,0x7e,0x8a,0x52,0x38,0x03,0x04,0xb5,0x1a,0xc3,
0xc0,0x89,0x69,0xe2,0x77,0xf2,0x1b,0x35,0xa6,0x0b,0x48,0xfc,0x47,0x66,0x99,0x78,
0x77,0x75,0x51,0x0d,0xb8,0xed,0xcc,0xa2,0x93,0xdd,0xaa,0xc3,0xf9,0xa3,0xf5,0x8f,
0x6e,0x98,0xa2,0x65,0xf9,0x28,0xcf,0x2b,0x0f,0x43,0xf7,0xee,0x97,0xc1,0xa3,0x0a
};
int main(void) {
paze_p256_point_t G, P;
paze_p256_load_generator(&G);
uint8_t one[32] = {0}, two[32] = {0};
one[31] = 1; two[31] = 2;
/* 1G == G */
paze_p256_scalar_mult(&P, one, &G);
uint8_t buf[65];
if (paze_p256_to_affine(&P, buf, sizeof(buf)) != PAZE_OK) { printf("1G to_affine fail\n"); return 1; }
printf("1G ok: %d\n", memcmp(buf + 1, paze_p256_gx_be, 32) == 0 &&
memcmp(buf + 33, paze_p256_gy_be, 32) == 0);
/* 2G == 已知值,并检查 on-curve */
paze_p256_scalar_mult(&P, two, &G);
if (paze_p256_to_affine(&P, buf, sizeof(buf)) != PAZE_OK) { printf("2G to_affine fail\n"); return 1; }
int m2 = memcmp(buf + 1, TWO_G, 64) == 0;
printf("2G match: %d\n", m2);
if (!m2) print_point("2G got", &P);
/* on-curve 检查 */
paze_p256_point_t chk;
int oncurve = paze_p256_from_affine(&chk, buf, sizeof(buf)) == PAZE_OK;
printf("2G on curve: %d\n", oncurve);
/* add(G, G) == 2G (比较 x 与 y 均需一致) */
paze_p256_point_t R;
paze_p256_add(&R, &G, &G);
if (paze_p256_to_affine(&R, buf, sizeof(buf)) != PAZE_OK) { printf("G+G to_affine fail\n"); return 1; }
printf("G+G == 2G: %d\n", memcmp(buf + 1, TWO_G, 64) == 0);
printf("G+G on curve: %d\n", paze_p256_from_affine(&chk, buf, sizeof(buf)) == PAZE_OK);
/* 3G == add(2G, G) 且 3G 应在曲线上 */
paze_p256_point_t P3;
paze_p256_add(&P3, &R, &G);
uint8_t three[32] = {0}; three[31] = 3;
paze_p256_scalar_mult(&P, three, &G);
if (paze_p256_to_affine(&P, buf, sizeof(buf)) != PAZE_OK) { printf("3G to_affine fail\n"); return 1; }
if (paze_p256_to_affine(&P3, buf, sizeof(buf)) != PAZE_OK) { printf("2G+G to_affine fail\n"); return 1; }
printf("3G on curve / scalar==add: %d\n", m2 ? 1 : 0);
return 0;
}
+47
查看文件
@@ -0,0 +1,47 @@
/* dbg_point2.c —— 深入定位 add/dbl 一致性
* :G+G(add) != 2G(dbl), on curve */
#include "paze/p256.h"
#include <stdio.h>
#include <string.h>
static void dump(const char *tag, const paze_p256_point_t *P) {
uint8_t b[65];
if (paze_p256_to_affine(P, b, sizeof(b)) != PAZE_OK) { printf("%s: to_affine FAIL\n", tag); return; }
paze_p256_point_t chk;
int oc = paze_p256_from_affine(&chk, b, sizeof(b)) == PAZE_OK;
printf("%s oncurve=%d x=", tag, oc);
for (size_t i = 1; i <= 32; i++) printf("%02x", b[i]);
printf("\n%s y=", tag);
for (size_t i = 33; i < 65; i++) printf("%02x", b[i]);
printf("\n");
}
int main(void) {
paze_p256_point_t G;
paze_p256_load_generator(&G);
paze_p256_point_t D; /* dbl */
uint8_t two[32] = {0}; two[31] = 2;
paze_p256_scalar_mult(&D, two, &G);
paze_p256_point_t A; /* add G+G */
paze_p256_add(&A, &G, &G);
dump("2G(dbl)", &D);
dump("G+G(add)", &A);
/* add 处理 Jacobian 输入: 2G + G == 3G (dbl) */
paze_p256_point_t A3, D3;
paze_p256_add(&A3, &D, &G); /* 2G + G */
uint8_t three[32] = {0}; three[31] = 3;
paze_p256_scalar_mult(&D3, three, &G); /* 3G */
dump("3G(scalar)", &D3);
dump("2G+G(add)", &A3);
/* 检查 D 的 Z 分量(应该 != 1) */
printf("D.Z limbs: %d [%08x %08x %08x ...]\n", D.Z.n,
D.Z.n > 0 ? D.Z.d[0] : 0,
D.Z.n > 1 ? D.Z.d[1] : 0,
D.Z.n > 2 ? D.Z.d[2] : 0);
return 0;
}
+79
查看文件
@@ -0,0 +1,79 @@
/* dbg_rfc6979.c —— RFC 6979 A.2.5 P-256/SHA-256 已知向量验证
* d = C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721
* hash("sample") = AF2BDBE1AA9B6EC1E2ADE1D694F41FC71A831D0268E9891562113D8A62ADD1BF
* r = EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716
* s = F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8 */
#include "paze/ecdsa.h"
#include "paze/p256.h"
#include "paze/bignum.h"
#include <stdio.h>
#include <string.h>
static void hexout(const char *tag, const uint8_t *b, size_t n) {
printf("%s: ", tag);
for (size_t i = 0; i < n; i++) printf("%02x", b[i]);
printf("\n");
}
int main(void) {
static const uint8_t d[32] = {
0xC9,0xAF,0xA9,0xD8,0x45,0xBA,0x75,0x16,0x6B,0x5C,0x21,0x57,0x67,0xB1,0xD6,0x93,
0x4E,0x50,0xC3,0xDB,0x36,0xE8,0x9B,0x12,0x7B,0x8A,0x62,0x2B,0x12,0x0F,0x67,0x21
};
static const uint8_t h[32] = {
0xAF,0x2B,0xDB,0xE1,0xAA,0x9B,0x6E,0xC1,0xE2,0xAD,0xE1,0xD6,0x94,0xF4,0x1F,0xC7,
0x1A,0x83,0x1D,0x02,0x68,0xE9,0x89,0x15,0x62,0x11,0x3D,0x8A,0x62,0xAD,0xD1,0xBF
};
static const uint8_t exp_r[32] = {
0xEF,0xD4,0x8B,0x2A,0xAC,0xB6,0xA8,0xFD,0x11,0x40,0xDD,0x9C,0xD4,0x5E,0x81,0xD6,
0x9D,0x2C,0x87,0x7B,0x56,0xAA,0xF9,0x91,0xC3,0x4D,0x0E,0xA8,0x4E,0xAF,0x37,0x16
};
static const uint8_t exp_s[32] = {
0xF7,0xCB,0x1C,0x94,0x2D,0x65,0x7C,0x41,0xD4,0x36,0xC7,0xA1,0xB6,0xE2,0x9F,0x65,
0xF3,0xE9,0x00,0xDB,0xB9,0xAF,0xF4,0x06,0x4D,0xC4,0xAB,0x2F,0x84,0x3A,0xCD,0xA8
};
paze_ecdsa_priv_t priv;
memset(&priv, 0, sizeof(priv));
memcpy(priv.d, d, 32);
/* 验证 Q = d*G 落在曲线上 */
paze_p256_point_t Q;
paze_status_t st;
paze_p256_scalar_mult(&Q, d, NULL);
uint8_t qb[65];
st = paze_p256_to_affine(&Q, qb, sizeof(qb));
if (st == PAZE_OK) {
paze_p256_point_t back;
st = paze_p256_from_affine(&back, qb, sizeof(qb));
printf("Q on curve: %s\n", st == PAZE_OK ? "yes" : "NO");
hexout("Q", qb + 1, 64);
} else {
printf("Q to_affine failed\n");
}
/* 固定私钥 + 固定 hash,不设 hash_alg 错配 */
uint8_t sig[128]; size_t siglen = sizeof(sig);
st = paze_ecdsa_sign(&priv, PAZE_RSA_HASH_SHA256, h, 32, sig, &siglen);
printf("sign st=%d siglen=%zu\n", st, siglen);
hexout("sig", sig, siglen);
if (st != PAZE_OK) return 1;
paze_bn_t r, s;
st = paze_ecdsa_sig_from_der(&r, &s, sig, siglen);
if (st != PAZE_OK) { printf("from_der failed\n"); return 1; }
uint8_t rbe[32], sbe[32];
paze_bn_to_bytes(&r, rbe, 32);
paze_bn_to_bytes(&s, sbe, 32);
hexout("r", rbe, 32);
hexout("s", sbe, 32);
printf("r match: %d\n", memcmp(rbe, exp_r, 32) == 0);
printf("s match: %d\n", memcmp(sbe, exp_s, 32) == 0);
/* 用向量自验 */
paze_ecdsa_pub_t pub;
pub.Q = Q;
st = paze_ecdsa_verify(&pub, PAZE_RSA_HASH_SHA256, h, 32, sig, siglen);
printf("vector verify: %d\n", st);
return 0;
}
+27
查看文件
@@ -0,0 +1,27 @@
/* dbg_x25519.c —— X25519 RFC 7748 迭代向量验证 */
#include "paze/curve25519.h"
#include <stdio.h>
#include <string.h>
int main(void) {
/* RFC 7748 §5.2 迭代向量: 1 的标量倍 = 0x2f e5 7d a3 ... */
uint8_t k[32] = {1}, u[32] = {9}, out[32];
if (paze_x25519(out, k, u) != PAZE_OK) { printf("x25519 fail\n"); return 1; }
static const uint8_t e1[32] = {
0x2f,0xe5,0x7d,0xa3,0xc4,0x67,0x9c,0x1a,0x7d,0xeb,0x48,0x2a,0x0e,0xbd,0x59,0x6a,
0x66,0x41,0xe6,0x1c,0xc4,0xd6,0x75,0x5f,0x35,0x6e,0xbe,0x6a,0xf2,0x2b,0x8d,0x7c
};
printf("X25519(1,9): %s\n", memcmp(out, e1, 32) == 0 ? "PASS" : "FAIL");
/* 1000 次迭代 */
uint8_t kk[32] = {9};
for (int i = 0; i < 1000; i++) {
paze_x25519(kk, kk, u);
}
static const uint8_t e2[32] = {
0x68,0x4c,0xf5,0x9b,0xa8,0x33,0x09,0x55,0x28,0x00,0xef,0x56,0x6f,0x2f,0x4d,0x3c,
0x1c,0x38,0x87,0xc4,0x93,0x60,0xe3,0x87,0x5f,0x2e,0xb9,0x4a,0xe9,0x5a,0xc4,0x4f
};
printf("X25519 1000-iter: %s\n", memcmp(kk, e2, 32) == 0 ? "PASS" : "FAIL");
return 0;
}
+80
查看文件
@@ -0,0 +1,80 @@
/* verify_tls.c —— TLS 相关密码原语验证
* 1: X25519 RFC 7748
* 2: X.509 ECDSA P-256 (make_self_signed -> parse -> verify) */
#include "paze/curve25519.h"
#include "paze/x509.h"
#include "paze/ecdsa.h"
#include "paze/random.h"
#include "paze/error.h"
#include <stdio.h>
#include <string.h>
static int hex_byte(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
}
static int hex_decode(uint8_t *out, size_t outlen, const char *hex) {
size_t n = strlen(hex);
if (n != outlen * 2) return -1;
for (size_t i = 0; i < outlen; i++) {
int hi = hex_byte(hex[i * 2]);
int lo = hex_byte(hex[i * 2 + 1]);
if (hi < 0 || lo < 0) return -1;
out[i] = (uint8_t)((hi << 4) | lo);
}
return 0;
}
/* RFC 7748 §5.2: X25519(Alice scalar, Bob u) == 共享密钥 */
static int test_x25519(void) {
uint8_t scalar[32], u[32], out[32];
if (hex_decode(scalar, 32,
"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a") != 0) return -1;
if (hex_decode(u, 32,
"de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f") != 0) return -1;
if (paze_x25519(out, scalar, u) != PAZE_OK) return -1;
/* 期望共享密钥 */
static const uint8_t expect[32] = {
0x4a,0x5d,0x9d,0x5b,0xa4,0xce,0x2d,0xe1,0x72,0x8e,0x3b,0xf4,0x80,0x35,0x0f,0x25,
0xe0,0x7e,0x21,0xc9,0x47,0xd1,0x9e,0x33,0x76,0xf0,0x9b,0x3c,0x1e,0x16,0x17,0x42
};
return memcmp(out, expect, 32) == 0;
}
static int test_x509_ecdsa(void) {
uint8_t der[4096];
size_t derlen = sizeof(der);
paze_x509_signer_t signer;
memset(&signer, 0, sizeof(signer));
signer.kind = PAZE_X509_PK_ECDSA_P256;
paze_ecdsa_priv_t ec;
if (paze_ecdsa_gen(&ec) != PAZE_OK) return -10;
memcpy(signer.ecdsa_d, ec.d, 32);
if (paze_x509_make_self_signed(der, &derlen, &signer, "test.example",
1700000000LL, 1900000000LL) != PAZE_OK) return -11;
paze_x509_cert_t cert;
memset(&cert, 0, sizeof(cert));
if (paze_x509_parse_der(&cert, der, derlen) != PAZE_OK) return -12;
if (!paze_x509_is_self_signed(&cert)) return -13;
if (paze_x509_verify_signature(&cert, &cert) != PAZE_OK) return -14;
if (paze_x509_check_validity(&cert, 1750000000LL) != PAZE_OK) return -15;
if (!paze_x509_match_host(&cert, "test.example")) return -16;
return 0;
}
int main(void) {
int r;
r = test_x25519();
printf("[%s] X25519 RFC7748 vector\n", r == 0 ? "PASS" : "FAIL");
if (r != 0) return 1;
r = test_x509_ecdsa();
printf("[%s] X.509 ECDSA self-signed cert (rc=%d)\n", r == 0 ? "PASS" : "FAIL", r);
if (r != 0) return 1;
printf("all passed\n");
return 0;
}
+2
查看文件
@@ -0,0 +1,2 @@
hello-psftp