You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

324 lines
9.0 KiB

/* sc_lib.c - Secure Channel library implementation using TinyCrypt */
#include "sc_lib.h"
#include <tinycrypt/ecc.h>
#include <tinycrypt/ecc_dh.h>
#include <tinycrypt/aes.h>
#include <tinycrypt/ccm_mode.h>
#include <tinycrypt/constants.h>
#include <tinycrypt/ecc_platform_specific.h>
#include <string.h>
#include <stddef.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
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);
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();
}
return uECC_valid_public_key(public_key, curve);
}
sc_status_t sc_generate_keypair(sc_context_t *ctx)
{
if (!ctx) {
return SC_ERR_INVALID_ARG;
}
if (!curve) {
curve = uECC_secp256r1();
}
/* Set custom RNG function */
uECC_set_rng(sc_rng);
if (!uECC_make_key(ctx->public_key, ctx->private_key, curve)) {
return SC_ERR_CRYPTO;
}
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_init_local_keys(sc_context_t *ctx,
const uint8_t *public_key,
const uint8_t *private_key)
{
if (!ctx || !public_key || !private_key) {
return SC_ERR_INVALID_ARG;
}
if (!curve) {
curve = uECC_secp256r1();
}
/* Validate public key */
if (sc_validate_key(public_key) != 0) {
return SC_ERR_INVALID_ARG;
}
memcpy(ctx->public_key, public_key, SC_PUBKEY_SIZE);
memcpy(ctx->private_key, private_key, SC_PRIVKEY_SIZE);
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 uint8_t *peer_public_key)
{
uint8_t shared_secret[SC_SHARED_SECRET_SIZE];
if (!ctx || !peer_public_key) {
return SC_ERR_INVALID_ARG;
}
if (!ctx->initialized) {
return SC_ERR_NOT_INITIALIZED;
}
if (!curve) {
curve = uECC_secp256r1();
}
/* Validate peer public key */
if (sc_validate_key(peer_public_key) != 0) {
return SC_ERR_INVALID_ARG;
}
/* Compute shared secret using ECDH */
if (!uECC_shared_secret(peer_public_key, ctx->private_key,
shared_secret, curve)) {
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(uint32_t counter, uint8_t *nonce_out)
{
/* Simple nonce construction: counter in little-endian + zeros */
memset(nonce_out, 0, SC_NONCE_SIZE);
nonce_out[0] = (counter >> 0) & 0xFF;
nonce_out[1] = (counter >> 8) & 0xFF;
nonce_out[2] = (counter >> 16) & 0xFF;
nonce_out[3] = (counter >> 24) & 0xFF;
}
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;
TCCcmMode_t c = &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(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(c, &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,
c) != 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(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;
}