stcp: streaming TCP module + stream cipher + comprehensive tests
- secure_channel: add sc_stream_state + sc_stream_init/sc_stream_xor/sc_stream_cleanup
AES-128-CTR streaming cipher for incremental encrypt/decrypt
- stcp.h/c: shared stcp_conn lifecycle, set_on_close, rx/tx queue setup
- stcp_server: listen/accept, handshake (obfuscated pubkey + ECDH + stream), DATA phase
- stcp_client: connect, handshake, DATA phase
- Protocol: key_block(72) + enc(data+crc) + padding for handshake
size(2 LE) + enc(data+crc) for data messages
- Tests: test_stream_cipher (AES-CTR correctness)
test_stcp (6 cases: sizes 0..65535, 200 msgs, wrong key,
close detection, multi-client, interleaved send/recv)
- Fix: 0-length message handling in tx_queue callbacks
This commit is contained in:
1 parent
0dd942be63
commit
f913bdf351
12 files changed
+1433
No files matched your search
@@ -4,6 +4,8 @@
|
||||
check_PROGRAMS = \
|
||||
test_ll_queue \
|
||||
test_serialize \
|
||||
test_stream_cipher \
|
||||
test_stcp \
|
||||
test_packet_dump \
|
||||
test_debug_categories \
|
||||
test_config_debug \
|
||||
@@ -176,6 +178,14 @@ test_etcp_crypto_SOURCES = test_etcp_crypto.c
|
||||
test_etcp_crypto_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
|
||||
test_etcp_crypto_LDADD = $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||
|
||||
test_stream_cipher_SOURCES = test_stream_cipher.c
|
||||
test_stream_cipher_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
|
||||
test_stream_cipher_LDADD = $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||
|
||||
test_stcp_SOURCES = test_stcp.c
|
||||
test_stcp_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
|
||||
test_stcp_LDADD = $(top_builddir)/src/utun-stcp.o $(top_builddir)/src/utun-stcp_server.o $(top_builddir)/src/utun-stcp_client.o $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||
|
||||
if USE_OPENSSL
|
||||
else
|
||||
test_crypto_SOURCES = test_crypto.c
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
// test_stcp.c — comprehensive STCP integration tests
|
||||
#include "../src/stcp.h"
|
||||
#include "../src/stcp_server.h"
|
||||
#include "../src/stcp_client.h"
|
||||
#include "../src/secure_channel.h"
|
||||
#include "../lib/u_async.h"
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static int tests_passed = 0, tests_total = 0;
|
||||
static struct SC_MYKEYS s_keys, c_keys;
|
||||
|
||||
#define BASE_PORT 23456
|
||||
|
||||
#define TASSERT(cond) do { \
|
||||
if (!(cond)) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, " FAIL: %s", #cond); return 1; } \
|
||||
} while(0)
|
||||
|
||||
#define TRUN(name) do { \
|
||||
tests_total++; \
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "--- %s ---", name); \
|
||||
int _r = name(); \
|
||||
if (_r == 0) { tests_passed++; DEBUG_INFO(DEBUG_CATEGORY_GENERAL, " PASS"); } \
|
||||
} while(0)
|
||||
|
||||
// ======================= peer helper =======================
|
||||
|
||||
struct test_peer {
|
||||
struct stcp_conn *conn;
|
||||
struct ll_queue *rx, *tx;
|
||||
int ready, closed, close_err, msg_count;
|
||||
uint8_t *accum;
|
||||
size_t accum_len, accum_cap;
|
||||
};
|
||||
|
||||
static void peer_rx_cb(struct ll_queue *q, void *arg) {
|
||||
struct test_peer *p = (struct test_peer *)arg;
|
||||
struct ll_entry *e = queue_data_get(q);
|
||||
if (!e) { queue_resume_callback(q); return; }
|
||||
p->msg_count++;
|
||||
size_t need = p->accum_len + e->len;
|
||||
if (need > p->accum_cap) { p->accum_cap = need + 4096; p->accum = u_realloc(p->accum, p->accum_cap); }
|
||||
memcpy(p->accum + p->accum_len, e->dgram, e->len);
|
||||
p->accum_len += e->len;
|
||||
queue_entry_free(e);
|
||||
queue_resume_callback(q);
|
||||
}
|
||||
|
||||
static void peer_close_cb(struct stcp_conn *conn, int err, void *arg) {
|
||||
(void)conn;
|
||||
struct test_peer *p = (struct test_peer *)arg;
|
||||
p->closed = 1; p->close_err = err;
|
||||
}
|
||||
|
||||
static void setup_peer(struct test_peer *p, struct stcp_conn *conn) {
|
||||
p->conn = conn;
|
||||
p->rx = queue_new(conn->ua, 0, 0, 0, "rx");
|
||||
p->tx = queue_new(conn->ua, 0, 0, 0, "tx");
|
||||
queue_set_callback(p->rx, peer_rx_cb, p);
|
||||
stcp_conn_set_rx_queue(conn, p->rx);
|
||||
stcp_conn_set_tx_queue(conn, p->tx);
|
||||
stcp_conn_set_on_close(conn, peer_close_cb, p);
|
||||
p->ready = 1;
|
||||
}
|
||||
|
||||
static void server_connect_cb(struct stcp_conn *conn, void *arg) {
|
||||
struct test_peer *p = (struct test_peer *)arg;
|
||||
setup_peer(p, conn);
|
||||
}
|
||||
|
||||
static void client_ready_cb(struct stcp_conn *conn, void *arg) {
|
||||
struct test_peer *p = (struct test_peer *)arg;
|
||||
setup_peer(p, conn);
|
||||
}
|
||||
|
||||
static void peer_cleanup(struct test_peer *p) {
|
||||
if (p->rx) { queue_free(p->rx); p->rx = NULL; }
|
||||
if (p->tx) { queue_free(p->tx); p->tx = NULL; }
|
||||
if (p->accum) { u_free(p->accum); p->accum = NULL; }
|
||||
}
|
||||
|
||||
static int peer_send(struct test_peer *p, const uint8_t *data, size_t len) {
|
||||
struct ll_entry *e = queue_entry_new(0);
|
||||
if (!e) return -1;
|
||||
e->dgram = u_malloc(len ? len : 1);
|
||||
if (!e->dgram) { queue_entry_free(e); return -1; }
|
||||
if (len) memcpy(e->dgram, data, len);
|
||||
e->len = (uint16_t)len;
|
||||
queue_data_put(p->tx, e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ======================= test 1: handshake + all message sizes =======================
|
||||
|
||||
static int test1_sizes(void) {
|
||||
struct UASYNC *ua = uasync_create(); TASSERT(ua);
|
||||
struct test_peer srv = {0}, cli = {0};
|
||||
uint16_t port = BASE_PORT + 1;
|
||||
|
||||
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv); TASSERT(ss);
|
||||
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli); TASSERT(sc);
|
||||
|
||||
size_t sizes[] = {0, 1, 16, 17, 255, 256, 1000, 65535};
|
||||
int n_sizes = 8;
|
||||
size_t total = 0; for (int i = 0; i < n_sizes; i++) total += sizes[i];
|
||||
uint8_t *payload = u_malloc(65536);
|
||||
for (int i = 0; i < 65536; i++) payload[i] = (uint8_t)(i * 7 + 13);
|
||||
|
||||
int sent = 0, ticks = 0;
|
||||
while (srv.msg_count < n_sizes && ticks < 5000) {
|
||||
uasync_poll(ua, 10);
|
||||
if (srv.ready && cli.ready && !sent) {
|
||||
for (int i = 0; i < n_sizes; i++) TASSERT(peer_send(&cli, payload, sizes[i]) == 0);
|
||||
sent = 1;
|
||||
}
|
||||
ticks++;
|
||||
}
|
||||
TASSERT(srv.msg_count == n_sizes);
|
||||
TASSERT(srv.accum_len == total);
|
||||
size_t off = 0;
|
||||
for (int i = 0; i < n_sizes; i++) {
|
||||
TASSERT(memcmp(srv.accum + off, payload, sizes[i]) == 0);
|
||||
off += sizes[i];
|
||||
}
|
||||
u_free(payload);
|
||||
peer_cleanup(&srv); peer_cleanup(&cli);
|
||||
if (srv.conn) stcp_conn_free(srv.conn);
|
||||
stcp_client_destroy(sc); stcp_server_destroy(ss);
|
||||
uasync_destroy(ua, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ======================= test 2: many sequential messages =======================
|
||||
|
||||
static int test2_many(void) {
|
||||
struct UASYNC *ua = uasync_create(); TASSERT(ua);
|
||||
struct test_peer srv = {0}, cli = {0};
|
||||
uint16_t port = BASE_PORT + 2;
|
||||
|
||||
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv); TASSERT(ss);
|
||||
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli); TASSERT(sc);
|
||||
|
||||
int sent = 0, ticks = 0;
|
||||
while (srv.msg_count < 200 && ticks < 5000) {
|
||||
uasync_poll(ua, 10);
|
||||
if (srv.ready && cli.ready && !sent) {
|
||||
for (int i = 0; i < 200; i++) {
|
||||
uint8_t buf[8];
|
||||
buf[0] = (uint8_t)(i >> 0); buf[1] = (uint8_t)(i >> 8);
|
||||
buf[2] = (uint8_t)(i >> 16); buf[3] = (uint8_t)(i >> 24);
|
||||
buf[4] = (uint8_t)(i * 3);
|
||||
TASSERT(peer_send(&cli, buf, 5) == 0);
|
||||
}
|
||||
sent = 1;
|
||||
}
|
||||
ticks++;
|
||||
}
|
||||
TASSERT(srv.msg_count == 200);
|
||||
TASSERT(srv.accum_len == 200 * 5);
|
||||
for (int i = 0; i < 200; i++) {
|
||||
uint32_t v; memcpy(&v, srv.accum + i * 5, 4); TASSERT(v == (uint32_t)i);
|
||||
TASSERT(srv.accum[i * 5 + 4] == (uint8_t)(i * 3));
|
||||
}
|
||||
peer_cleanup(&srv); peer_cleanup(&cli);
|
||||
if (srv.conn) stcp_conn_free(srv.conn);
|
||||
stcp_client_destroy(sc); stcp_server_destroy(ss);
|
||||
uasync_destroy(ua, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ======================= test 3: wrong peer pubkey =======================
|
||||
|
||||
static int test3_wrong_key(void) {
|
||||
struct UASYNC *ua = uasync_create(); TASSERT(ua);
|
||||
struct test_peer srv = {0}, cli = {0};
|
||||
uint16_t port = BASE_PORT + 3;
|
||||
|
||||
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv); TASSERT(ss);
|
||||
|
||||
struct SC_MYKEYS rogue;
|
||||
TASSERT(sc_generate_keypair(&rogue) == SC_OK);
|
||||
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, rogue.public_key, client_ready_cb, &cli); TASSERT(sc);
|
||||
|
||||
int ticks = 0;
|
||||
while (ticks < 3000) {
|
||||
uasync_poll(ua, 10);
|
||||
if (cli.closed || srv.closed) break;
|
||||
ticks++;
|
||||
}
|
||||
TASSERT(!cli.ready);
|
||||
TASSERT(!srv.ready);
|
||||
|
||||
peer_cleanup(&srv); peer_cleanup(&cli);
|
||||
stcp_client_destroy(sc); stcp_server_destroy(ss);
|
||||
uasync_destroy(ua, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ======================= test 4: close detection =======================
|
||||
|
||||
static int test4_close(void) {
|
||||
struct UASYNC *ua = uasync_create(); TASSERT(ua);
|
||||
struct test_peer srv = {0}, cli = {0};
|
||||
uint16_t port = BASE_PORT + 4;
|
||||
|
||||
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv); TASSERT(ss);
|
||||
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli); TASSERT(sc);
|
||||
|
||||
int closed = 0, ticks = 0;
|
||||
while (!srv.closed && ticks < 3000) {
|
||||
uasync_poll(ua, 10);
|
||||
if (srv.ready && cli.ready && !closed) {
|
||||
uint8_t m = 0xAB; peer_send(&cli, &m, 1);
|
||||
closed = 1;
|
||||
}
|
||||
if (closed && srv.msg_count >= 1 && cli.conn) {
|
||||
stcp_conn_free(cli.conn); cli.conn = NULL;
|
||||
cli.closed = 1;
|
||||
}
|
||||
ticks++;
|
||||
}
|
||||
TASSERT(srv.closed);
|
||||
|
||||
peer_cleanup(&srv); peer_cleanup(&cli);
|
||||
stcp_client_destroy(sc); stcp_server_destroy(ss);
|
||||
uasync_destroy(ua, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ======================= test 5: multiple concurrent clients =======================
|
||||
|
||||
static struct test_peer *g_multi_peers;
|
||||
static int g_multi_idx, g_multi_max;
|
||||
|
||||
static void multi_connect_cb(struct stcp_conn *conn, void *arg) {
|
||||
(void)arg;
|
||||
int i = g_multi_idx++;
|
||||
if (i >= g_multi_max) return;
|
||||
struct test_peer *p = &g_multi_peers[i];
|
||||
setup_peer(p, conn);
|
||||
}
|
||||
|
||||
static int test5_multi(void) {
|
||||
struct UASYNC *ua = uasync_create(); TASSERT(ua);
|
||||
uint16_t port = BASE_PORT + 5;
|
||||
|
||||
#define NCLI 3
|
||||
struct test_peer srvp[NCLI];
|
||||
struct test_peer clip[NCLI];
|
||||
memset(srvp, 0, sizeof(srvp)); memset(clip, 0, sizeof(clip));
|
||||
|
||||
g_multi_peers = srvp; g_multi_idx = 0; g_multi_max = NCLI;
|
||||
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, multi_connect_cb, NULL); TASSERT(ss);
|
||||
|
||||
struct stcp_client *clients[NCLI] = {0};
|
||||
for (int i = 0; i < NCLI; i++) {
|
||||
clients[i] = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &clip[i]);
|
||||
TASSERT(clients[i]);
|
||||
}
|
||||
|
||||
int sent = 0, ticks = 0;
|
||||
while (ticks < 5000) {
|
||||
uasync_poll(ua, 10);
|
||||
if (!sent) {
|
||||
int all_ready = 1;
|
||||
for (int i = 0; i < NCLI; i++) if (!clip[i].ready || !srvp[i].ready) all_ready = 0;
|
||||
if (all_ready) {
|
||||
for (int i = 0; i < NCLI; i++) {
|
||||
uint8_t buf[4]; buf[0] = (uint8_t)i; buf[1] = (uint8_t)(i * 17 + 42);
|
||||
TASSERT(peer_send(&clip[i], buf, 2) == 0);
|
||||
}
|
||||
sent = 1;
|
||||
}
|
||||
}
|
||||
if (sent) {
|
||||
int all_got = 1;
|
||||
for (int i = 0; i < NCLI; i++) if (srvp[i].msg_count < 1) all_got = 0;
|
||||
if (all_got) break;
|
||||
}
|
||||
ticks++;
|
||||
}
|
||||
for (int i = 0; i < NCLI; i++) {
|
||||
TASSERT(srvp[i].msg_count >= 1);
|
||||
TASSERT(srvp[i].accum_len == 2);
|
||||
TASSERT(srvp[i].accum[0] == (uint8_t)i);
|
||||
TASSERT(srvp[i].accum[1] == (uint8_t)(i * 17 + 42));
|
||||
}
|
||||
for (int i = 0; i < NCLI; i++) {
|
||||
peer_cleanup(&srvp[i]); peer_cleanup(&clip[i]);
|
||||
if (srvp[i].conn) stcp_conn_free(srvp[i].conn);
|
||||
stcp_client_destroy(clients[i]);
|
||||
}
|
||||
stcp_server_destroy(ss);
|
||||
uasync_destroy(ua, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ======================= test 6: interleaved send/recv =======================
|
||||
|
||||
static int test6_interleaved(void) {
|
||||
struct UASYNC *ua = uasync_create(); TASSERT(ua);
|
||||
struct test_peer srv = {0}, cli = {0};
|
||||
uint16_t port = BASE_PORT + 6;
|
||||
|
||||
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, server_connect_cb, &srv); TASSERT(ss);
|
||||
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, client_ready_cb, &cli); TASSERT(sc);
|
||||
|
||||
int round = 0, ticks = 0;
|
||||
while (srv.msg_count < 50 || cli.msg_count < 50) {
|
||||
uasync_poll(ua, 10);
|
||||
if (srv.ready && cli.ready && round < 50) {
|
||||
uint8_t cb = (uint8_t)(round + 100);
|
||||
uint8_t sb = (uint8_t)(round + 200);
|
||||
if (peer_send(&cli, &cb, 1) == 0 && peer_send(&srv, &sb, 1) == 0) round++;
|
||||
}
|
||||
if (++ticks > 5000) break;
|
||||
}
|
||||
TASSERT(srv.msg_count >= 50);
|
||||
TASSERT(cli.msg_count >= 50);
|
||||
for (int i = 0; i < 50; i++) {
|
||||
TASSERT(srv.accum[i] == (uint8_t)(i + 100));
|
||||
TASSERT(cli.accum[i] == (uint8_t)(i + 200));
|
||||
}
|
||||
|
||||
peer_cleanup(&srv); peer_cleanup(&cli);
|
||||
if (srv.conn) stcp_conn_free(srv.conn);
|
||||
stcp_client_destroy(sc); stcp_server_destroy(ss);
|
||||
uasync_destroy(ua, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ======================= main =======================
|
||||
|
||||
int main(void) {
|
||||
debug_config_init();
|
||||
debug_set_level(DEBUG_LEVEL_INFO);
|
||||
debug_set_categories(DEBUG_CATEGORY_GENERAL | DEBUG_CATEGORY_SOCKET | DEBUG_CATEGORY_CRYPTO);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "============================================");
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "=== STCP Integration Tests ===");
|
||||
|
||||
TASSERT(sc_generate_keypair(&s_keys) == SC_OK);
|
||||
TASSERT(sc_generate_keypair(&c_keys) == SC_OK);
|
||||
|
||||
TRUN(test1_sizes);
|
||||
TRUN(test2_many);
|
||||
TRUN(test3_wrong_key);
|
||||
TRUN(test4_close);
|
||||
TRUN(test5_multi);
|
||||
TRUN(test6_interleaved);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "============================================");
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "Results: %d/%d passed", tests_passed, tests_total);
|
||||
return tests_passed == tests_total ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
// test_stream_cipher.c — tests for streaming cipher (AES-128-CTR)
|
||||
#include "../src/secure_channel.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static int test_failed = 0;
|
||||
|
||||
#define CHECK(expr, msg) do { \
|
||||
if (!(expr)) { \
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "FAIL: %s", msg); \
|
||||
test_failed = 1; \
|
||||
} else { \
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "PASS: %s", msg); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
int main(void) {
|
||||
debug_config_init();
|
||||
debug_set_level(DEBUG_LEVEL_INFO);
|
||||
debug_set_categories(DEBUG_CATEGORY_CRYPTO);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "=== Stream Cipher Test ===");
|
||||
|
||||
struct SC_MYKEYS keys_a, keys_b;
|
||||
CHECK(sc_generate_keypair(&keys_a) == SC_OK, "generate keypair A");
|
||||
CHECK(sc_generate_keypair(&keys_b) == SC_OK, "generate keypair B");
|
||||
|
||||
sc_context_t ctx_a, ctx_b;
|
||||
CHECK(sc_init_ctx(&ctx_a, &keys_a) == SC_OK, "init ctx A");
|
||||
CHECK(sc_init_ctx(&ctx_b, &keys_b) == SC_OK, "init ctx B");
|
||||
|
||||
CHECK(sc_set_peer_public_key(&ctx_a, keys_b.public_key, SC_PEER_PUBKEY_BIN) == SC_OK, "set peer key A<-B");
|
||||
CHECK(sc_set_peer_public_key(&ctx_b, keys_a.public_key, SC_PEER_PUBKEY_BIN) == SC_OK, "set peer key B<-A");
|
||||
|
||||
struct sc_stream_state send_a, recv_a, send_b, recv_b;
|
||||
CHECK(sc_stream_init(&ctx_a, &send_a, 0) == SC_OK, "init send_A (id=0)");
|
||||
CHECK(sc_stream_init(&ctx_a, &recv_a, 1) == SC_OK, "init recv_A (id=1)");
|
||||
CHECK(sc_stream_init(&ctx_b, &recv_b, 0) == SC_OK, "init recv_B (id=0)");
|
||||
CHECK(sc_stream_init(&ctx_b, &send_b, 1) == SC_OK, "init send_B (id=1)");
|
||||
|
||||
uint8_t original[2048], data[2048];
|
||||
for (int i = 0; i < (int)sizeof(original); i++) original[i] = (uint8_t)(i * 3 + 17);
|
||||
|
||||
// Test 1: A->B, single chunk, 1024 bytes
|
||||
memcpy(data, original, 1024);
|
||||
CHECK(sc_stream_xor(&send_a, data, 1024) == SC_OK, "encrypt 1024B A->B");
|
||||
CHECK(sc_stream_xor(&recv_b, data, 1024) == SC_OK, "decrypt 1024B A->B");
|
||||
CHECK(memcmp(data, original, 1024) == 0, "round-trip A->B matches");
|
||||
|
||||
// Test 2: B->A, single chunk
|
||||
memcpy(data, original, 1024);
|
||||
CHECK(sc_stream_xor(&send_b, data, 1024) == SC_OK, "encrypt 1024B B->A");
|
||||
CHECK(sc_stream_xor(&recv_a, data, 1024) == SC_OK, "decrypt 1024B B->A");
|
||||
CHECK(memcmp(data, original, 1024) == 0, "round-trip B->A matches");
|
||||
|
||||
// Test 3: incremental feeding — encrypt in 4 chunks, decrypt in 2
|
||||
memcpy(data, original, 1024);
|
||||
CHECK(sc_stream_xor(&send_a, data, 7) == SC_OK, "inc_A enc 7B");
|
||||
CHECK(sc_stream_xor(&send_a, data + 7, 33) == SC_OK, "inc_A enc 33B");
|
||||
CHECK(sc_stream_xor(&send_a, data + 40, 1) == SC_OK, "inc_A enc 1B");
|
||||
CHECK(sc_stream_xor(&send_a, data + 41, 1024 - 41) == SC_OK, "inc_A enc 983B");
|
||||
CHECK(sc_stream_xor(&recv_b, data, 513) == SC_OK, "inc_B dec 513B");
|
||||
CHECK(sc_stream_xor(&recv_b, data + 513, 1024 - 513) == SC_OK, "inc_B dec 511B");
|
||||
CHECK(memcmp(data, original, 1024) == 0, "inc round-trip matches");
|
||||
|
||||
// Test 4: all sizes 1..255 — catches alignment/keystream issues
|
||||
for (size_t sz = 1; sz <= 255; sz++) {
|
||||
uint8_t buf[256], cpy[256];
|
||||
for (size_t i = 0; i < sz; i++) buf[i] = (uint8_t)(sz ^ i);
|
||||
memcpy(cpy, buf, sz);
|
||||
CHECK(sc_stream_xor(&send_a, buf, sz) == SC_OK, "various-size enc");
|
||||
CHECK(sc_stream_xor(&recv_b, buf, sz) == SC_OK, "various-size dec");
|
||||
CHECK(memcmp(buf, cpy, sz) == 0, "various-size match");
|
||||
}
|
||||
|
||||
// Test 5: 2048 bytes (= 128 AES blocks, whole number)
|
||||
memcpy(data, original, 2048);
|
||||
CHECK(sc_stream_xor(&send_a, data, 2048) == SC_OK, "enc 2048B");
|
||||
CHECK(sc_stream_xor(&recv_b, data, 2048) == SC_OK, "dec 2048B");
|
||||
CHECK(memcmp(data, original, 2048) == 0, "round-trip 2048 matches");
|
||||
|
||||
// Test 6: A and B streams must produce DIFFERENT keystreams
|
||||
{
|
||||
uint8_t test_data[16];
|
||||
memset(test_data, 0xAA, 16);
|
||||
uint8_t a_out[16], b_out[16];
|
||||
memcpy(a_out, test_data, 16);
|
||||
memcpy(b_out, test_data, 16);
|
||||
CHECK(sc_stream_xor(&send_a, a_out, 16) == SC_OK, "xor on send_A");
|
||||
CHECK(sc_stream_xor(&send_b, b_out, 16) == SC_OK, "xor on send_B");
|
||||
CHECK(memcmp(a_out, b_out, 16) != 0, "stream_id=0 != stream_id=1 keystream");
|
||||
// Decrypt back
|
||||
CHECK(sc_stream_xor(&recv_b, a_out, 16) == SC_OK, "recv_B decrypt send_A data");
|
||||
CHECK(sc_stream_xor(&recv_a, b_out, 16) == SC_OK, "recv_A decrypt send_B data");
|
||||
CHECK(memcmp(a_out, test_data, 16) == 0, "recv_B restores original");
|
||||
CHECK(memcmp(b_out, test_data, 16) == 0, "recv_A restores original");
|
||||
}
|
||||
|
||||
// Test 7: encrypt 1 byte — extreme alignment edge
|
||||
for (int j = 0; j < 50; j++) {
|
||||
uint8_t one_byte = (uint8_t)j;
|
||||
CHECK(sc_stream_xor(&send_a, &one_byte, 1) == SC_OK, "enc 1B");
|
||||
CHECK(sc_stream_xor(&recv_b, &one_byte, 1) == SC_OK, "dec 1B");
|
||||
CHECK(one_byte == (uint8_t)j, "1-byte round-trip");
|
||||
}
|
||||
|
||||
// Test 8: error handling
|
||||
CHECK(sc_stream_xor(&send_a, NULL, 0) == SC_OK, "xor len=0 OK");
|
||||
CHECK(sc_stream_xor(&send_a, data, 0) == SC_OK, "xor data_len=0 OK");
|
||||
CHECK(sc_stream_init(&ctx_a, NULL, 0) == SC_ERR_INVALID_ARG, "init NULL state");
|
||||
CHECK(sc_stream_init(NULL, &send_a, 0) == SC_ERR_INVALID_ARG, "init NULL ctx");
|
||||
|
||||
struct sc_stream_state uninit;
|
||||
memset(&uninit, 0, sizeof(uninit));
|
||||
CHECK(sc_stream_xor(&uninit, data, 1) == SC_ERR_NOT_INITIALIZED, "xor uninitialized");
|
||||
|
||||
// У ctx без session_ready
|
||||
sc_context_t no_session_ctx;
|
||||
struct SC_MYKEYS dummy_keys;
|
||||
memset(&dummy_keys, 0, sizeof(dummy_keys));
|
||||
sc_init_ctx(&no_session_ctx, &dummy_keys);
|
||||
struct sc_stream_state bad_stream;
|
||||
CHECK(sc_stream_init(&no_session_ctx, &bad_stream, 0) == SC_ERR_NOT_INITIALIZED, "init without session");
|
||||
|
||||
// Cleanup
|
||||
sc_stream_cleanup(&send_a);
|
||||
sc_stream_cleanup(&recv_a);
|
||||
sc_stream_cleanup(&send_b);
|
||||
sc_stream_cleanup(&recv_b);
|
||||
sc_stream_cleanup(NULL);
|
||||
sc_stream_cleanup(&uninit);
|
||||
|
||||
if (test_failed) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "=== Stream Cipher Test: FAILED ===");
|
||||
return 1;
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "=== Stream Cipher Test: PASSED ===");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user