|
|
|
@ -404,6 +404,144 @@ static int test8_srv_recv_close(void) { |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ======================= raw TCP client helpers =======================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Подключается к 127.0.0.1:port, отправляет len байт, оставляет сокет открытым.
|
|
|
|
|
|
|
|
// Возвращает открытый socket (SOCKET_INVALID при ошибке).
|
|
|
|
|
|
|
|
static socket_t raw_connect_send(uint16_t port, const uint8_t *data, size_t len) { |
|
|
|
|
|
|
|
socket_t s = socket(AF_INET, SOCK_STREAM, 0); |
|
|
|
|
|
|
|
if (s == SOCKET_INVALID) return SOCKET_INVALID; |
|
|
|
|
|
|
|
struct sockaddr_in addr; |
|
|
|
|
|
|
|
memset(&addr, 0, sizeof(addr)); |
|
|
|
|
|
|
|
addr.sin_family = AF_INET; |
|
|
|
|
|
|
|
addr.sin_port = htons(port); |
|
|
|
|
|
|
|
if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { socket_close_wrapper(s); return SOCKET_INVALID; } |
|
|
|
|
|
|
|
if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { socket_close_wrapper(s); return SOCKET_INVALID; } |
|
|
|
|
|
|
|
size_t off = 0; |
|
|
|
|
|
|
|
while (off < len) { |
|
|
|
|
|
|
|
int n = (int)send(s, data + off, len - off, 0); |
|
|
|
|
|
|
|
if (n <= 0) { socket_close_wrapper(s); return SOCKET_INVALID; } |
|
|
|
|
|
|
|
off += (size_t)n; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return s; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Собирает криптографически валидный client handshake (91 байт):
|
|
|
|
|
|
|
|
// salt(8) + obfuscated_client_pubkey(32) + зашифрованный hs(51) с заданным padding_size.
|
|
|
|
|
|
|
|
// Клиентская сторона: X25519(c_keys.priv, s_keys.pub), stream STCP_STREAM_CLIENT_SEND.
|
|
|
|
|
|
|
|
static int build_malicious_handshake(uint16_t padding_size, uint8_t *out) { |
|
|
|
|
|
|
|
uint8_t salt[SC_PUBKEY_ENC_SALT_SIZE]; |
|
|
|
|
|
|
|
memset(salt, 0x5A, sizeof(salt)); |
|
|
|
|
|
|
|
memcpy(out, salt, SC_PUBKEY_ENC_SALT_SIZE); |
|
|
|
|
|
|
|
if (sc_obfuscate_pubkey(salt, s_keys.public_key, c_keys.public_key, |
|
|
|
|
|
|
|
out + SC_PUBKEY_ENC_SALT_SIZE) != SC_OK) return -1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t plain[STCP_HS_PLAIN_SIZE]; |
|
|
|
|
|
|
|
memset(plain, 0, sizeof(plain)); |
|
|
|
|
|
|
|
plain[41] = (uint8_t)(padding_size & 0xff); |
|
|
|
|
|
|
|
plain[42] = (uint8_t)(padding_size >> 8); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t enc[STCP_HS_ENC_CLIENT]; |
|
|
|
|
|
|
|
memcpy(enc, plain, STCP_HS_PLAIN_SIZE); |
|
|
|
|
|
|
|
uint32_t crc = crc32_calc(plain, STCP_HS_PLAIN_SIZE); |
|
|
|
|
|
|
|
enc[47] = (uint8_t)(crc >> 0); enc[48] = (uint8_t)(crc >> 8); |
|
|
|
|
|
|
|
enc[49] = (uint8_t)(crc >> 16); enc[50] = (uint8_t)(crc >> 24); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct secure_channel sc; |
|
|
|
|
|
|
|
sc_init_ctx(&sc, &c_keys); |
|
|
|
|
|
|
|
if (sc_set_peer_public_key(&sc, s_keys.public_key, SC_PEER_PUBKEY_BIN) != SC_OK) return -1; |
|
|
|
|
|
|
|
struct sc_stream_state ss; |
|
|
|
|
|
|
|
if (sc_stream_init(&sc, &ss, STCP_STREAM_CLIENT_SEND) != SC_OK) return -1; |
|
|
|
|
|
|
|
int rc = sc_stream_xor(&ss, enc, STCP_HS_ENC_CLIENT); |
|
|
|
|
|
|
|
sc_stream_cleanup(&ss); |
|
|
|
|
|
|
|
if (rc != SC_OK) return -1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
memcpy(out + SC_PUBKEY_ENC_SIZE, enc, STCP_HS_ENC_CLIENT); |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ======================= test 9: контроль — raw handshake с padding=8 принимается =======================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int test9_raw_handshake_ok(void) { |
|
|
|
|
|
|
|
struct UASYNC *ua = uasync_create(); TASSERT(ua); |
|
|
|
|
|
|
|
struct test_peer srv = {0}; |
|
|
|
|
|
|
|
uint16_t port = BASE_PORT + 9; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, NULL, NULL, server_connect_cb, &srv, peer_close_cb, &srv, AF_INET); TASSERT(ss); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t buf[SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_CLIENT + 8]; |
|
|
|
|
|
|
|
TASSERT(build_malicious_handshake(8, buf) == 0); |
|
|
|
|
|
|
|
memset(buf + SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_CLIENT, 0, 8); // 8 байт паддинга (как легитимный клиент)
|
|
|
|
|
|
|
|
socket_t s = raw_connect_send(port, buf, sizeof(buf)); |
|
|
|
|
|
|
|
TASSERT(s != SOCKET_INVALID); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int ticks = 0; |
|
|
|
|
|
|
|
while (!srv.ready && !srv.closed && ticks < 200) { uasync_poll(ua, 10); ticks++; } |
|
|
|
|
|
|
|
TASSERT(srv.ready); |
|
|
|
|
|
|
|
TASSERT(!srv.closed); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
socket_close_wrapper(s); |
|
|
|
|
|
|
|
peer_cleanup(&srv); |
|
|
|
|
|
|
|
if (srv.conn) stcp_conn_free(srv.conn); |
|
|
|
|
|
|
|
stcp_server_destroy(ss); |
|
|
|
|
|
|
|
uasync_destroy(ua, 1); |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ======================= test 10: oversized padding отклоняется (clamp) =======================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int test10_oversized_padding(void) { |
|
|
|
|
|
|
|
struct UASYNC *ua = uasync_create(); TASSERT(ua); |
|
|
|
|
|
|
|
struct test_peer srv = {0}; |
|
|
|
|
|
|
|
uint16_t port = BASE_PORT + 10; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, NULL, NULL, server_connect_cb, &srv, peer_close_cb, &srv, AF_INET); TASSERT(ss); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t buf[SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_CLIENT]; |
|
|
|
|
|
|
|
TASSERT(build_malicious_handshake(0xFFFF, buf) == 0); |
|
|
|
|
|
|
|
socket_t s = raw_connect_send(port, buf, sizeof(buf)); |
|
|
|
|
|
|
|
TASSERT(s != SOCKET_INVALID); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int ticks = 0; |
|
|
|
|
|
|
|
while (!srv.closed && ticks < 200) { uasync_poll(ua, 10); ticks++; } |
|
|
|
|
|
|
|
TASSERT(srv.closed); |
|
|
|
|
|
|
|
TASSERT(!srv.ready); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
socket_close_wrapper(s); |
|
|
|
|
|
|
|
peer_cleanup(&srv); |
|
|
|
|
|
|
|
srv.conn = NULL; // уже освобождён deferred-close (allocated=1)
|
|
|
|
|
|
|
|
stcp_server_destroy(ss); |
|
|
|
|
|
|
|
uasync_destroy(ua, 1); |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ======================= test 11: мусор в handshake не валит сервер =======================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int test11_garbage_handshake(void) { |
|
|
|
|
|
|
|
struct UASYNC *ua = uasync_create(); TASSERT(ua); |
|
|
|
|
|
|
|
struct test_peer srv = {0}; |
|
|
|
|
|
|
|
uint16_t port = BASE_PORT + 11; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, NULL, NULL, server_connect_cb, &srv, peer_close_cb, &srv, AF_INET); TASSERT(ss); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t buf[SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_CLIENT]; |
|
|
|
|
|
|
|
for (int i = 0; i < (int)sizeof(buf); i++) buf[i] = (uint8_t)(i * 31 + 7); |
|
|
|
|
|
|
|
socket_t s = raw_connect_send(port, buf, sizeof(buf)); |
|
|
|
|
|
|
|
TASSERT(s != SOCKET_INVALID); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int ticks = 0; |
|
|
|
|
|
|
|
while (!srv.closed && ticks < 200) { uasync_poll(ua, 10); ticks++; } |
|
|
|
|
|
|
|
TASSERT(srv.closed); |
|
|
|
|
|
|
|
TASSERT(!srv.ready); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
socket_close_wrapper(s); |
|
|
|
|
|
|
|
peer_cleanup(&srv); |
|
|
|
|
|
|
|
srv.conn = NULL; |
|
|
|
|
|
|
|
stcp_server_destroy(ss); |
|
|
|
|
|
|
|
uasync_destroy(ua, 1); |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ======================= main =======================
|
|
|
|
// ======================= main =======================
|
|
|
|
|
|
|
|
|
|
|
|
int main(void) { |
|
|
|
int main(void) { |
|
|
|
@ -425,6 +563,9 @@ int main(void) { |
|
|
|
TRUN(test6_interleaved); |
|
|
|
TRUN(test6_interleaved); |
|
|
|
TRUN(test7_bulk_4mb); |
|
|
|
TRUN(test7_bulk_4mb); |
|
|
|
TRUN(test8_srv_recv_close); |
|
|
|
TRUN(test8_srv_recv_close); |
|
|
|
|
|
|
|
TRUN(test9_raw_handshake_ok); |
|
|
|
|
|
|
|
TRUN(test10_oversized_padding); |
|
|
|
|
|
|
|
TRUN(test11_garbage_handshake); |
|
|
|
|
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "============================================"); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "============================================"); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "Results: %d/%d passed", tests_passed, tests_total); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "Results: %d/%d passed", tests_passed, tests_total); |
|
|
|
|