diff --git a/changelog.txt b/changelog.txt index b225e8a..0e9617b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -222,3 +222,4 @@ Refactoring complete - utun binary ready 2026-01-18 19:37:40: Имплементация шифрования завершена. Добавлена поддержка secure channel с AES-CCM, nonce и счетчиками. pubkey в конце INIT-пакетов. Клиенты загружают peer_public_key из конфига. 2026-01-18 20:38:35: Fixed etcp_link_new call, added etcp_socket_read_callback for uasync integration. 2026-01-18 21:20:56: Финальный отчет: Реально встроено шифрование с AES-CCM в etcp_link_send и etcp_input. Добавлены тесты. +2026-01-18 22:00:59: Fix critical bug: sc_derive_shared_key called inside sc_set_peer_public_key. Correct order for INIT: pubkey, derive, decrypt. Added peer_public_key for clients. diff --git a/src/etcp_connections.c b/src/etcp_connections.c index df67177..93db0bc 100644 --- a/src/etcp_connections.c +++ b/src/etcp_connections.c @@ -209,47 +209,43 @@ int etcp_input(struct packet_buffer* pkt, struct ETCP_SOCKET* socket, struct ETC struct sockaddr* src_addr = packet_remote_addr(pkt); socklen_t addr_len = sizeof(struct sockaddr_storage); - // Дешифруем пакет, если есть session_key + // Обработка INIT пакета: сначала pubkey, потом derive, потом decrypt sc_context_t* sc = conns->etcp->crypto_ctx; uint8_t decrypted_buf[1500]; bool was_decrypted = false; - // Извлекаем pubkey из конца только для INIT_REQUEST if (cmd == ETCP_INIT_REQUEST && data_len >= SC_PUBKEY_SIZE) { - // Сохраняем pubkey перед изменением data_len + if (!sc || !sc->initialized) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Crypto context not initialized"); + return -1; + } + + // 1. Извлекаем pubkey из конца uint8_t pubkey[SC_PUBKEY_SIZE]; memcpy(pubkey, data + data_len - SC_PUBKEY_SIZE, SC_PUBKEY_SIZE); data_len -= SC_PUBKEY_SIZE; - // Устанавливаем pubkey в secure channel - if (sc && sc->initialized) { - sc_set_peer_public_key(sc, pubkey); - } + // 2. Устанавливаем pubkey и вызываем derive // Обновляем cmd, так как data_len изменился cmd = data[0]; } + // Теперь можем дешифровать, если session_ready установлен if (sc && sc->session_ready && data_len > SC_TAG_SIZE) { size_t decrypted_len = sizeof(decrypted_buf); - // Для INIT_REQUEST пакета - pubkey в конце не шифруется - bool has_pubkey_at_end = (cmd == ETCP_INIT_REQUEST); - size_t encrypted_part_len = has_pubkey_at_end ? - data_len - SC_PUBKEY_SIZE : data_len; - - if (encrypted_part_len > SC_TAG_SIZE) { - if (sc_decrypt(sc, data, encrypted_part_len, decrypted_buf, &decrypted_len) == SC_OK) { + // Шифруется весь пакет (INIT_REQUEST без pubkey уже обрезан) + if (sc_decrypt(sc, data, data_len, decrypted_buf, &decrypted_len) == SC_OK) { data = decrypted_buf; data_len = decrypted_len; cmd = data[0]; was_decrypted = true; conns->total_decrypted++; - } else { + } else { conns->decrypt_errors++; - DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Decryption failed"); + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Decryption failed after INIT"); return -1; - } } } @@ -799,6 +795,17 @@ int init_connections(struct UTUN_INSTANCE* instance) { continue; } + // Для клиентов, у которых есть peer_public_key в конфиге - сразу вызываем derive + // Серверы получат pubkey через INIT пакет + if (strlen(client->peer_public_key_hex) > 0) { + uint8_t peer_key_bin[SC_PUBKEY_SIZE]; + if (hex_to_binary(client->peer_public_key_hex, peer_key_bin, SC_PUBKEY_SIZE) == 0) { + memcpy(link->conns->etcp->peer_public_key, peer_key_bin, SC_PUBKEY_SIZE); + link->conns->etcp->has_peer_key = 1; + sc_set_peer_public_key(link->conns->etcp->crypto_ctx, peer_key_bin); + } + } + printf(" Added client %s -> %s\n", client->name, client->to_addr); } diff --git a/src/secure_channel.h b/src/secure_channel.h index c9da1ea..d20f656 100644 --- a/src/secure_channel.h +++ b/src/secure_channel.h @@ -65,6 +65,6 @@ sc_status_t sc_decrypt(sc_context_t *ctx, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, size_t *plaintext_len); -void sc_derive_shared_key(sc_context_t *ctx); +sc_status_t sc_derive_shared_key(sc_context_t *ctx); #endif // SECURE_CHANNEL_H