Browse Source

ETCP: Fix protocol parsing and crypto nonce handling

- Fixed ACK section parsing (elm_cnt and till offset)
- Fixed payload section to include seq number in packet
- Fixed assembly trigger condition (last_delivered_id+1==seq)
- Crypto: Added nonce transmission in encrypted packets
- Crypto: Fixed nonce generation with usec timestamp
- Reduced debug verbosity in connection handling
- Added unknown section type handling
nodeinfo-routing-update
Evgeny 2 months ago
parent
commit
a34035f41f
  1. 33
      build.log
  2. 38
      debug_ll_queue.c
  3. 23
      src/etcp.c
  4. 19
      src/etcp_connections.c
  5. 57
      src/secure_channel.c
  6. 369
      src/secure_channel.c.bak
  7. 388
      src/secure_channel.c1
  8. 2
      src/secure_channel.h
  9. 68
      src/secure_channel.h1
  10. 0
      test_ll_queue.c
  11. BIN
      tests/debug_simple
  12. 45
      tests/debug_simple.c
  13. 223072
      tests/log
  14. 78
      tests/replace_patterns.py
  15. BIN
      tests/test_ecc_encrypt
  16. BIN
      tests/test_etcp_crypto
  17. BIN
      tests/test_etcp_minimal
  18. BIN
      tests/test_etcp_simple_traffic
  19. BIN
      tests/test_etcp_two_instances
  20. BIN
      tests/test_offset
  21. 40
      tests/test_offset.c
  22. 81
      tests/update_test.py

33
build.log

@ -0,0 +1,33 @@
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 13.3.0-6ubuntu2~24.04' --with-bugurl=file:///usr/share/doc/gcc-13/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-13 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-13-fG75Ri/gcc-13-13.3.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-13-fG75Ri/gcc-13-13.3.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04)
COLLECT_GCC_OPTIONS='-I' '../src' '-I' '../lib' '-I' '../tinycrypt/lib/include' '-I' '../tinycrypt/lib/source' '-g' '-O2' '-v' '-o' 'test_ll_queue_fixed' '-mtune=generic' '-march=x86-64' '-dumpdir' 'test_ll_queue_fixed-'
/usr/libexec/gcc/x86_64-linux-gnu/13/cc1 -quiet -v -I ../src -I ../lib -I ../tinycrypt/lib/include -I ../tinycrypt/lib/source -imultiarch x86_64-linux-gnu test_ll_queue_fixed.c -D_FORTIFY_SOURCE=3 -quiet -dumpdir test_ll_queue_fixed- -dumpbase test_ll_queue_fixed.c -dumpbase-ext .c -mtune=generic -march=x86-64 -g -O2 -version -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -fcf-protection -o /tmp/ccZX9kaj.s
GNU C17 (Ubuntu 13.3.0-6ubuntu2~24.04) version 13.3.0 (x86_64-linux-gnu)
compiled by GNU C version 13.3.0, GMP version 6.3.0, MPFR version 4.2.1, MPC version 1.3.1, isl version isl-0.26-GMP
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include"
ignoring nonexistent directory "../src"
ignoring nonexistent directory "../lib"
ignoring nonexistent directory "../tinycrypt/lib/include"
ignoring nonexistent directory "../tinycrypt/lib/source"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/13/include
/usr/local/include
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
cc1: fatal error: test_ll_queue_fixed.c: Нет такого файла или каталога
compilation terminated.

38
debug_ll_queue.c

@ -0,0 +1,38 @@
#include "lib/ll_queue.h"
#include "lib/u_async.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int id;
char name[32];
int value;
} test_data_t;
int main() {
struct UASYNC* ua = uasync_create();
struct ll_queue* q = queue_new(ua, 0);
/* Create test data */
test_data_t* data1 = (test_data_t*)queue_data_new(sizeof(test_data_t));
data1->id = 1;
strcpy(data1->name, "test1");
data1->value = 100;
printf("Created data: id=%d, name=%s, value=%d\n", data1->id, data1->name, data1->value);
printf("Data pointer: %p\n", data1);
/* Put data into queue */
queue_data_put(q, data1, data1->id);
printf("After put: queue count = %d\n", queue_entry_count(q));
/* Get data from queue */
test_data_t* retrieved = (test_data_t*)queue_data_get(q);
printf("Retrieved pointer: %p\n", retrieved);
printf("Retrieved data: id=%d, name=%s, value=%d\n", retrieved->id, retrieved->name, retrieved->value);
queue_free(q);
uasync_destroy(ua, 0);
return 0;
}

23
src/etcp.c

@ -264,8 +264,7 @@ static void input_queue_cb(struct ll_queue* q, void* arg) {
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg; struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
struct ETCP_FRAGMENT* in_pkt = queue_data_get(q); struct ETCP_FRAGMENT* in_pkt = queue_data_get(q);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "input_queue_cb: processing ETCP_FRAGMENT %p (seq=%u, len=%u, data=%p)", DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "input_queue_cb: processing ETCP_FRAGMENT %p (seq=%u, len=%u)", in_pkt, in_pkt->seq, in_pkt->ll.size);
in_pkt, in_pkt->seq, in_pkt->ll.size, in_pkt->pkt_data);
// Create INFLIGHT_PACKET // Create INFLIGHT_PACKET
struct INFLIGHT_PACKET* p = memory_pool_alloc(etcp->inflight_pool); struct INFLIGHT_PACKET* p = memory_pool_alloc(etcp->inflight_pool);
@ -370,9 +369,9 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
size_t send_q_size = queue_entry_count(etcp->input_send_q); size_t send_q_size = queue_entry_count(etcp->input_send_q);
if (send_q_size == 0) {// сгребаем из input_queue if (send_q_size == 0) {// сгребаем из input_queue
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: input_send_q empty, check if avail input_queue -> inflight"); // DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: input_send_q empty, check if avail input_queue -> inflight");
input_queue_try_resume(etcp); input_queue_try_resume(etcp);
return NULL; // return NULL;
} }
// First, check if there's a packet in input_send_q (retrans or new) // First, check if there's a packet in input_send_q (retrans or new)
@ -442,6 +441,11 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
// фрейм data (0) обязательно в конец // фрейм data (0) обязательно в конец
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: packet with payload (seq=%u, len=%u), ack_size=%d", inf_pkt->seq, inf_pkt->ll.size, dgram->data[1]); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: packet with payload (seq=%u, len=%u), ack_size=%d", inf_pkt->seq, inf_pkt->ll.size, dgram->data[1]);
dgram->data[ptr++]=0;// payload dgram->data[ptr++]=0;// payload
dgram->data[ptr++]=inf_pkt->seq;
dgram->data[ptr++]=inf_pkt->seq>>8;
dgram->data[ptr++]=inf_pkt->seq>>16;
dgram->data[ptr++]=inf_pkt->seq>>24;
memcpy(&dgram->data[ptr], inf_pkt->pkt_data, inf_pkt->ll.size); ptr+=inf_pkt->ll.size; memcpy(&dgram->data[ptr], inf_pkt->pkt_data, inf_pkt->ll.size); ptr+=inf_pkt->ll.size;
} }
else { else {
@ -457,7 +461,7 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
// Callback for when a link is ready to send data // Callback for when a link is ready to send data
static void etcp_link_ready_callback(struct ETCP_CONN* etcp) { static void etcp_link_ready_callback(struct ETCP_CONN* etcp) {
if (!etcp) return; if (!etcp) return;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_link_ready_callback: processing send queue for etcp=%p", etcp); // DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_link_ready_callback: processing send queue for etcp=%p", etcp);
etcp_conn_process_send_queue(etcp); etcp_conn_process_send_queue(etcp);
} }
@ -465,7 +469,7 @@ static void etcp_link_ready_callback(struct ETCP_CONN* etcp) {
static void etcp_conn_process_send_queue(struct ETCP_CONN* etcp) { static void etcp_conn_process_send_queue(struct ETCP_CONN* etcp) {
struct ETCP_DGRAM* dgram; struct ETCP_DGRAM* dgram;
while(dgram = etcp_request_pkt(etcp)) { while(dgram = etcp_request_pkt(etcp)) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_conn_process_send_queue: sending packet"); // DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_conn_process_send_queue: sending packet");
etcp_loadbalancer_send(dgram); etcp_loadbalancer_send(dgram);
} }
} }
@ -618,8 +622,8 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
// Process sections as per protocol.txt // Process sections as per protocol.txt
switch (type) { switch (type) {
case ETCP_SECTION_ACK: { case ETCP_SECTION_ACK: {
int elm_cnt=data[1]*8; int elm_cnt=data[1];
uint32_t till=data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); uint32_t till=data[2] | (data[3]<<8) | (data[4]<<16) | (data[5]<<24);
data+=6; data+=6;
for (int i=0; i<elm_cnt; i++) { for (int i=0; i<elm_cnt; i++) {
uint32_t seq=data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); uint32_t seq=data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
@ -659,7 +663,7 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
memcpy(payload_data, data + 5, pkt_len); memcpy(payload_data, data + 5, pkt_len);
queue_data_put(etcp->recv_q, rx_pkt, seq); queue_data_put(etcp->recv_q, rx_pkt, seq);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_conn_input: packet seq=%u added to recv_q, calling assembly (last_delivered_id=%u)", seq, etcp->last_delivered_id); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_conn_input: packet seq=%u added to recv_q, calling assembly (last_delivered_id=%u)", seq, etcp->last_delivered_id);
if (etcp->last_delivered_id==seq) etcp_output_try_assembly(etcp);// пробуем собрать выходную очередь из фрагментов if (etcp->last_delivered_id+1==seq) etcp_output_try_assembly(etcp);// пробуем собрать выходную очередь из фрагментов
} }
} }
len=0; len=0;
@ -668,6 +672,7 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
default: default:
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_conn_input: unknown section type=0x%02x", type); DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_conn_input: unknown section type=0x%02x", type);
len=0;
break; break;
} }

19
src/etcp_connections.c

@ -426,7 +426,7 @@ int etcp_encrypt_send(struct ETCP_DGRAM* dgram) {
memcpy(enc_buf+enc_buf_len, dgram->data+len, dgram->noencrypt_len); memcpy(enc_buf+enc_buf_len, dgram->data+len, dgram->noencrypt_len);
// DUMP: Show complete packet before sending // DUMP: Show complete packet before sending
// dump_packet_bytes("READY TO SEND", enc_buf, enc_buf_len + dgram->noencrypt_len); // log_dump("ENCRYPTED, READY TO SEND", enc_buf, enc_buf_len + dgram->noencrypt_len);
struct sockaddr_storage* addr=&dgram->link->remote_addr; struct sockaddr_storage* addr=&dgram->link->remote_addr;
socklen_t addr_len = (addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); socklen_t addr_len = (addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
@ -481,7 +481,6 @@ static void etcp_connections_read_callback(int fd, void* arg) {
ssize_t recv_len = recvfrom(fd, data, PACKET_DATA_SIZE, 0, (struct sockaddr*)&addr, &addr_len); ssize_t recv_len = recvfrom(fd, data, PACKET_DATA_SIZE, 0, (struct sockaddr*)&addr, &addr_len);
if (recv_len <= 0) { if (recv_len <= 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[ETCP] recvfrom failed or no data, recv_len=%zd, errno=%d", recv_len, errno);
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: recvfrom failed, error=%zd, errno=%d", recv_len, errno); DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: recvfrom failed, error=%zd, errno=%d", recv_len, errno);
return; return;
} }
@ -489,7 +488,7 @@ static void etcp_connections_read_callback(int fd, void* arg) {
// printf("[ETCP] Received packet: %zd bytes from address\n", recv_len); // printf("[ETCP] Received packet: %zd bytes from address\n", recv_len);
// DUMP: Show received packet content // DUMP: Show received packet content
log_dump("RECV in:", data, recv_len); // link unknown at this point // log_dump("RECV in:", data, recv_len); // link unknown at this point
struct ETCP_DGRAM* pkt = memory_pool_alloc(e_sock->instance->pkt_pool); struct ETCP_DGRAM* pkt = memory_pool_alloc(e_sock->instance->pkt_pool);
if (!pkt) return; if (!pkt) return;
@ -581,9 +580,9 @@ static void etcp_connections_read_callback(int fd, void* arg) {
if (sc_decrypt(&link->etcp->crypto_ctx, data, recv_len, (uint8_t*)&pkt->timestamp, &pkt_len)) { if (sc_decrypt(&link->etcp->crypto_ctx, data, recv_len, (uint8_t*)&pkt->timestamp, &pkt_len)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to decrypt packet from node %llu len=%d", (unsigned long long)link->etcp->instance->node_id, recv_len); DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to decrypt packet from node %llu len=%d", (unsigned long long)link->etcp->instance->node_id, recv_len);
log_dump("my_privkey",&link->etcp->crypto_ctx.pk->private_key, SC_PRIVKEY_SIZE); // log_dump("my_privkey",&link->etcp->crypto_ctx.pk->private_key, SC_PRIVKEY_SIZE);
log_dump("my_pubkey",&link->etcp->crypto_ctx.pk->public_key, SC_PUBKEY_SIZE); // log_dump("my_pubkey",&link->etcp->crypto_ctx.pk->public_key, SC_PUBKEY_SIZE);
log_dump("peer_pubkey",&link->etcp->crypto_ctx.peer_public_key, SC_PUBKEY_SIZE); // log_dump("peer_pubkey",&link->etcp->crypto_ctx.peer_public_key, SC_PUBKEY_SIZE);
errorcode=6; errorcode=6;
goto ec_fr; goto ec_fr;
} }
@ -608,17 +607,15 @@ static void etcp_connections_read_callback(int fd, void* arg) {
link->mtu = (pkt->data[offset++] << 8) | pkt->data[offset++]; link->mtu = (pkt->data[offset++] << 8) | pkt->data[offset++];
if (offset > pkt_len) { errorcode=13; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: packet parsing overflow, offset=%zu, pkt_len=%zu", offset, pkt_len); goto ec_fr; } if (offset > pkt_len) { errorcode=13; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: packet parsing overflow, offset=%zu, pkt_len=%zu", offset, pkt_len); goto ec_fr; }
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Received INIT_RESPONSE from server_node_id=%llu, mtu=%d", // DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Received INIT_RESPONSE from server_node_id=%llu, mtu=%d", (unsigned long long)server_node_id, link->mtu);
(unsigned long long)server_node_id, link->mtu);
link->etcp->peer_node_id = server_node_id; // If not set link->etcp->peer_node_id = server_node_id; // If not set
// Mark link as initialized // Mark link as initialized
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "Setting link->initialized=1, link=%p, is_server=%d", link, link->is_server); // DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "Setting link->initialized=1, link=%p, is_server=%d", link, link->is_server);
link->initialized = 1;// получен init response (client) link->initialized = 1;// получен init response (client)
loadbalancer_link_ready(link); loadbalancer_link_ready(link);
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "[ETCP] Link initialized successfully! Server node_id=%llu, mtu=%d", DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "etcp client: Link initialized successfully! Server node_id=%llu, mtu=%d", (unsigned long long)server_node_id, link->mtu);
(unsigned long long)server_node_id, link->mtu);
// Cancel init timer if exists // Cancel init timer if exists
if (link->init_timer) { if (link->init_timer) {

57
src/secure_channel.c

@ -213,7 +213,7 @@ static void sc_build_nonce(uint64_t counter, uint8_t *nonce_out)
struct tc_sha256_state_struct sha_ctx; struct tc_sha256_state_struct sha_ctx;
uint8_t hash[32]; uint8_t hash[32];
struct timeval tv; struct timeval tv;
uint8_t data[8 + 8 + 4]; uint8_t data[8 + 8 + 8];
if (!sc_urandom_initialized) { if (!sc_urandom_initialized) {
sc_init_random_seed(); sc_init_random_seed();
@ -234,26 +234,25 @@ static void sc_build_nonce(uint64_t counter, uint8_t *nonce_out)
data[17] = (tv.tv_sec >> 8) & 0xFF; data[17] = (tv.tv_sec >> 8) & 0xFF;
data[18] = (tv.tv_sec >> 16) & 0xFF; data[18] = (tv.tv_sec >> 16) & 0xFF;
data[19] = (tv.tv_sec >> 24) & 0xFF; data[19] = (tv.tv_sec >> 24) & 0xFF;
data[20] = (tv.tv_usec >> 0) & 0xFF;
data[21] = (tv.tv_usec >> 8) & 0xFF;
data[22] = (tv.tv_usec >> 16) & 0xFF;
data[23] = (tv.tv_usec >> 24) & 0xFF;
tc_sha256_init(&sha_ctx); tc_sha256_init(&sha_ctx);
tc_sha256_update(&sha_ctx, data, 20); tc_sha256_update(&sha_ctx, data, 24);
tc_sha256_final(hash, &sha_ctx); tc_sha256_final(hash, &sha_ctx);
memcpy(nonce_out, hash, SC_NONCE_SIZE); memcpy(nonce_out, hash, SC_NONCE_SIZE);
} }
sc_status_t sc_encrypt(sc_context_t *ctx, sc_status_t sc_encrypt(sc_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t *ciphertext_len) {
const uint8_t *plaintext,
size_t plaintext_len,
uint8_t *ciphertext,
size_t *ciphertext_len)
{
uint8_t nonce[SC_NONCE_SIZE]; uint8_t nonce[SC_NONCE_SIZE];
struct tc_aes_key_sched_struct sched; uint8_t plaintext_with_crc[plaintext_len + SC_CRC32_SIZE];
struct tc_ccm_mode_struct ccm_state;
size_t total_plaintext_len = plaintext_len + SC_CRC32_SIZE; size_t total_plaintext_len = plaintext_len + SC_CRC32_SIZE;
uint8_t plaintext_with_crc[total_plaintext_len];
uint8_t combined_output[total_plaintext_len + SC_TAG_SIZE]; uint8_t combined_output[total_plaintext_len + SC_TAG_SIZE];
struct tc_aes_key_sched_struct sched;
struct tc_ccm_mode_struct ccm_state;
if (!ctx || !plaintext || !ciphertext || !ciphertext_len) { if (!ctx || !plaintext || !ciphertext || !ciphertext_len) {
return SC_ERR_INVALID_ARG; return SC_ERR_INVALID_ARG;
@ -275,14 +274,14 @@ sc_status_t sc_encrypt(sc_context_t *ctx,
plaintext_with_crc[plaintext_len + 2] = (crc >> 16) & 0xFF; plaintext_with_crc[plaintext_len + 2] = (crc >> 16) & 0xFF;
plaintext_with_crc[plaintext_len + 3] = (crc >> 24) & 0xFF; plaintext_with_crc[plaintext_len + 3] = (crc >> 24) & 0xFF;
/* Генерируем nonce с таймером */
sc_build_nonce(ctx->tx_counter, nonce);
/* Initialize AES key schedule */ /* Initialize AES key schedule */
if (tc_aes128_set_encrypt_key(&sched, ctx->session_key) != TC_CRYPTO_SUCCESS) { if (tc_aes128_set_encrypt_key(&sched, ctx->session_key) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO; return SC_ERR_CRYPTO;
} }
/* Build nonce from counter */
sc_build_nonce(ctx->tx_counter, nonce);
/* Configure CCM mode */ /* Configure CCM mode */
if (tc_ccm_config(&ccm_state, &sched, nonce, SC_NONCE_SIZE, SC_TAG_SIZE) != TC_CRYPTO_SUCCESS) { if (tc_ccm_config(&ccm_state, &sched, nonce, SC_NONCE_SIZE, SC_TAG_SIZE) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO; return SC_ERR_CRYPTO;
@ -296,9 +295,10 @@ sc_status_t sc_encrypt(sc_context_t *ctx,
return SC_ERR_CRYPTO; return SC_ERR_CRYPTO;
} }
/* Copy ciphertext + tag to output buffer */ /* Copy nonce + ciphertext + tag to output buffer */
memcpy(ciphertext, combined_output, total_plaintext_len + SC_TAG_SIZE); memcpy(ciphertext, nonce, SC_NONCE_SIZE);
*ciphertext_len = total_plaintext_len + SC_TAG_SIZE; memcpy(ciphertext + SC_NONCE_SIZE, combined_output, total_plaintext_len + SC_TAG_SIZE);
*ciphertext_len = SC_NONCE_SIZE + total_plaintext_len + SC_TAG_SIZE;
ctx->tx_counter++; ctx->tx_counter++;
@ -314,8 +314,7 @@ sc_status_t sc_decrypt(sc_context_t *ctx,
uint8_t nonce[SC_NONCE_SIZE]; uint8_t nonce[SC_NONCE_SIZE];
struct tc_aes_key_sched_struct sched; struct tc_aes_key_sched_struct sched;
struct tc_ccm_mode_struct ccm_state; struct tc_ccm_mode_struct ccm_state;
TCCcmMode_t c = &ccm_state; size_t total_plaintext_len = ciphertext_len - SC_NONCE_SIZE - SC_TAG_SIZE;
size_t total_plaintext_len = ciphertext_len - SC_TAG_SIZE;
uint8_t plaintext_with_crc[total_plaintext_len]; uint8_t plaintext_with_crc[total_plaintext_len];
if (!ctx || !ciphertext || !plaintext || !plaintext_len) { if (!ctx || !ciphertext || !plaintext || !plaintext_len) {
@ -326,28 +325,32 @@ sc_status_t sc_decrypt(sc_context_t *ctx,
return SC_ERR_NOT_INITIALIZED; return SC_ERR_NOT_INITIALIZED;
} }
if (ciphertext_len < SC_TAG_SIZE + SC_CRC32_SIZE) { if (ciphertext_len < SC_NONCE_SIZE + SC_TAG_SIZE + SC_CRC32_SIZE) {
return SC_ERR_INVALID_ARG; return SC_ERR_INVALID_ARG;
} }
/* Извлекаем nonce из начала ciphertext */
memcpy(nonce, ciphertext, SC_NONCE_SIZE);
/* Ciphertext для расшифровки начинается после nonce */
const uint8_t *encrypted_data = ciphertext + SC_NONCE_SIZE;
size_t encrypted_len = ciphertext_len - SC_NONCE_SIZE;
/* Initialize AES key schedule */ /* Initialize AES key schedule */
if (tc_aes128_set_encrypt_key(&sched, ctx->session_key) != TC_CRYPTO_SUCCESS) { if (tc_aes128_set_encrypt_key(&sched, ctx->session_key) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO; return SC_ERR_CRYPTO;
} }
/* Build nonce from counter */ /* Configure CCM mode с извлечённым nonce */
sc_build_nonce(ctx->rx_counter, nonce); if (tc_ccm_config(&ccm_state, &sched, nonce, SC_NONCE_SIZE, SC_TAG_SIZE) != TC_CRYPTO_SUCCESS) {
/* Configure CCM mode */
if (tc_ccm_config(c, &sched, nonce, SC_NONCE_SIZE, SC_TAG_SIZE) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO; return SC_ERR_CRYPTO;
} }
/* Decrypt and verify tag */ /* Decrypt and verify tag */
if (tc_ccm_decryption_verification(plaintext_with_crc, total_plaintext_len, if (tc_ccm_decryption_verification(plaintext_with_crc, total_plaintext_len,
NULL, 0, /* no associated data */ NULL, 0, /* no associated data */
ciphertext, ciphertext_len, encrypted_data, encrypted_len,
c) != TC_CRYPTO_SUCCESS) { &ccm_state) != TC_CRYPTO_SUCCESS) {
return SC_ERR_AUTH_FAILED; return SC_ERR_AUTH_FAILED;
} }

369
src/secure_channel.c.bak

@ -0,0 +1,369 @@
/* sc_lib.c - Secure Channel library implementation using TinyCrypt */
#include "secure_channel.h"
#include "../tinycrypt/lib/include/tinycrypt/ecc.h"
#include "../tinycrypt/lib/include/tinycrypt/ecc_dh.h"
#include "../tinycrypt/lib/include/tinycrypt/aes.h"
#include "../tinycrypt/lib/include/tinycrypt/ccm_mode.h"
#include "../tinycrypt/lib/include/tinycrypt/constants.h"
#include "../tinycrypt/lib/include/tinycrypt/ecc_platform_specific.h"
#include "../tinycrypt/lib/include/tinycrypt/sha256.h"
#include <string.h>
#include <stddef.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdio.h>
// Simple debug macros
#define DEBUG_CATEGORY_CRYPTO 1
#define DEBUG_ERROR(category, fmt, ...) fprintf(stderr, "ERROR: " fmt "\n", ##__VA_ARGS__)
#define DEBUG_INFO(category, fmt, ...) fprintf(stdout, "INFO: " fmt "\n", ##__VA_ARGS__)
#include <stdio.h>
#include <fcntl.h>
#include "crc32.h"
static const struct uECC_Curve_t *curve = NULL;
static uint8_t sc_urandom_seed[8] = {0};
static int sc_urandom_initialized = 0;
static void sc_init_random_seed(void)
{
int fd = open("/dev/urandom", O_RDONLY);
if (fd >= 0) {
ssize_t ret = read(fd, sc_urandom_seed, 8);
close(fd);
if (ret == 8) {
sc_urandom_initialized = 1;
}
}
}
static int sc_rng(uint8_t *dest, unsigned size)
{
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
return 0;
}
ssize_t ret = read(fd, dest, size);
close(fd);
if (ret != size) {
return 0;
}
/* Mix in PID and microtime for additional entropy */
pid_t pid = getpid();
struct timeval tv;
gettimeofday(&tv, NULL);
for (unsigned i = 0; i < size; i++) {
dest[i] ^= ((pid >> (i % (sizeof(pid) * 8))) & 0xFF);
dest[i] ^= ((tv.tv_sec >> (i % (sizeof(tv.tv_sec) * 8))) & 0xFF);
dest[i] ^= ((tv.tv_usec >> (i % (sizeof(tv.tv_usec) * 8))) & 0xFF);
}
return 1;
}
static int sc_validate_key(const uint8_t *public_key)
{
if (!curve) {
curve = uECC_secp256r1();
}
int result = uECC_valid_public_key(public_key, curve);
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_validate_key: uECC_valid_public_key returned %d", result);
return result;
}
sc_status_t sc_generate_keypair(struct SC_MYKEYS *pk)
{
if (!pk) {
return SC_ERR_INVALID_ARG;
}
if (!curve) {
curve = uECC_secp256r1();
}
/* Set custom RNG function */
uECC_set_rng(sc_rng);
if (!uECC_make_key(pk->public_key, pk->private_key, curve)) {
return SC_ERR_CRYPTO;
}
return SC_OK;
}
// Конвертация hex строки в бинарный формат
static int hex_to_binary(const char *hex_str, uint8_t *binary, size_t binary_len) {
if (!hex_str || !binary || strlen(hex_str) != binary_len * 2) return -1;
for (size_t i = 0; i < binary_len; i++) {
unsigned int byte;
if (sscanf(hex_str + i * 2, "%2x", &byte) != 1) return -1;
binary[i] = (uint8_t)byte;
}
return 0;
}
sc_status_t sc_init_local_keys(struct SC_MYKEYS *mykeys, const char *public_key, const char *private_key) {
if (!mykeys || !public_key || !private_key) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: invalid arguments");
return SC_ERR_INVALID_ARG;
}
if (!curve) {
curve = uECC_secp256r1();
}
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: public_key len=%zu, private_key len=%zu",
strlen(public_key), strlen(private_key));
/* Convert hex to binary first */
if (hex_to_binary(public_key, mykeys->public_key, SC_PUBKEY_SIZE)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: failed to convert public key from hex");
return SC_ERR_INVALID_ARG;
}
if (hex_to_binary(private_key, mykeys->private_key, SC_PRIVKEY_SIZE)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: failed to convert private key from hex");
return SC_ERR_INVALID_ARG;
}
/* Validate the converted binary public key */
if (sc_validate_key(mykeys->public_key) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: public key validation failed");
return SC_ERR_INVALID_ARG;
}
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: keys initialized successfully");
return SC_OK;
}
sc_status_t sc_init_ctx(sc_context_t *ctx, struct SC_MYKEYS *mykeys) {
ctx->pk=mykeys;
ctx->initialized = 1;
ctx->peer_key_set = 0;
ctx->session_ready = 0;
ctx->tx_counter = 0;
ctx->rx_counter = 0;
return SC_OK;
}
sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const char *peer_public_key_h, int mode) {
uint8_t shared_secret[SC_SHARED_SECRET_SIZE];
uint8_t peer_public_key[SC_PUBKEY_SIZE];
if (mode) {
if (hex_to_binary(peer_public_key_h, peer_public_key, SC_PUBKEY_SIZE)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: invalid hex key format");
return SC_ERR_INVALID_ARG;
}
}
else memcpy(peer_public_key, peer_public_key_h, SC_PUBKEY_SIZE);
if (!ctx) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: invalid ctx");
return SC_ERR_INVALID_ARG;
}
if (!ctx->initialized) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: ctx not initialized");
return SC_ERR_NOT_INITIALIZED;
}
if (!curve) {
curve = uECC_secp256r1();
}
/* Validate peer public key */
if (sc_validate_key(peer_public_key) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: invalid key");
return SC_ERR_INVALID_ARG;
}
/* Compute shared secret using ECDH */
if (!ctx->pk) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: no private key");
return SC_ERR_NOT_INITIALIZED;
}
if (!uECC_shared_secret(peer_public_key, ctx->pk->private_key,
shared_secret, curve)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: shared secret error");
return SC_ERR_CRYPTO;
}
/* Derive session key from shared secret (simple copy for demo) */
memcpy(ctx->session_key, shared_secret, SC_SESSION_KEY_SIZE);
/* Store peer public key */
memcpy(ctx->peer_public_key, peer_public_key, SC_PUBKEY_SIZE);
ctx->peer_key_set = 1;
ctx->session_ready = 1;
return SC_OK;
}
// Новая функция для генерации nonce с микросекундами
static void generate_nonce_with_timer(uint8_t *nonce) {
struct timeval tv;
gettimeofday(&tv, NULL);
uint32_t usec = (uint32_t)tv.tv_usec;
// Поместить usec в первые 4 байта (little-endian)
nonce[0] = usec & 0xFF;
nonce[1] = (usec >> 8) & 0xFF;
nonce[2] = (usec >> 16) & 0xFF;
nonce[3] = (usec >> 24) & 0xFF;
// Заполнить оставшиеся 9 байт случайными данными
sc_rng(nonce + 4, SC_NONCE_SIZE - 4);
}
sc_status_t sc_encrypt(sc_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t *ciphertext_len) {
uint8_t nonce[SC_NONCE_SIZE];
uint8_t plaintext_with_crc[plaintext_len + SC_CRC32_SIZE];
size_t total_plaintext_len = plaintext_len + SC_CRC32_SIZE;
uint8_t combined_output[total_plaintext_len + SC_TAG_SIZE];
struct tc_aes_key_sched_struct sched;
struct tc_ccm_mode_struct ccm_state;
if (!ctx || !plaintext || !ciphertext || !ciphertext_len) {
return SC_ERR_INVALID_ARG;
}
if (!ctx->session_ready) {
return SC_ERR_NOT_INITIALIZED;
}
if (plaintext_len == 0) {
return SC_ERR_INVALID_ARG;
}
/* Добавляем CRC32 к данным */
memcpy(plaintext_with_crc, plaintext, plaintext_len);
uint32_t crc = crc32_calc(plaintext, plaintext_len);
plaintext_with_crc[plaintext_len] = (crc >> 0) & 0xFF;
plaintext_with_crc[plaintext_len + 1] = (crc >> 8) & 0xFF;
plaintext_with_crc[plaintext_len + 2] = (crc >> 16) & 0xFF;
plaintext_with_crc[plaintext_len + 3] = (crc >> 24) & 0xFF;
/* Генерируем nonce с таймером */
generate_nonce_with_timer(nonce);
/* Initialize AES key schedule */
if (tc_aes128_set_encrypt_key(&sched, ctx->session_key) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Configure CCM mode */
if (tc_ccm_config(&ccm_state, &sched, nonce, SC_NONCE_SIZE, SC_TAG_SIZE) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Encrypt and generate tag */
if (tc_ccm_generation_encryption(combined_output, sizeof(combined_output),
NULL, 0, /* no associated data */
plaintext_with_crc, total_plaintext_len,
&ccm_state) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Copy nonce + ciphertext + tag to output buffer */
memcpy(ciphertext, nonce, SC_NONCE_SIZE);
memcpy(ciphertext + SC_NONCE_SIZE, combined_output, total_plaintext_len + SC_TAG_SIZE);
*ciphertext_len = SC_NONCE_SIZE + total_plaintext_len + SC_TAG_SIZE;
ctx->tx_counter++;
return SC_OK;
}
sc_status_t sc_decrypt(sc_context_t *ctx,
const uint8_t *ciphertext,
size_t ciphertext_len,
uint8_t *plaintext,
size_t *plaintext_len)
{
uint8_t nonce[SC_NONCE_SIZE];
struct tc_aes_key_sched_struct sched;
struct tc_ccm_mode_struct ccm_state;
size_t total_plaintext_len = ciphertext_len - SC_NONCE_SIZE - SC_TAG_SIZE;
uint8_t plaintext_with_crc[total_plaintext_len];
if (!ctx || !ciphertext || !plaintext || !plaintext_len) {
return SC_ERR_INVALID_ARG;
}
if (!ctx->session_ready) {
return SC_ERR_NOT_INITIALIZED;
}
if (ciphertext_len < SC_NONCE_SIZE + SC_TAG_SIZE + SC_CRC32_SIZE) {
return SC_ERR_INVALID_ARG;
}
/* Извлекаем nonce из начала ciphertext */
memcpy(nonce, ciphertext, SC_NONCE_SIZE);
/* Ciphertext для расшифровки начинается после nonce */
const uint8_t *encrypted_data = ciphertext + SC_NONCE_SIZE;
size_t encrypted_len = ciphertext_len - SC_NONCE_SIZE;
/* Initialize AES key schedule */
if (tc_aes128_set_encrypt_key(&sched, ctx->session_key) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Configure CCM mode с извлечённым nonce */
if (tc_ccm_config(&ccm_state, &sched, nonce, SC_NONCE_SIZE, SC_TAG_SIZE) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Decrypt and verify tag */
if (tc_ccm_decryption_verification(plaintext_with_crc, total_plaintext_len,
NULL, 0, /* no associated data */
encrypted_data, encrypted_len,
&ccm_state) != TC_CRYPTO_SUCCESS) {
return SC_ERR_AUTH_FAILED;
}
/* Проверяем CRC32 */
size_t data_len = total_plaintext_len - SC_CRC32_SIZE;
uint32_t expected_crc = crc32_calc(plaintext_with_crc, data_len);
uint32_t received_crc = (plaintext_with_crc[data_len] << 0) |
(plaintext_with_crc[data_len + 1] << 8) |
(plaintext_with_crc[data_len + 2] << 16) |
(plaintext_with_crc[data_len + 3] << 24);
if (expected_crc != received_crc) {
return SC_ERR_CRC_FAILED;
}
/* Копируем данные без CRC32 */
memcpy(plaintext, plaintext_with_crc, data_len);
*plaintext_len = data_len;
ctx->rx_counter++;
return SC_OK;
}
sc_status_t sc_compute_public_key_from_private(const uint8_t *private_key, uint8_t *public_key) {
if (!private_key || !public_key) {
return SC_ERR_INVALID_ARG;
}
if (!curve) {
curve = uECC_secp256r1();
}
if (!uECC_compute_public_key(private_key, public_key, curve)) {
return SC_ERR_CRYPTO;
}
return SC_OK;
}

388
src/secure_channel.c1

@ -0,0 +1,388 @@
/* sc_lib.c - Secure Channel library implementation using TinyCrypt */
#include "secure_channel.h"
#include "../tinycrypt/lib/include/tinycrypt/ecc.h"
#include "../tinycrypt/lib/include/tinycrypt/ecc_dh.h"
#include "../tinycrypt/lib/include/tinycrypt/aes.h"
#include "../tinycrypt/lib/include/tinycrypt/ccm_mode.h"
#include "../tinycrypt/lib/include/tinycrypt/constants.h"
#include "../tinycrypt/lib/include/tinycrypt/ecc_platform_specific.h"
#include "../tinycrypt/lib/include/tinycrypt/sha256.h"
#include <string.h>
#include <stddef.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdio.h>
// Simple debug macros
#define DEBUG_CATEGORY_CRYPTO 1
#define DEBUG_ERROR(category, fmt, ...) fprintf(stderr, "ERROR: " fmt "\n", ##__VA_ARGS__)
#define DEBUG_INFO(category, fmt, ...) fprintf(stdout, "INFO: " fmt "\n", ##__VA_ARGS__)
#include <stdio.h>
#include <fcntl.h>
#include "crc32.h"
static const struct uECC_Curve_t *curve = NULL;
static uint8_t sc_urandom_seed[8] = {0};
static int sc_urandom_initialized = 0;
static void sc_init_random_seed(void)
{
int fd = open("/dev/urandom", O_RDONLY);
if (fd >= 0) {
ssize_t ret = read(fd, sc_urandom_seed, 8);
close(fd);
if (ret == 8) {
sc_urandom_initialized = 1;
}
}
}
static int sc_rng(uint8_t *dest, unsigned size)
{
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
return 0;
}
ssize_t ret = read(fd, dest, size);
close(fd);
if (ret != size) {
return 0;
}
/* Mix in PID and microtime for additional entropy */
pid_t pid = getpid();
struct timeval tv;
gettimeofday(&tv, NULL);
for (unsigned i = 0; i < size; i++) {
dest[i] ^= ((pid >> (i % (sizeof(pid) * 8))) & 0xFF);
dest[i] ^= ((tv.tv_sec >> (i % (sizeof(tv.tv_sec) * 8))) & 0xFF);
dest[i] ^= ((tv.tv_usec >> (i % (sizeof(tv.tv_usec) * 8))) & 0xFF);
}
return 1;
}
static int sc_validate_key(const uint8_t *public_key)
{
if (!curve) {
curve = uECC_secp256r1();
}
int result = uECC_valid_public_key(public_key, curve);
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_validate_key: uECC_valid_public_key returned %d", result);
return result;
}
sc_status_t sc_generate_keypair(struct SC_MYKEYS *pk)
{
if (!pk) {
return SC_ERR_INVALID_ARG;
}
if (!curve) {
curve = uECC_secp256r1();
}
/* Set custom RNG function */
uECC_set_rng(sc_rng);
if (!uECC_make_key(pk->public_key, pk->private_key, curve)) {
return SC_ERR_CRYPTO;
}
return SC_OK;
}
// Конвертация hex строки в бинарный формат
static int hex_to_binary(const char *hex_str, uint8_t *binary, size_t binary_len) {
if (!hex_str || !binary || strlen(hex_str) != binary_len * 2) return -1;
for (size_t i = 0; i < binary_len; i++) {
unsigned int byte;
if (sscanf(hex_str + i * 2, "%2x", &byte) != 1) return -1;
binary[i] = (uint8_t)byte;
}
return 0;
}
sc_status_t sc_init_local_keys(struct SC_MYKEYS *mykeys, const char *public_key, const char *private_key) {
if (!mykeys || !public_key || !private_key) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: invalid arguments");
return SC_ERR_INVALID_ARG;
}
if (!curve) {
curve = uECC_secp256r1();
}
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: public_key len=%zu, private_key len=%zu",
strlen(public_key), strlen(private_key));
/* Convert hex to binary first */
if (hex_to_binary(public_key, mykeys->public_key, SC_PUBKEY_SIZE)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: failed to convert public key from hex");
return SC_ERR_INVALID_ARG;
}
if (hex_to_binary(private_key, mykeys->private_key, SC_PRIVKEY_SIZE)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: failed to convert private key from hex");
return SC_ERR_INVALID_ARG;
}
/* Validate the converted binary public key */
if (sc_validate_key(mykeys->public_key) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: public key validation failed");
return SC_ERR_INVALID_ARG;
}
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: keys initialized successfully");
return SC_OK;
}
sc_status_t sc_init_ctx(sc_context_t *ctx, struct SC_MYKEYS *mykeys) {
ctx->pk=mykeys;
ctx->initialized = 1;
ctx->peer_key_set = 0;
ctx->session_ready = 0;
ctx->tx_counter = 0;
ctx->rx_counter = 0;
return SC_OK;
}
sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const char *peer_public_key_h, int mode) {
uint8_t shared_secret[SC_SHARED_SECRET_SIZE];
uint8_t peer_public_key[SC_PUBKEY_SIZE];
if (mode) {
if (hex_to_binary(peer_public_key_h, peer_public_key, SC_PUBKEY_SIZE)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: invalid hex key format");
return SC_ERR_INVALID_ARG;
}
}
else memcpy(peer_public_key, peer_public_key_h, SC_PUBKEY_SIZE);
if (!ctx) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: invalid ctx");
return SC_ERR_INVALID_ARG;
}
if (!ctx->initialized) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: ctx not initialized");
return SC_ERR_NOT_INITIALIZED;
}
if (!curve) {
curve = uECC_secp256r1();
}
/* Validate peer public key */
if (sc_validate_key(peer_public_key) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: invalid key");
return SC_ERR_INVALID_ARG;
}
/* Compute shared secret using ECDH */
if (!ctx->pk) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: no private key");
return SC_ERR_NOT_INITIALIZED;
}
if (!uECC_shared_secret(peer_public_key, ctx->pk->private_key,
shared_secret, curve)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: shared secret error");
return SC_ERR_CRYPTO;
}
/* Derive session key from shared secret (simple copy for demo) */
memcpy(ctx->session_key, shared_secret, SC_SESSION_KEY_SIZE);
/* Store peer public key */
memcpy(ctx->peer_public_key, peer_public_key, SC_PUBKEY_SIZE);
ctx->peer_key_set = 1;
ctx->session_ready = 1;
return SC_OK;
}
static void sc_build_nonce(uint64_t counter, uint8_t *nonce_out)
{
struct tc_sha256_state_struct sha_ctx;
uint8_t hash[32];
struct timeval tv;
uint8_t data[8 + 8 + 4];
if (!sc_urandom_initialized) {
sc_init_random_seed();
}
gettimeofday(&tv, NULL);
memcpy(data, sc_urandom_seed, 8);
data[8] = (counter >> 0) & 0xFF;
data[9] = (counter >> 8) & 0xFF;
data[10] = (counter >> 16) & 0xFF;
data[11] = (counter >> 24) & 0xFF;
data[12] = (counter >> 32) & 0xFF;
data[13] = (counter >> 40) & 0xFF;
data[14] = (counter >> 48) & 0xFF;
data[15] = (counter >> 56) & 0xFF;
data[16] = (tv.tv_sec >> 0) & 0xFF;
data[17] = (tv.tv_sec >> 8) & 0xFF;
data[18] = (tv.tv_sec >> 16) & 0xFF;
data[19] = (tv.tv_sec >> 24) & 0xFF;
tc_sha256_init(&sha_ctx);
tc_sha256_update(&sha_ctx, data, 20);
tc_sha256_final(hash, &sha_ctx);
memcpy(nonce_out, hash, SC_NONCE_SIZE);
}
sc_status_t sc_encrypt(sc_context_t *ctx,
const uint8_t *plaintext,
size_t plaintext_len,
uint8_t *ciphertext,
size_t *ciphertext_len)
{
uint8_t nonce[SC_NONCE_SIZE];
struct tc_aes_key_sched_struct sched;
struct tc_ccm_mode_struct ccm_state;
size_t total_plaintext_len = plaintext_len + SC_CRC32_SIZE;
uint8_t plaintext_with_crc[total_plaintext_len];
uint8_t combined_output[total_plaintext_len + SC_TAG_SIZE];
if (!ctx || !plaintext || !ciphertext || !ciphertext_len) {
return SC_ERR_INVALID_ARG;
}
if (!ctx->session_ready) {
return SC_ERR_NOT_INITIALIZED;
}
if (plaintext_len == 0) {
return SC_ERR_INVALID_ARG;
}
/* Добавляем CRC32 к данным */
memcpy(plaintext_with_crc, plaintext, plaintext_len);
uint32_t crc = crc32_calc(plaintext, plaintext_len);
plaintext_with_crc[plaintext_len] = (crc >> 0) & 0xFF;
plaintext_with_crc[plaintext_len + 1] = (crc >> 8) & 0xFF;
plaintext_with_crc[plaintext_len + 2] = (crc >> 16) & 0xFF;
plaintext_with_crc[plaintext_len + 3] = (crc >> 24) & 0xFF;
/* Initialize AES key schedule */
if (tc_aes128_set_encrypt_key(&sched, ctx->session_key) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Build nonce from counter */
sc_build_nonce(ctx->tx_counter, nonce);
/* Configure CCM mode */
if (tc_ccm_config(&ccm_state, &sched, nonce, SC_NONCE_SIZE, SC_TAG_SIZE) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Encrypt and generate tag */
if (tc_ccm_generation_encryption(combined_output, sizeof(combined_output),
NULL, 0, /* no associated data */
plaintext_with_crc, total_plaintext_len,
&ccm_state) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Copy ciphertext + tag to output buffer */
memcpy(ciphertext, combined_output, total_plaintext_len + SC_TAG_SIZE);
*ciphertext_len = total_plaintext_len + SC_TAG_SIZE;
ctx->tx_counter++;
return SC_OK;
}
sc_status_t sc_decrypt(sc_context_t *ctx,
const uint8_t *ciphertext,
size_t ciphertext_len,
uint8_t *plaintext,
size_t *plaintext_len)
{
uint8_t nonce[SC_NONCE_SIZE];
struct tc_aes_key_sched_struct sched;
struct tc_ccm_mode_struct ccm_state;
TCCcmMode_t c = &ccm_state;
size_t total_plaintext_len = ciphertext_len - SC_TAG_SIZE;
uint8_t plaintext_with_crc[total_plaintext_len];
if (!ctx || !ciphertext || !plaintext || !plaintext_len) {
return SC_ERR_INVALID_ARG;
}
if (!ctx->session_ready) {
return SC_ERR_NOT_INITIALIZED;
}
if (ciphertext_len < SC_TAG_SIZE + SC_CRC32_SIZE) {
return SC_ERR_INVALID_ARG;
}
/* Initialize AES key schedule */
if (tc_aes128_set_encrypt_key(&sched, ctx->session_key) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Build nonce from counter */
sc_build_nonce(ctx->rx_counter, nonce);
/* Configure CCM mode */
if (tc_ccm_config(c, &sched, nonce, SC_NONCE_SIZE, SC_TAG_SIZE) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Decrypt and verify tag */
if (tc_ccm_decryption_verification(plaintext_with_crc, total_plaintext_len,
NULL, 0, /* no associated data */
ciphertext, ciphertext_len,
c) != TC_CRYPTO_SUCCESS) {
return SC_ERR_AUTH_FAILED;
}
/* Проверяем CRC32 */
size_t data_len = total_plaintext_len - SC_CRC32_SIZE;
uint32_t expected_crc = crc32_calc(plaintext_with_crc, data_len);
uint32_t received_crc = (plaintext_with_crc[data_len] << 0) |
(plaintext_with_crc[data_len + 1] << 8) |
(plaintext_with_crc[data_len + 2] << 16) |
(plaintext_with_crc[data_len + 3] << 24);
if (expected_crc != received_crc) {
return SC_ERR_CRC_FAILED;
}
/* Копируем данные без CRC32 */
memcpy(plaintext, plaintext_with_crc, data_len);
*plaintext_len = data_len;
ctx->rx_counter++;
return SC_OK;
}
sc_status_t sc_compute_public_key_from_private(const uint8_t *private_key, uint8_t *public_key) {
if (!private_key || !public_key) {
return SC_ERR_INVALID_ARG;
}
if (!curve) {
curve = uECC_secp256r1();
}
if (!uECC_compute_public_key(private_key, public_key, curve)) {
return SC_ERR_CRYPTO;
}
return SC_OK;
}

2
src/secure_channel.h

@ -43,7 +43,7 @@ struct secure_channel {
uint8_t peer_public_key[SC_PUBKEY_SIZE]; uint8_t peer_public_key[SC_PUBKEY_SIZE];
uint8_t session_key[SC_SESSION_KEY_SIZE]; /* Derived session key */ uint8_t session_key[SC_SESSION_KEY_SIZE]; /* Derived session key */
/* Nonces для отправки и приема */ /* Nonces для отправки и приема (теперь генерируются динамически, не используются counters для nonce) */
uint8_t send_nonce[SC_NONCE_SIZE]; uint8_t send_nonce[SC_NONCE_SIZE];
uint8_t recv_nonce[SC_NONCE_SIZE]; uint8_t recv_nonce[SC_NONCE_SIZE];

68
src/secure_channel.h1

@ -0,0 +1,68 @@
// secure_channel.h
#ifndef SECURE_CHANNEL_H
#define SECURE_CHANNEL_H
#include <stdint.h>
#include <stddef.h>
// Размеры ключей
#define SC_PRIVKEY_SIZE 32
#define SC_PUBKEY_SIZE 64
#define SC_HASH_SIZE 32
#define SC_NONCE_SIZE 13 // CCM requires exactly 13 bytes
#define SC_SHARED_SECRET_SIZE SC_HASH_SIZE
#define SC_SESSION_KEY_SIZE 16
#define SC_TAG_SIZE 8
#define SC_CRC32_SIZE 4
// Коды возврата
#define SC_OK 0
#define SC_ERR_INVALID_ARG -1
#define SC_ERR_CRYPTO -2
#define SC_ERR_NOT_INITIALIZED -3
#define SC_ERR_AUTH_FAILED -4
#define SC_ERR_CRC_FAILED -5
#define SC_PEER_PUBKEY_BIN 0
#define SC_PEER_PUBKEY_HEX 1
// Типы
typedef int sc_status_t;
typedef struct secure_channel sc_context_t;
struct SC_MYKEYS {
/* Локальные ключи */
uint8_t private_key[SC_PRIVKEY_SIZE];
uint8_t public_key[SC_PUBKEY_SIZE];
};
// Контекст защищенного канала
struct secure_channel {
struct SC_MYKEYS* pk;
/* Ключи пира (после key exchange) */
uint8_t peer_public_key[SC_PUBKEY_SIZE];
uint8_t session_key[SC_SESSION_KEY_SIZE]; /* Derived session key */
/* Nonces для отправки и приема */
uint8_t send_nonce[SC_NONCE_SIZE];
uint8_t recv_nonce[SC_NONCE_SIZE];
uint8_t initialized;
uint8_t peer_key_set;
uint8_t session_ready;
uint64_t tx_counter;
uint64_t rx_counter;
};
// Функции инициализации
sc_status_t sc_init_ctx(sc_context_t *ctx, struct SC_MYKEYS *mykeys);
sc_status_t sc_generate_keypair(struct SC_MYKEYS *keys);
sc_status_t sc_init_local_keys(struct SC_MYKEYS *mykeys, const char *public_key, const char *private_key);
sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const char *peer_public_key, int mode);// mode: 0-bin 1-hex key format
sc_status_t sc_compute_public_key_from_private(const uint8_t *private_key, uint8_t *public_key);
// Криптографические операции
sc_status_t sc_encrypt(sc_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t *ciphertext_len);
sc_status_t sc_decrypt(sc_context_t *ctx, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, size_t *plaintext_len);
#endif // SECURE_CHANNEL_H

0
test_ll_queue.c

BIN
tests/debug_simple

Binary file not shown.

45
tests/debug_simple.c

@ -0,0 +1,45 @@
#include "../lib/ll_queue.h"
#include "../lib/u_async.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int id;
char name[32];
int value;
} test_data_t;
int main() {
struct UASYNC* ua = uasync_create();
struct ll_queue* q = queue_new(ua, 0);
/* Create test data */
test_data_t* data1 = (test_data_t*)queue_data_new(sizeof(test_data_t));
printf("queue_data_new returned: %p\n", data1);
data1->id = 1;
strcpy(data1->name, "test1");
data1->value = 100;
printf("Created data: id=%d, name=%s, value=%d\n", data1->id, data1->name, data1->value);
printf("Data pointer: %p\n", data1);
/* Put data into queue */
int put_result = queue_data_put(q, data1, data1->id);
printf("queue_data_put returned: %d\n", put_result);
printf("After put: queue count = %d\n", queue_entry_count(q));
/* Get data from queue */
test_data_t* retrieved = (test_data_t*)queue_data_get(q);
printf("Retrieved pointer: %p\n", retrieved);
if (retrieved) {
printf("Retrieved data: id=%d, name=%s, value=%d\n", retrieved->id, retrieved->name, retrieved->value);
} else {
printf("Retrieved NULL!\n");
}
queue_free(q);
uasync_destroy(ua, 0);
return 0;
}

223072
tests/log

File diff suppressed because it is too large Load Diff

78
tests/replace_patterns.py

@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""
Script to replace queue_data patterns with macros in test_ll_queue.c
"""
import re
with open('test_ll_queue.c', 'r') as f:
content = f.read()
# Pattern 1: test_data_t* var = (test_data_t*)queue_data_new(sizeof(test_data_t));
# Replace with: Q_NEW(var);
content = re.sub(
r'test_data_t\* (\w+) = \(test_data_t\*\)queue_data_new\(sizeof\(test_data_t\)\);',
r'Q_NEW(\1);',
content
)
# Pattern 2: test_data_t* var = (test_data_t*)queue_data_new_from_pool(pool);
# Replace with: Q_NEW_POOL(var, pool);
content = re.sub(
r'test_data_t\* (\w+) = \(test_data_t\*\)queue_data_new_from_pool\(([^)]+)\);',
r'Q_NEW_POOL(\1, \2);',
content
)
# Pattern 3: test_data_t* var = (test_data_t*)queue_data_get(q);
# Replace with: Q_GET(q, var);
content = re.sub(
r'test_data_t\* (\w+) = \(test_data_t\*\)queue_data_get\(([^)]+)\);',
r'Q_GET(\2, \1);',
content
)
# Pattern 4: queue_data_put(q, var, var->id)
# Replace with: Q_PUT(q, var)
content = re.sub(
r'queue_data_put\(([^,]+), ([^,]+), \2->id\)',
r'Q_PUT(\1, \2)',
content
)
# Pattern 5: queue_data_put_first(q, var, var->id)
# Replace with: Q_PUT_FIRST(q, var)
content = re.sub(
r'queue_data_put_first\(([^,]+), ([^,]+), \2->id\)',
r'Q_PUT_FIRST(\1, \2)',
content
)
# Pattern 6: queue_data_free(var);
# Replace with: Q_FREE(var);
content = re.sub(
r'queue_data_free\((\w+)\);',
r'Q_FREE(\1);',
content
)
# Pattern 7: test_data_t* var = (test_data_t*)queue_find_data_by_id(q, id);
# Replace with: Q_FIND(q, id, var);
content = re.sub(
r'test_data_t\* (\w+) = \(test_data_t\*\)queue_find_data_by_id\(([^,]+), ([^)]+)\);',
r'Q_FIND(\2, \3, \1);',
content
)
# Pattern 8: queue_remove_data(q, var)
# Replace with: Q_REMOVE(q, var)
content = re.sub(
r'queue_remove_data\(([^,]+), ([^)]+)\)',
r'Q_REMOVE(\1, \2)',
content
)
with open('test_ll_queue.c', 'w') as f:
f.write(content)
print("All patterns replaced successfully!")

BIN
tests/test_ecc_encrypt

Binary file not shown.

BIN
tests/test_etcp_crypto

Binary file not shown.

BIN
tests/test_etcp_minimal

Binary file not shown.

BIN
tests/test_etcp_simple_traffic

Binary file not shown.

BIN
tests/test_etcp_two_instances

Binary file not shown.

BIN
tests/test_offset

Binary file not shown.

40
tests/test_offset.c

@ -0,0 +1,40 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef struct {
int a;
int b;
int ref_count;
uint8_t data[0];
} test_struct;
int main() {
test_struct* entry = malloc(sizeof(test_struct) + 100);
entry->a = 1;
entry->b = 2;
entry->ref_count = 3;
void* data_ptr = entry->data; // or (void*)(entry + 1)
printf("entry pointer: %p\n", entry);
printf("data pointer: %p\n", data_ptr);
printf("sizeof(test_struct): %zu\n", sizeof(test_struct));
printf("difference in bytes: %ld\n", (char*)data_ptr - (char*)entry);
// Test different ways to convert back
test_struct* back1 = (test_struct*)((char*)data_ptr - sizeof(test_struct));
test_struct* back2 = (test_struct*)data_ptr - 1;
test_struct* back3 = (test_struct*)data_ptr - 0;
printf("back1 (char* subtraction): %p\n", back1);
printf("back2 (pointer - 1): %p\n", back2);
printf("back3 (pointer - 0): %p\n", back3);
printf("back1 ref_count: %d\n", back1->ref_count);
printf("back2 ref_count: %d\n", back2->ref_count);
printf("back3 ref_count: %d\n", back3->ref_count);
free(entry);
return 0;
}

81
tests/update_test.py

@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""
Script to update test_ll_queue.c for xxx=0 architecture
Changes all queue_data_* calls to use struct ll_entry* intermediate
"""
import re
# Read the file
with open('test_ll_queue.c', 'r') as f:
content = f.read()
# Pattern 1: queue_data_new with variable declaration
# FROM: test_data_t* data = (test_data_t*)queue_data_new(sizeof(test_data_t));
# TO: struct ll_entry* entry = (struct ll_entry*)queue_data_new(sizeof(test_data_t));
# test_data_t* data = (test_data_t*)entry->data;
# Find all occurrences and replace
lines = content.split('\n')
new_lines = []
i = 0
while i < len(lines):
line = lines[i]
# Pattern 1: test_data_t* data = (test_data_t*)queue_data_new
match = re.match(r'(\s*)test_data_t\* (\w+) = \(test_data_t\*\)queue_data_new\(sizeof\(test_data_t\)\);', line)
if match:
indent = match.group(1)
var_name = match.group(2)
# Replace with two lines
new_lines.append(f'{indent}struct ll_entry* entry_{var_name} = (struct ll_entry*)queue_data_new(sizeof(test_data_t));')
new_lines.append(f'{indent}test_data_t* {var_name} = (test_data_t*)entry_{var_name}->data;')
i += 1
continue
# Pattern 2: test_data_t* var = (test_data_t*)queue_data_get
match = re.match(r'(\s*)test_data_t\* (\w+) = \(test_data_t\*\)queue_data_get\(([^)]+)\);', line)
if match:
indent = match.group(1)
var_name = match.group(2)
queue_var = match.group(3)
# Replace with two lines
new_lines.append(f'{indent}struct ll_entry* entry_{var_name} = (struct ll_entry*)queue_data_get({queue_var});')
new_lines.append(f'{indent}test_data_t* {var_name} = entry_{var_name} ? (test_data_t*)entry_{var_name}->data : NULL;')
i += 1
continue
# Pattern 3: queue_data_put with data variable
# Need to find the corresponding entry variable
match = re.match(r'(\s*)(\w+)\(q, (\w+), (\w+)->id\)', line)
if match and 'queue_data_put' in line:
func = match.group(2)
var = match.group(3)
# Replace data with entry variable
new_line = line.replace(f'{var}, {var}->id)', f'entry_{var}, {var}->id)')
new_lines.append(new_line)
i += 1
continue
# Pattern 4: queue_data_free with data variable
match = re.match(r'(\s*)queue_data_free\((\w+)\);', line)
if match:
indent = match.group(1)
var = match.group(2)
# Check if this is a data variable (not entry)
if not var.startswith('entry_') and var != 'data' and var != 'item':
# Replace with entry variable
new_lines.append(f'{indent}queue_data_free(entry_{var});')
else:
new_lines.append(line)
i += 1
continue
new_lines.append(line)
i += 1
# Write the updated file
with open('test_ll_queue.c', 'w') as f:
f.write('\n'.join(new_lines))
print("File updated successfully!")
Loading…
Cancel
Save