/* sc_lib.c - Secure Channel library implementation using TinyCrypt */ #include "sc_lib.h" #include #include #include #include #include #include #include #include #include #include #include #include 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; }