/* secure_channel.c - Secure Channel library implementation using TinyCrypt or OpenSSL */ #ifdef HAVE_CONFIG_H #include #endif #include "secure_channel.h" #include "../lib/debug_config.h" #include #include #include #include #include #include #include #include "crc32.h" #include "../lib/sha256.h" // To switch between implementations, define USE_OPENSSL before including/compiling. // If USE_OPENSSL is defined, use OpenSSL; otherwise, use TinyCrypt (original logic). // The core logic (e.g., nonce building, CRC, session key derivation as memcpy, counters, etc.) remains unchanged. #ifdef USE_OPENSSL #include #include #include #include #include #else #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" #endif 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; } } } // Конвертация hex строки в бинарный формат (common) 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_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; } #ifdef USE_OPENSSL // OpenSSL-specific implementations static int sc_rng(uint8_t *dest, unsigned size) { // OpenSSL has its own RNG, but for consistency with original, mix urandom + pid + time 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; } 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) { EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); if (!group) return -1; EC_POINT *point = EC_POINT_new(group); if (!point) { EC_GROUP_free(group); return -1; } BIGNUM *x = BN_bin2bn(public_key, 32, NULL); BIGNUM *y = BN_bin2bn(public_key + 32, 32, NULL); if (!x || !y || EC_POINT_set_affine_coordinates(group, point, x, y, NULL) != 1) { BN_free(x); BN_free(y); EC_POINT_free(point); EC_GROUP_free(group); return -1; } int result = EC_POINT_is_on_curve(group, point, NULL); BN_free(x); BN_free(y); EC_POINT_free(point); EC_GROUP_free(group); // To match TinyCrypt return convention in the provided code (0 valid, !=0 invalid) return (result == 1) ? 0 : -1; } sc_status_t sc_generate_keypair(struct SC_MYKEYS *pk) { if (!pk) { return SC_ERR_INVALID_ARG; } EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); if (!group) return SC_ERR_CRYPTO; EC_KEY *key = EC_KEY_new(); if (!key) { EC_GROUP_free(group); return SC_ERR_CRYPTO; } if (EC_KEY_set_group(key, group) != 1) { EC_KEY_free(key); EC_GROUP_free(group); return SC_ERR_CRYPTO; } // Use custom RNG if needed, but OpenSSL RAND is fine; for consistency, seed if necessary if (EC_KEY_generate_key(key) != 1) { EC_KEY_free(key); EC_GROUP_free(group); return SC_ERR_CRYPTO; } const BIGNUM *priv = EC_KEY_get0_private_key(key); if (BN_bn2binpad(priv, pk->private_key, SC_PRIVKEY_SIZE) != SC_PRIVKEY_SIZE) { EC_KEY_free(key); EC_GROUP_free(group); return SC_ERR_CRYPTO; } const EC_POINT *pub_point = EC_KEY_get0_public_key(key); uint8_t pub_buf[65]; if (EC_POINT_point2oct(group, pub_point, POINT_CONVERSION_UNCOMPRESSED, pub_buf, 65, NULL) != 65) { EC_KEY_free(key); EC_GROUP_free(group); return SC_ERR_CRYPTO; } memcpy(pk->public_key, pub_buf + 1, SC_PUBKEY_SIZE); // Skip 0x04 prefix EC_KEY_free(key); EC_GROUP_free(group); return SC_OK; } 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; } DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: public_key len=%zu, private_key len=%zu", strlen(public_key), strlen(private_key)); 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; } 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_set_peer_public_key(sc_context_t *ctx, const char *peer_public_key_h, int mode) { 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, (const uint8_t *)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 (sc_validate_key(peer_public_key) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: invalid key"); return SC_ERR_INVALID_ARG; } if (!ctx->pk) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: no private key"); return SC_ERR_NOT_INITIALIZED; } EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); if (!group) return SC_ERR_CRYPTO; EC_KEY *my_key = EC_KEY_new(); if (!my_key) { EC_GROUP_free(group); return SC_ERR_CRYPTO; } if (EC_KEY_set_group(my_key, group) != 1) { EC_KEY_free(my_key); EC_GROUP_free(group); return SC_ERR_CRYPTO; } BIGNUM *my_priv = BN_bin2bn(ctx->pk->private_key, SC_PRIVKEY_SIZE, NULL); if (!my_priv || EC_KEY_set_private_key(my_key, my_priv) != 1) { BN_free(my_priv); EC_KEY_free(my_key); EC_GROUP_free(group); return SC_ERR_CRYPTO; } EC_POINT *peer_point = EC_POINT_new(group); if (!peer_point) { BN_free(my_priv); EC_KEY_free(my_key); EC_GROUP_free(group); return SC_ERR_CRYPTO; } BIGNUM *x = BN_bin2bn(peer_public_key, 32, NULL); BIGNUM *y = BN_bin2bn(peer_public_key + 32, 32, NULL); if (!x || !y || EC_POINT_set_affine_coordinates(group, peer_point, x, y, NULL) != 1) { BN_free(x); BN_free(y); BN_free(my_priv); EC_POINT_free(peer_point); EC_KEY_free(my_key); EC_GROUP_free(group); return SC_ERR_CRYPTO; } uint8_t shared_secret[SC_SHARED_SECRET_SIZE]; int len = ECDH_compute_key(shared_secret, SC_SHARED_SECRET_SIZE, peer_point, my_key, NULL); if (len != SC_SHARED_SECRET_SIZE) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: shared secret error"); BN_free(x); BN_free(y); BN_free(my_priv); EC_POINT_free(peer_point); EC_KEY_free(my_key); EC_GROUP_free(group); return SC_ERR_CRYPTO; } memcpy(ctx->session_key, shared_secret, SC_SESSION_KEY_SIZE); memcpy(ctx->peer_public_key, peer_public_key, SC_PUBKEY_SIZE); ctx->peer_key_set = 1; ctx->session_ready = 1; BN_free(x); BN_free(y); BN_free(my_priv); EC_POINT_free(peer_point); EC_KEY_free(my_key); EC_GROUP_free(group); return SC_OK; } static void sc_build_nonce(uint64_t counter, uint8_t *nonce_out) { SHA256_CTX sha_ctx; uint8_t hash[32]; struct timeval tv; uint8_t data[24]; 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; 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; SHA256_Init(&sha_ctx); SHA256_Update(&sha_ctx, data, 24); 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) { 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; } uint8_t plaintext_with_crc[plaintext_len + SC_CRC32_SIZE]; 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; size_t total_plaintext_len = plaintext_len + SC_CRC32_SIZE; uint8_t nonce[SC_NONCE_SIZE]; sc_build_nonce(ctx->tx_counter, nonce); EVP_CIPHER_CTX *ectx = EVP_CIPHER_CTX_new(); if (!ectx) return SC_ERR_CRYPTO; if (EVP_EncryptInit_ex(ectx, EVP_aes_128_ccm(), NULL, NULL, NULL) != 1) { EVP_CIPHER_CTX_free(ectx); return SC_ERR_CRYPTO; } if (EVP_CIPHER_CTX_ctrl(ectx, EVP_CTRL_AEAD_SET_IVLEN, SC_NONCE_SIZE, NULL) != 1) { EVP_CIPHER_CTX_free(ectx); return SC_ERR_CRYPTO; } if (EVP_CIPHER_CTX_ctrl(ectx, EVP_CTRL_AEAD_SET_TAG, SC_TAG_SIZE, NULL) != 1) { EVP_CIPHER_CTX_free(ectx); return SC_ERR_CRYPTO; } if (EVP_EncryptInit_ex(ectx, NULL, NULL, ctx->session_key, nonce) != 1) { EVP_CIPHER_CTX_free(ectx); return SC_ERR_CRYPTO; } int outlen; uint8_t outbuf[total_plaintext_len]; if (EVP_EncryptUpdate(ectx, outbuf, &outlen, plaintext_with_crc, total_plaintext_len) != 1 || outlen != (int)total_plaintext_len) { EVP_CIPHER_CTX_free(ectx); return SC_ERR_CRYPTO; } int tmp; if (EVP_EncryptFinal_ex(ectx, outbuf + outlen, &tmp) != 1) { EVP_CIPHER_CTX_free(ectx); return SC_ERR_CRYPTO; } uint8_t tag[SC_TAG_SIZE]; if (EVP_CIPHER_CTX_ctrl(ectx, EVP_CTRL_AEAD_GET_TAG, SC_TAG_SIZE, tag) != 1) { EVP_CIPHER_CTX_free(ectx); return SC_ERR_CRYPTO; } memcpy(ciphertext, nonce, SC_NONCE_SIZE); memcpy(ciphertext + SC_NONCE_SIZE, outbuf, total_plaintext_len); memcpy(ciphertext + SC_NONCE_SIZE + total_plaintext_len, tag, SC_TAG_SIZE); *ciphertext_len = SC_NONCE_SIZE + total_plaintext_len + SC_TAG_SIZE; ctx->tx_counter++; EVP_CIPHER_CTX_free(ectx); 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) { 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; } uint8_t nonce[SC_NONCE_SIZE]; memcpy(nonce, ciphertext, SC_NONCE_SIZE); const uint8_t *encrypted_data = ciphertext + SC_NONCE_SIZE; size_t encrypted_len = ciphertext_len - SC_NONCE_SIZE; size_t total_plaintext_len = encrypted_len - SC_TAG_SIZE; uint8_t plaintext_with_crc[total_plaintext_len]; EVP_CIPHER_CTX *dctx = EVP_CIPHER_CTX_new(); if (!dctx) return SC_ERR_CRYPTO; if (EVP_DecryptInit_ex(dctx, EVP_aes_128_ccm(), NULL, NULL, NULL) != 1) { EVP_CIPHER_CTX_free(dctx); return SC_ERR_CRYPTO; } if (EVP_CIPHER_CTX_ctrl(dctx, EVP_CTRL_AEAD_SET_IVLEN, SC_NONCE_SIZE, NULL) != 1) { EVP_CIPHER_CTX_free(dctx); return SC_ERR_CRYPTO; } if (EVP_CIPHER_CTX_ctrl(dctx, EVP_CTRL_AEAD_SET_TAG, SC_TAG_SIZE, (void *)(encrypted_data + total_plaintext_len)) != 1) { EVP_CIPHER_CTX_free(dctx); return SC_ERR_CRYPTO; } if (EVP_DecryptInit_ex(dctx, NULL, NULL, ctx->session_key, nonce) != 1) { EVP_CIPHER_CTX_free(dctx); return SC_ERR_CRYPTO; } int outlen; if (EVP_DecryptUpdate(dctx, plaintext_with_crc, &outlen, encrypted_data, total_plaintext_len) != 1 || outlen != (int)total_plaintext_len) { EVP_CIPHER_CTX_free(dctx); return SC_ERR_AUTH_FAILED; } int tmp; if (EVP_DecryptFinal_ex(dctx, plaintext_with_crc + outlen, &tmp) != 1) { EVP_CIPHER_CTX_free(dctx); return SC_ERR_AUTH_FAILED; } EVP_CIPHER_CTX_free(dctx); 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; } 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; } EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); if (!group) return SC_ERR_CRYPTO; BIGNUM *priv = BN_bin2bn(private_key, SC_PRIVKEY_SIZE, NULL); if (!priv) { EC_GROUP_free(group); return SC_ERR_CRYPTO; } EC_POINT *pub_point = EC_POINT_new(group); if (!pub_point) { BN_free(priv); EC_GROUP_free(group); return SC_ERR_CRYPTO; } if (EC_POINT_mul(group, pub_point, priv, NULL, NULL, NULL) != 1) { EC_POINT_free(pub_point); BN_free(priv); EC_GROUP_free(group); return SC_ERR_CRYPTO; } uint8_t pub_buf[65]; if (EC_POINT_point2oct(group, pub_point, POINT_CONVERSION_UNCOMPRESSED, pub_buf, 65, NULL) != 65) { EC_POINT_free(pub_point); BN_free(priv); EC_GROUP_free(group); return SC_ERR_CRYPTO; } memcpy(public_key, pub_buf + 1, SC_PUBKEY_SIZE); EC_POINT_free(pub_point); BN_free(priv); EC_GROUP_free(group); return SC_OK; } #else // Original TinyCrypt implementations (unchanged logic) static const struct uECC_Curve_t *curve = NULL; 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); /* Try to generate valid key pair (max 10 attempts) */ for (int attempt = 0; attempt < 10; attempt++) { if (!uECC_make_key(pk->public_key, pk->private_key, curve)) { continue; } /* Validate generated public key immediately */ if (sc_validate_key(pk->public_key) == 0) { DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_generate_keypair: generated valid keypair on attempt %d", attempt + 1); return SC_OK; } DEBUG_WARN(DEBUG_CATEGORY_CRYPTO, "sc_generate_keypair: generated invalid key on attempt %d, retrying...", attempt + 1); } DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_generate_keypair: failed to generate valid keypair after 10 attempts"); return SC_ERR_CRYPTO; } 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_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 + 8]; 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; 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_update(&sha_ctx, data, 24); 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]; 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 с таймером */ sc_build_nonce(ctx->tx_counter, 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; } #endif sc_status_t sc_sha_transcode(const uint8_t *key, size_t key_len, uint8_t *data, size_t data_len) { if (!key || !data || key_len == 0 || data_len == 0) { return SC_ERR_INVALID_ARG; } uint8_t sha_hash[SC_SHA256_BLOCK_SIZE]; SC_SHA256_CTX ctx; sc_sha256_init(&ctx); sc_sha256_update(&ctx, key, key_len); sc_sha256_final(&ctx, sha_hash); for (size_t i = 0; i < data_len; i++) { data[i] ^= sha_hash[i % SC_SHA256_BLOCK_SIZE]; } return SC_OK; }