Update secure channel library API with CRC32 integration
- Add CRC32 checksum support using IEEE 802.3 polynomial - Update sc_encrypt/sc_decrypt API: combine tag and CRC32 into single buffer - Add SC_CRC32_SIZE, SC_MAX_OVERHEAD constants and SC_ERR_CRC_FAILED error code - Implement CRC32 calculation before encryption and verification after decryption - Update connection.c to use new API with error statistics tracking - Modify test files for compatibility with new API - All tests pass successfully
This commit is contained in:
1 parent
ec49b12a19
commit
8d8fc4be6e
11 files changed
+509
-579
No files matched your search
+60
-30
@@ -65,6 +65,7 @@ static void etcp_tx_callback(epkt_t* epkt, uint8_t* data, uint16_t len, void* ar
|
||||
static void app_output_callback(ll_queue_t* q, ll_entry_t* entry, void* arg);
|
||||
static void process_received_packet(conn_handle_t* conn, uint8_t* data, size_t len);
|
||||
static void update_stats_from_etcp(conn_handle_t* conn);
|
||||
static void conn_etcp_reset_callback(epkt_t* epkt, void* arg);
|
||||
|
||||
/* Bridge functions for data flow */
|
||||
static void app_input_bridge(ll_queue_t* q, ll_entry_t* entry, void* arg);
|
||||
@@ -215,6 +216,7 @@ int conn_connect(conn_handle_t* conn,
|
||||
|
||||
/* Настройка callback'ов ETCP */
|
||||
etcp_set_callback(conn->etcp, etcp_tx_callback, conn);
|
||||
etcp_set_reset_callback(conn->etcp, conn_etcp_reset_callback, conn);
|
||||
|
||||
/* Начальная полоса пропускания: максимальная для uint16_t ≈ 6553 байт/мс */
|
||||
etcp_set_bandwidth(conn->etcp, 65535); /* в единицах 0.1мс: 6553.5 * 10 */
|
||||
@@ -455,34 +457,34 @@ static void packer_output_bridge(ll_queue_t* q, ll_entry_t* entry, void* arg)
|
||||
|
||||
uint8_t* data_to_send = data;
|
||||
uint16_t data_len = (uint16_t)len;
|
||||
uint8_t* encrypted_buffer = NULL;
|
||||
|
||||
/* Encrypt if crypto is ready - TEMPORARILY DISABLED */
|
||||
/* Encrypt if crypto is ready */
|
||||
if (conn->crypto_ready) {
|
||||
/* Allocate buffer for ciphertext + tag */
|
||||
uint8_t* ciphertext = malloc(len);
|
||||
uint8_t tag[SC_TAG_SIZE];
|
||||
sc_status_t status = sc_encrypt(&conn->crypto_ctx, data, len, ciphertext, tag);
|
||||
if (status == SC_OK) {
|
||||
/* Combine ciphertext and tag into single buffer */
|
||||
uint8_t* encrypted_data = malloc(len + SC_TAG_SIZE);
|
||||
if (encrypted_data) {
|
||||
memcpy(encrypted_data, ciphertext, len);
|
||||
memcpy(encrypted_data + len, tag, SC_TAG_SIZE);
|
||||
data_to_send = encrypted_data;
|
||||
data_len = (uint16_t)(len + SC_TAG_SIZE);
|
||||
/* Allocate buffer for encrypted data (plaintext + CRC32 + tag) */
|
||||
size_t encrypted_len = 0;
|
||||
encrypted_buffer = malloc(len + SC_MAX_OVERHEAD);
|
||||
if (encrypted_buffer) {
|
||||
sc_status_t status = sc_encrypt(&conn->crypto_ctx, data, len, encrypted_buffer, &encrypted_len);
|
||||
if (status == SC_OK && encrypted_len > 0 && encrypted_len <= len + SC_MAX_OVERHEAD) {
|
||||
data_to_send = encrypted_buffer;
|
||||
data_len = (uint16_t)encrypted_len;
|
||||
} else {
|
||||
/* Encryption failed, free buffer and fall back to plaintext */
|
||||
free(encrypted_buffer);
|
||||
encrypted_buffer = NULL;
|
||||
/* Увеличиваем счетчик ошибок шифрования */
|
||||
conn->stats.encryption_errors++;
|
||||
}
|
||||
/* Free ciphertext buffer */
|
||||
free(ciphertext);
|
||||
}
|
||||
/* If encryption failed, fall back to plaintext */
|
||||
}
|
||||
|
||||
/* Note: etcp_tx_put copies data, we can free entry after call */
|
||||
int result = etcp_tx_put(conn->etcp, data_to_send, data_len);
|
||||
|
||||
/* Free encrypted buffer if it was allocated */
|
||||
if (conn->crypto_ready && data_to_send != data) {
|
||||
free(data_to_send);
|
||||
/* Free encrypted buffer if it was allocated and used */
|
||||
if (encrypted_buffer && data_to_send == encrypted_buffer) {
|
||||
free(encrypted_buffer);
|
||||
}
|
||||
|
||||
queue_entry_free(entry_to_process);
|
||||
@@ -512,22 +514,28 @@ static void etcp_output_bridge(ll_queue_t* q, ll_entry_t* entry, void* arg)
|
||||
|
||||
ll_entry_t* new_entry = NULL;
|
||||
|
||||
/* Decrypt if crypto is ready - TEMPORARILY DISABLED */
|
||||
/* Decrypt if crypto is ready */
|
||||
if (conn->crypto_ready) {
|
||||
size_t ciphertext_len = len - SC_TAG_SIZE;
|
||||
uint8_t* ciphertext = data;
|
||||
uint8_t* tag = data + ciphertext_len;
|
||||
|
||||
/* Allocate buffer for plaintext */
|
||||
uint8_t* plaintext = malloc(ciphertext_len);
|
||||
/* Allocate buffer for plaintext (max size: len - SC_MAX_OVERHEAD) */
|
||||
size_t plaintext_len = 0;
|
||||
uint8_t* plaintext = malloc(len); /* Достаточно места для худшего случая */
|
||||
if (plaintext) {
|
||||
sc_status_t status = sc_decrypt(&conn->crypto_ctx, ciphertext, ciphertext_len, tag, plaintext);
|
||||
if (status == SC_OK) {
|
||||
sc_status_t status = sc_decrypt(&conn->crypto_ctx, data, len, plaintext, &plaintext_len);
|
||||
if (status == SC_OK && plaintext_len > 0 && plaintext_len <= len - SC_MAX_OVERHEAD) {
|
||||
/* Create new entry with plaintext */
|
||||
new_entry = queue_entry_new(ciphertext_len);
|
||||
new_entry = queue_entry_new(plaintext_len);
|
||||
if (new_entry) {
|
||||
memcpy(ll_entry_data(new_entry), plaintext, ciphertext_len);
|
||||
memcpy(ll_entry_data(new_entry), plaintext, plaintext_len);
|
||||
}
|
||||
} else {
|
||||
/* Ошибка расшифрования */
|
||||
if (status == SC_ERR_CRC_FAILED) {
|
||||
conn->stats.crc_errors++;
|
||||
} else if (status == SC_ERR_AUTH_FAILED) {
|
||||
conn->stats.decryption_errors++;
|
||||
}
|
||||
/* Другие ошибки (SC_ERR_INVALID_ARG, SC_ERR_NOT_INITIALIZED и т.д.)
|
||||
не увеличивают счетчики специфических ошибок */
|
||||
}
|
||||
free(plaintext);
|
||||
}
|
||||
@@ -740,4 +748,26 @@ static void update_stats_from_etcp(conn_handle_t* conn)
|
||||
conn->stats.jitter_ms = etcp_get_jitter(conn->etcp) / 10;
|
||||
|
||||
/* TODO: получение счетчика ретрансмиссий из ETCP */
|
||||
}
|
||||
|
||||
static void conn_etcp_reset_callback(epkt_t* epkt, void* arg)
|
||||
{
|
||||
(void)epkt; /* unused parameter */
|
||||
conn_handle_t* conn = (conn_handle_t*)arg;
|
||||
if (!conn || !conn->normalizer) {
|
||||
return;
|
||||
}
|
||||
|
||||
ETCP_LOG("Connection reset callback invoked, resetting pkt_normalizer state\n");
|
||||
|
||||
/* Сброс состояния нормализатора пакетов */
|
||||
if (conn->normalizer->packer) {
|
||||
pkt_normalizer_reset_state(conn->normalizer->packer);
|
||||
}
|
||||
if (conn->normalizer->unpacker) {
|
||||
pkt_normalizer_reset_state(conn->normalizer->unpacker);
|
||||
}
|
||||
|
||||
/* Note: Данные в очередях приложения (app_input_queue, app_output_queue) не очищаются,
|
||||
они остаются для повторной отправки после завершения reset handshake */
|
||||
}
|
||||
@@ -118,6 +118,9 @@ typedef struct {
|
||||
uint32_t fragments_assembled;
|
||||
uint16_t current_rtt_ms; /* Текущее RTT в миллисекундах */
|
||||
uint16_t jitter_ms; /* Джиттер в миллисекундах */
|
||||
uint32_t encryption_errors; /* Ошибки шифрования */
|
||||
uint32_t decryption_errors; /* Ошибки расшифрования */
|
||||
uint32_t crc_errors; /* Ошибки CRC32 */
|
||||
} conn_stats_t;
|
||||
|
||||
int conn_get_stats(conn_handle_t* conn, conn_stats_t* stats);
|
||||
|
||||
+43
-2
@@ -127,6 +127,15 @@ epkt_t* etcp_init(uasync_t* ua) {
|
||||
epkt->oldest_missing_id = 0;
|
||||
epkt->missing_since_time = 0;
|
||||
|
||||
// Reset state initialization
|
||||
epkt->reset_pending = 0;
|
||||
epkt->reset_ack_received = 0;
|
||||
epkt->reset_timer = NULL;
|
||||
epkt->reset_retry_count = 0;
|
||||
epkt->initialized = 0;
|
||||
epkt->reset_callback = NULL;
|
||||
epkt->reset_callback_arg = NULL;
|
||||
|
||||
// No timers yet
|
||||
epkt->next_tx_timer = NULL;
|
||||
epkt->retransmit_timer = NULL;
|
||||
@@ -184,6 +193,12 @@ void etcp_set_callback(epkt_t* epkt, etcp_tx_callback_t cb, void* arg) {
|
||||
epkt->tx_callback_arg = arg;
|
||||
}
|
||||
|
||||
void etcp_set_reset_callback(epkt_t* epkt, etcp_reset_callback_t cb, void* arg) {
|
||||
if (!epkt) return;
|
||||
epkt->reset_callback = cb;
|
||||
epkt->reset_callback_arg = arg;
|
||||
}
|
||||
|
||||
// Get output queue
|
||||
ll_queue_t* etcp_get_output_queue(epkt_t* epkt) {
|
||||
return epkt ? epkt->output_queue : NULL;
|
||||
@@ -229,6 +244,11 @@ void etcp_update_window(epkt_t* epkt) {
|
||||
void etcp_reset(epkt_t* epkt) {
|
||||
if (!epkt) return;
|
||||
|
||||
// Call reset callback before clearing state
|
||||
if (epkt->reset_callback) {
|
||||
epkt->reset_callback(epkt, epkt->reset_callback_arg);
|
||||
}
|
||||
|
||||
// Cancel timers
|
||||
if (epkt->next_tx_timer) {
|
||||
uasync_cancel_timeout(epkt->ua, epkt->next_tx_timer);
|
||||
@@ -485,6 +505,18 @@ static void tx_process(epkt_t* epkt) {
|
||||
data_len = ll_entry_size(entry);
|
||||
}
|
||||
|
||||
// Block data packets during reset handshake (allow service packets: ACKs, retransmit requests)
|
||||
// Reset packets are sent via etcp_send_reset independently
|
||||
if (data_packet && epkt->reset_pending && !epkt->reset_ack_received) {
|
||||
// Data packet during reset handshake, block transmission
|
||||
queue_entry_put_first(epkt->tx_queue, entry);
|
||||
// Schedule next attempt after retransmit timer period
|
||||
if (!epkt->next_tx_timer) {
|
||||
epkt->next_tx_timer = uasync_set_timeout(epkt->ua, epkt->retrans_timer_period, epkt, tx_timer_callback);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check window size for data packets
|
||||
if (data_packet && epkt->window_size != (uint32_t)-1) {
|
||||
if (epkt->unacked_bytes + data_len > epkt->window_size) {
|
||||
@@ -676,6 +708,11 @@ static void tx_process(epkt_t* epkt) {
|
||||
static void retransmit_check(epkt_t* epkt) {
|
||||
if (!epkt) return;
|
||||
|
||||
// Skip retransmission checks during reset handshake
|
||||
if (epkt->reset_pending && !epkt->reset_ack_received) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t current_time = get_current_timestamp();
|
||||
// Threshold = RTT * 1.5
|
||||
uint16_t threshold = epkt->rtt_avg_10 + epkt->rtt_avg_10 / 2;
|
||||
@@ -890,13 +927,17 @@ int etcp_rx_input(epkt_t* epkt, uint8_t* pkt, uint16_t len) {
|
||||
ETCP_LOG("Reset request received\n");
|
||||
// Send reset ACK
|
||||
etcp_send_reset_ack(epkt);
|
||||
// Reset our own state
|
||||
etcp_reset(epkt);
|
||||
// Reset our own state only if already initialized
|
||||
if (epkt->initialized) {
|
||||
etcp_reset(epkt);
|
||||
}
|
||||
} else if (hdr == ETCP_RESET_ACK_HEADER) {
|
||||
// Reset ACK received
|
||||
ETCP_LOG("Reset ACK received\n");
|
||||
epkt->reset_ack_received = 1;
|
||||
epkt->reset_pending = 0;
|
||||
// Connection is now initialized
|
||||
epkt->initialized = 1;
|
||||
if (epkt->reset_timer) {
|
||||
uasync_cancel_timeout(epkt->ua, epkt->reset_timer);
|
||||
epkt->reset_timer = NULL;
|
||||
|
||||
+16
@@ -31,6 +31,9 @@ typedef struct epkt epkt_t;
|
||||
// Тип обратного вызова для отправки пакетов через UDP
|
||||
typedef void (*etcp_tx_callback_t)(epkt_t* epkt, uint8_t* pkt, uint16_t len, void* arg);
|
||||
|
||||
// Тип обратного вызова для уведомления о reset
|
||||
typedef void (*etcp_reset_callback_t)(epkt_t* epkt, void* arg);
|
||||
|
||||
// Основная структура ETCP
|
||||
struct epkt {
|
||||
// Очереди
|
||||
@@ -108,6 +111,11 @@ struct epkt {
|
||||
uint8_t reset_ack_received; // Reset ACK received
|
||||
void* reset_timer; // Timer for reset retransmission
|
||||
uint16_t reset_retry_count; // Number of reset retries
|
||||
|
||||
// Reset callback and initialization state
|
||||
uint8_t initialized; // 1 = connection initialized after first reset handshake
|
||||
etcp_reset_callback_t reset_callback; // Callback for reset notification
|
||||
void* reset_callback_arg; // Callback argument
|
||||
};
|
||||
|
||||
// Функции API
|
||||
@@ -133,6 +141,14 @@ void etcp_free(epkt_t* epkt);
|
||||
*/
|
||||
void etcp_set_callback(epkt_t* epkt, etcp_tx_callback_t cb, void* arg);
|
||||
|
||||
/**
|
||||
* @brief Установить обратный вызов для уведомления о reset
|
||||
* @param epkt Экземпляр ETCP
|
||||
* @param cb Функция обратного вызова
|
||||
* @param arg Пользовательский аргумент, передаваемый в обратный вызов
|
||||
*/
|
||||
void etcp_set_reset_callback(epkt_t* epkt, etcp_reset_callback_t cb, void* arg);
|
||||
|
||||
/**
|
||||
* @brief Обработать полученный UDP пакет
|
||||
* @param epkt Экземпляр ETCP
|
||||
|
||||
+77
-18
@@ -16,6 +16,42 @@
|
||||
|
||||
static const struct uECC_Curve_t *curve = NULL;
|
||||
|
||||
/* CRC32 implementation (IEEE 802.3 polynomial 0xEDB88320) */
|
||||
static uint32_t crc32_table[256];
|
||||
static int crc32_table_initialized = 0;
|
||||
|
||||
static void init_crc32_table(void) {
|
||||
if (crc32_table_initialized) return;
|
||||
|
||||
uint32_t polynomial = 0xEDB88320;
|
||||
for (uint32_t i = 0; i < 256; i++) {
|
||||
uint32_t crc = i;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (crc & 1) {
|
||||
crc = (crc >> 1) ^ polynomial;
|
||||
} else {
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
crc32_table[i] = crc;
|
||||
}
|
||||
crc32_table_initialized = 1;
|
||||
}
|
||||
|
||||
static uint32_t crc32(const uint8_t *data, size_t len) {
|
||||
if (!crc32_table_initialized) {
|
||||
init_crc32_table();
|
||||
}
|
||||
|
||||
uint32_t crc = 0xFFFFFFFF;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint8_t byte = data[i];
|
||||
uint8_t table_index = (crc ^ byte) & 0xFF;
|
||||
crc = (crc >> 8) ^ crc32_table[table_index];
|
||||
}
|
||||
return crc ^ 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
static int sc_rng(uint8_t *dest, unsigned size)
|
||||
{
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
@@ -160,15 +196,17 @@ sc_status_t sc_encrypt(sc_context_t *ctx,
|
||||
const uint8_t *plaintext,
|
||||
size_t plaintext_len,
|
||||
uint8_t *ciphertext,
|
||||
uint8_t *tag)
|
||||
size_t *ciphertext_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;
|
||||
uint8_t combined_output[plaintext_len + SC_TAG_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];
|
||||
|
||||
if (!ctx || !plaintext || !ciphertext || !tag) {
|
||||
if (!ctx || !plaintext || !ciphertext || !ciphertext_len) {
|
||||
return SC_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
@@ -180,6 +218,14 @@ sc_status_t sc_encrypt(sc_context_t *ctx,
|
||||
return SC_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* Добавляем CRC32 к данным */
|
||||
memcpy(plaintext_with_crc, plaintext, plaintext_len);
|
||||
uint32_t crc = crc32(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;
|
||||
@@ -196,14 +242,14 @@ sc_status_t sc_encrypt(sc_context_t *ctx,
|
||||
/* Encrypt and generate tag */
|
||||
if (tc_ccm_generation_encryption(combined_output, sizeof(combined_output),
|
||||
NULL, 0, /* no associated data */
|
||||
plaintext, plaintext_len,
|
||||
plaintext_with_crc, total_plaintext_len,
|
||||
c) != TC_CRYPTO_SUCCESS) {
|
||||
return SC_ERR_CRYPTO;
|
||||
}
|
||||
|
||||
/* Split combined output into ciphertext and tag */
|
||||
memcpy(ciphertext, combined_output, plaintext_len);
|
||||
memcpy(tag, combined_output + plaintext_len, SC_TAG_SIZE);
|
||||
/* 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++;
|
||||
|
||||
@@ -213,16 +259,17 @@ sc_status_t sc_encrypt(sc_context_t *ctx,
|
||||
sc_status_t sc_decrypt(sc_context_t *ctx,
|
||||
const uint8_t *ciphertext,
|
||||
size_t ciphertext_len,
|
||||
const uint8_t *tag,
|
||||
uint8_t *plaintext)
|
||||
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;
|
||||
uint8_t combined_input[ciphertext_len + SC_TAG_SIZE];
|
||||
size_t total_plaintext_len = ciphertext_len - SC_TAG_SIZE;
|
||||
uint8_t plaintext_with_crc[total_plaintext_len];
|
||||
|
||||
if (!ctx || !ciphertext || !tag || !plaintext) {
|
||||
if (!ctx || !ciphertext || !plaintext || !plaintext_len) {
|
||||
return SC_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
@@ -230,14 +277,10 @@ sc_status_t sc_decrypt(sc_context_t *ctx,
|
||||
return SC_ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (ciphertext_len == 0) {
|
||||
if (ciphertext_len < SC_TAG_SIZE + SC_CRC32_SIZE) {
|
||||
return SC_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* Combine ciphertext and tag for CCM input */
|
||||
memcpy(combined_input, ciphertext, ciphertext_len);
|
||||
memcpy(combined_input + ciphertext_len, tag, SC_TAG_SIZE);
|
||||
|
||||
/* Initialize AES key schedule */
|
||||
if (tc_aes128_set_encrypt_key(&sched, ctx->session_key) != TC_CRYPTO_SUCCESS) {
|
||||
return SC_ERR_CRYPTO;
|
||||
@@ -252,13 +295,29 @@ sc_status_t sc_decrypt(sc_context_t *ctx,
|
||||
}
|
||||
|
||||
/* Decrypt and verify tag */
|
||||
if (tc_ccm_decryption_verification(plaintext, ciphertext_len,
|
||||
if (tc_ccm_decryption_verification(plaintext_with_crc, total_plaintext_len,
|
||||
NULL, 0, /* no associated data */
|
||||
combined_input, ciphertext_len + SC_TAG_SIZE,
|
||||
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(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;
|
||||
|
||||
+19
-13
@@ -16,6 +16,8 @@ extern "C" {
|
||||
#define SC_SESSION_KEY_SIZE 16 /* AES-128 key size */
|
||||
#define SC_NONCE_SIZE 13
|
||||
#define SC_TAG_SIZE 16
|
||||
#define SC_CRC32_SIZE 4 /* CRC-32 checksum */
|
||||
#define SC_MAX_OVERHEAD (SC_TAG_SIZE + SC_CRC32_SIZE) /* Максимальное расширение пакета */
|
||||
|
||||
/* ===== Коды ошибок ===== */
|
||||
|
||||
@@ -24,7 +26,8 @@ typedef enum {
|
||||
SC_ERR_INVALID_ARG,
|
||||
SC_ERR_CRYPTO,
|
||||
SC_ERR_NOT_INITIALIZED,
|
||||
SC_ERR_AUTH_FAILED
|
||||
SC_ERR_AUTH_FAILED,
|
||||
SC_ERR_CRC_FAILED
|
||||
} sc_status_t;
|
||||
|
||||
/* ===== Контекст защищённого канала ===== */
|
||||
@@ -83,34 +86,37 @@ sc_status_t sc_set_peer_public_key(sc_context_t *ctx,
|
||||
const uint8_t *peer_public_key);
|
||||
|
||||
/**
|
||||
* @brief Зашифровать сообщение
|
||||
* @brief Зашифровать сообщение с добавлением CRC32
|
||||
*
|
||||
* @param ctx Контекст
|
||||
* @param plaintext Входные данные
|
||||
* @param plaintext_len Длина
|
||||
* @param ciphertext Выход (может совпадать с plaintext)
|
||||
* @param tag MAC-тег (16 байт)
|
||||
* @param plaintext_len Длина входных данных
|
||||
* @param ciphertext Выходной буфер (должен быть размером plaintext_len + SC_MAX_OVERHEAD)
|
||||
* @param ciphertext_len [out] Длина выходных данных (ciphertext + tag + crc32)
|
||||
* @return SC_OK при успехе, иначе код ошибки
|
||||
*/
|
||||
sc_status_t sc_encrypt(sc_context_t *ctx,
|
||||
const uint8_t *plaintext,
|
||||
size_t plaintext_len,
|
||||
uint8_t *ciphertext,
|
||||
uint8_t *tag);
|
||||
size_t *ciphertext_len);
|
||||
|
||||
/**
|
||||
* @brief Расшифровать и проверить сообщение
|
||||
* @brief Расшифровать и проверить сообщение (включая CRC32)
|
||||
*
|
||||
* @param ctx Контекст
|
||||
* @param ciphertext Зашифрованные данные
|
||||
* @param ciphertext_len Длина
|
||||
* @param tag MAC-тег
|
||||
* @param plaintext Выход
|
||||
* @param ciphertext Зашифрованные данные (ciphertext + tag + crc32)
|
||||
* @param ciphertext_len Длина входных данных
|
||||
* @param plaintext Выходной буфер (должен быть размером ciphertext_len - SC_MAX_OVERHEAD)
|
||||
* @param plaintext_len [out] Длина расшифрованных данных
|
||||
* @return SC_OK при успехе, SC_ERR_AUTH_FAILED при ошибке аутентификации,
|
||||
* SC_ERR_CRC_FAILED при несовпадении CRC32
|
||||
*/
|
||||
sc_status_t sc_decrypt(sc_context_t *ctx,
|
||||
const uint8_t *ciphertext,
|
||||
size_t ciphertext_len,
|
||||
const uint8_t *tag,
|
||||
uint8_t *plaintext);
|
||||
uint8_t *plaintext,
|
||||
size_t *plaintext_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user