Files
utun2/src/secure_channel.c
T

860 lines
30 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* secure_channel.c - Secure Channel library implementation using TinyCrypt or OpenSSL */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define OPENSSL_API_COMPAT 0x10100000L
#include "secure_channel.h"
#include "../lib/debug_config.h"
#include "../lib/platform_compat.h"
#include <string.h>
#include <stddef.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdio.h>
#include <fcntl.h>
#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 <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <openssl/err.h>
#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
#include "../lib/platform_compat.h"
static uint8_t sc_urandom_seed[8] = {0};
static int sc_urandom_initialized = 0;
static void sc_init_random_seed(void)
{
if (random_bytes(sc_urandom_seed, 8) == 0) {
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;
}
// Common helper functions for input validation and CRC handling
static sc_status_t validate_encrypt_inputs(sc_context_t *ctx, const uint8_t *plaintext,
const uint8_t *ciphertext, const size_t *ciphertext_len,
size_t plaintext_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;
}
return SC_OK;
}
static sc_status_t validate_decrypt_inputs(sc_context_t *ctx, const uint8_t *ciphertext,
const uint8_t *plaintext, const size_t *plaintext_len,
size_t ciphertext_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;
}
return SC_OK;
}
static void append_crc32(const uint8_t *plaintext, size_t plaintext_len, uint8_t *output) {
memcpy(output, plaintext, plaintext_len);
uint32_t crc = crc32_calc(plaintext, plaintext_len);
output[plaintext_len] = (crc >> 0) & 0xFF;
output[plaintext_len + 1] = (crc >> 8) & 0xFF;
output[plaintext_len + 2] = (crc >> 16) & 0xFF;
output[plaintext_len + 3] = (crc >> 24) & 0xFF;
}
static sc_status_t verify_and_strip_crc32(uint8_t *plaintext_with_crc, size_t total_len,
uint8_t *plaintext, size_t *plaintext_len) {
size_t data_len = total_len - SC_CRC32_SIZE;
uint32_t received_crc = ((uint32_t)plaintext_with_crc[data_len]) |
((uint32_t)plaintext_with_crc[data_len + 1] << 8) |
((uint32_t)plaintext_with_crc[data_len + 2] << 16) |
((uint32_t)plaintext_with_crc[data_len + 3] << 24);
uint32_t calc_crc = crc32_calc(plaintext_with_crc, data_len);
if (received_crc != calc_crc) {
return SC_ERR_CRC_FAILED;
}
memcpy(plaintext, plaintext_with_crc, data_len);
*plaintext_len = data_len;
return SC_OK;
}
static void sc_derive_session_key(const uint8_t *shared_secret, uint8_t *session_key) {
SC_SHA256_CTX sha_ctx;
uint8_t hash[SC_HASH_SIZE];
sc_sha256_init(&sha_ctx);
sc_sha256_update(&sha_ctx, shared_secret, SC_SHARED_SECRET_SIZE);
sc_sha256_update(&sha_ctx, (const uint8_t *)"uTun-v3-session", 15);
sc_sha256_final(&sha_ctx, hash);
memcpy(session_key, hash, SC_SESSION_KEY_SIZE);
}
#ifdef USE_OPENSSL
// OpenSSL-specific implementations
static int sc_rng(uint8_t *dest, unsigned size) {
if (random_bytes(dest, size) != 0) {
return 0;
}
/* Mix in PID and microtime for additional entropy */
#ifdef _WIN32
DWORD pid = GetCurrentProcessId();
#else
pid_t pid = getpid();
#endif
struct timeval tv;
utun_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 uint8_t *peer_public_key_h, int mode) {
uint8_t peer_public_key[SC_PUBKEY_SIZE];
if (mode) {
if (hex_to_binary((const char*)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 (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;
}
sc_derive_session_key(shared_secret, ctx->session_key);
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();
}
utun_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) {
sc_status_t status = validate_encrypt_inputs(ctx, plaintext, ciphertext, ciphertext_len, plaintext_len);
if (status != SC_OK) return status;
uint8_t plaintext_with_crc[plaintext_len + SC_CRC32_SIZE];
append_crc32(plaintext, plaintext_len, plaintext_with_crc);
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) {
sc_status_t status = validate_decrypt_inputs(ctx, ciphertext, plaintext, plaintext_len, ciphertext_len);
if (status != SC_OK) return status;
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);
sc_status_t result = verify_and_strip_crc32(plaintext_with_crc, total_plaintext_len, plaintext, plaintext_len);
if (result == SC_OK) {
ctx->rx_counter++;
}
return result;
}
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)
{
if (random_bytes(dest, size) != 0) {
return 0;
}
/* Mix in PID and microtime for additional entropy */
#ifdef _WIN32
DWORD pid = GetCurrentProcessId();
#else
pid_t pid = getpid();
#endif
struct timeval tv;
utun_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();
}
/* Set RNG function for TinyCrypt - must be done before any ECC operations */
uECC_set_rng(sc_rng);
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 uint8_t *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((const char*)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;
}
sc_derive_session_key(shared_secret, ctx->session_key);
/* 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();
}
utun_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) {
sc_status_t status = validate_encrypt_inputs(ctx, plaintext, ciphertext, ciphertext_len, plaintext_len);
if (status != SC_OK) return status;
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;
/* Добавляем CRC32 к данным */
append_crc32(plaintext, plaintext_len, plaintext_with_crc);
/* Генерируем 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)
{
sc_status_t status = validate_decrypt_inputs(ctx, ciphertext, plaintext, plaintext_len, ciphertext_len);
if (status != SC_OK) return status;
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];
/* Извлекаем 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 используя helper-функцию */
sc_status_t result = verify_and_strip_crc32(plaintext_with_crc, total_plaintext_len, plaintext, plaintext_len);
if (result == SC_OK) {
ctx->rx_counter++;
}
return result;
}
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_HASH_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_HASH_SIZE];
}
return SC_OK;
}
sc_status_t sc_obfuscate_pubkey(const uint8_t *salt, const uint8_t *peer_pubkey, const uint8_t *pubkey, uint8_t *output) {
if (!salt || !peer_pubkey || !pubkey || !output) return SC_ERR_INVALID_ARG;
uint8_t sha[SC_HASH_SIZE*2];
SC_SHA256_CTX ctx;
sc_sha256_init(&ctx);
sc_sha256_update(&ctx, salt, SC_PUBKEY_ENC_SALT_SIZE);
sc_sha256_update(&ctx, peer_pubkey, SC_PUBKEY_SIZE);
sc_sha256_final(&ctx, sha);
sc_sha256_init(&ctx);
sc_sha256_update(&ctx, peer_pubkey, SC_PUBKEY_SIZE);
sc_sha256_update(&ctx, salt, SC_PUBKEY_ENC_SALT_SIZE);
sc_sha256_final(&ctx, sha+SC_HASH_SIZE);
for (size_t i = 0; i < SC_PUBKEY_SIZE; i++) output[i] = pubkey[i] ^ sha[i];
return SC_OK;
}