crypto: migrate ECDH(secp256r1) to X25519, drop TinyCrypt
- SC_PUBKEY_SIZE: 64 -> 32 bytes, SC_PUBKEY_ENC_SIZE: 72 -> 40 - sc_set_peer_public_key uses EVP_PKEY_derive with X25519 - sc_generate_keypair via EVP_PKEY_keygen(EVP_PKEY_X25519) - sc_compute_public_key_from_private via EVP_PKEY_new_raw_private_key - sc_validate_key: check non-zero (X25519 has no invalid keys) - sc_obfuscate_pubkey: XOR now 32 bytes - Remove all TinyCrypt code (~860 lines), always use OpenSSL - Remove TinyCrypt from build system, drop test_crypto/test_ecc_encrypt - Update all test keys (128 hex -> 64 hex) - Fix bbr_integration.c split-line pubkeys
This commit is contained in:
1 parent
34683f0500
commit
73f707ef9b
27 files changed
+242
-972
No files matched your search
+5
-92
@@ -51,109 +51,22 @@ utun_CORE_SOURCES = \
|
||||
# Platform-specific TUN libs (Windows only)
|
||||
utun_TUN_LIBS = @TUN_LIBS@
|
||||
|
||||
# TinyCrypt sources (only used without OpenSSL)
|
||||
utun_TINYCRYPT_SOURCES = \
|
||||
$(top_srcdir)/tinycrypt/lib/source/aes_encrypt.c \
|
||||
$(top_srcdir)/tinycrypt/lib/source/aes_decrypt.c \
|
||||
$(top_srcdir)/tinycrypt/lib/source/cbc_mode.c \
|
||||
$(top_srcdir)/tinycrypt/lib/source/ccm_mode.c \
|
||||
$(top_srcdir)/tinycrypt/lib/source/cmac_mode.c \
|
||||
$(top_srcdir)/tinycrypt/lib/source/ctr_mode.c \
|
||||
$(top_srcdir)/tinycrypt/lib/source/ecc.c \
|
||||
$(top_srcdir)/tinycrypt/lib/source/ecc_dh.c \
|
||||
$(top_srcdir)/tinycrypt/lib/source/ecc_dsa.c \
|
||||
$(top_srcdir)/tinycrypt/lib/source/ecc_platform_specific.c \
|
||||
$(top_srcdir)/tinycrypt/lib/source/hmac.c \
|
||||
$(top_srcdir)/tinycrypt/lib/source/sha256.c \
|
||||
$(top_srcdir)/tinycrypt/lib/source/utils.c
|
||||
|
||||
# Combine sources based on OpenSSL usage
|
||||
if USE_OPENSSL
|
||||
utun_SOURCES = $(utun_CORE_SOURCES) $(utun_TUN_SOURCES)
|
||||
else
|
||||
utun_SOURCES = $(utun_CORE_SOURCES) $(utun_TINYCRYPT_SOURCES) $(utun_TUN_SOURCES)
|
||||
endif
|
||||
|
||||
# Include paths
|
||||
utun_CORE_CFLAGS = \
|
||||
utun_CFLAGS = \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(top_srcdir)/src/uip \
|
||||
-I$(top_srcdir)/tinycrypt/lib/include \
|
||||
-I$(top_srcdir)/tinycrypt/lib/source \
|
||||
-g \
|
||||
$(DEBUG_FLAGS)
|
||||
|
||||
utun_CFLAGS = $(utun_CORE_CFLAGS)
|
||||
|
||||
# Libraries
|
||||
utun_CORE_LDADD = \
|
||||
utun_LDADD = \
|
||||
$(top_builddir)/lib/libuasync.a \
|
||||
-lpthread \
|
||||
-lm
|
||||
|
||||
if USE_OPENSSL
|
||||
utun_LDADD = $(utun_CORE_LDADD) -lcrypto $(utun_TUN_LIBS)
|
||||
else
|
||||
utun_LDADD = $(utun_CORE_LDADD) $(utun_TUN_LIBS)
|
||||
endif
|
||||
|
||||
# TinyCrypt object files (for tests that need them)
|
||||
TINYCRYPT_SRCDIR = $(top_srcdir)/tinycrypt/lib/source
|
||||
|
||||
TINYCRYPT_OBJS = \
|
||||
utun-aes_encrypt.o \
|
||||
utun-aes_decrypt.o \
|
||||
utun-ccm_mode.o \
|
||||
utun-cmac_mode.o \
|
||||
utun-ctr_mode.o \
|
||||
utun-ecc.o \
|
||||
utun-ecc_dh.o \
|
||||
utun-ecc_dsa.o \
|
||||
utun-sha256.o \
|
||||
utun-ecc_platform_specific.o \
|
||||
utun-utils.o
|
||||
|
||||
# Rules to build TinyCrypt objects (for tests)
|
||||
TINYCRYPT_CFLAGS = -g -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
|
||||
|
||||
utun-aes_encrypt.o: $(TINYCRYPT_SRCDIR)/aes_encrypt.c
|
||||
$(AM_V_CC)$(COMPILE) $(TINYCRYPT_CFLAGS) -c -o $@ $<
|
||||
|
||||
utun-aes_decrypt.o: $(TINYCRYPT_SRCDIR)/aes_decrypt.c
|
||||
$(AM_V_CC)$(COMPILE) $(TINYCRYPT_CFLAGS) -c -o $@ $<
|
||||
|
||||
utun-ccm_mode.o: $(TINYCRYPT_SRCDIR)/ccm_mode.c
|
||||
$(AM_V_CC)$(COMPILE) $(TINYCRYPT_CFLAGS) -c -o $@ $<
|
||||
|
||||
utun-cmac_mode.o: $(TINYCRYPT_SRCDIR)/cmac_mode.c
|
||||
$(AM_V_CC)$(COMPILE) $(TINYCRYPT_CFLAGS) -c -o $@ $<
|
||||
|
||||
utun-ctr_mode.o: $(TINYCRYPT_SRCDIR)/ctr_mode.c
|
||||
$(AM_V_CC)$(COMPILE) $(TINYCRYPT_CFLAGS) -c -o $@ $<
|
||||
|
||||
utun-ecc.o: $(TINYCRYPT_SRCDIR)/ecc.c
|
||||
$(AM_V_CC)$(COMPILE) $(TINYCRYPT_CFLAGS) -c -o $@ $<
|
||||
|
||||
utun-ecc_dh.o: $(TINYCRYPT_SRCDIR)/ecc_dh.c
|
||||
$(AM_V_CC)$(COMPILE) $(TINYCRYPT_CFLAGS) -c -o $@ $<
|
||||
|
||||
utun-ecc_dsa.o: $(TINYCRYPT_SRCDIR)/ecc_dsa.c
|
||||
$(AM_V_CC)$(COMPILE) $(TINYCRYPT_CFLAGS) -c -o $@ $<
|
||||
|
||||
utun-sha256.o: $(TINYCRYPT_SRCDIR)/sha256.c
|
||||
$(AM_V_CC)$(COMPILE) $(TINYCRYPT_CFLAGS) -c -o $@ $<
|
||||
|
||||
# Force rebuild of ecc_platform_specific.o to pick up platform changes
|
||||
utun-ecc_platform_specific.o: $(TINYCRYPT_SRCDIR)/ecc_platform_specific.c FORCE
|
||||
$(AM_V_CC)$(COMPILE) $(TINYCRYPT_CFLAGS) -c -o $@ $<
|
||||
|
||||
FORCE:
|
||||
|
||||
utun-utils.o: $(TINYCRYPT_SRCDIR)/utils.c
|
||||
$(AM_V_CC)$(COMPILE) $(TINYCRYPT_CFLAGS) -c -o $@ $<
|
||||
|
||||
# Build TinyCrypt objects for tests (convenience target)
|
||||
tinycrypt-objects: $(TINYCRYPT_OBJS)
|
||||
-lm \
|
||||
-lcrypto \
|
||||
$(utun_TUN_LIBS)
|
||||
|
||||
# Copy binary to project root after building
|
||||
all-local: copy-to-root
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ struct CFG_CONTROL_ALLOW {
|
||||
};
|
||||
|
||||
struct CFG_ALLOWED_KEY {
|
||||
uint8_t key_bin[SC_PUBKEY_SIZE]; // public key in binary (64 bytes)
|
||||
uint8_t key_bin[SC_PUBKEY_SIZE]; // public key in binary (32 bytes, X25519)
|
||||
struct CFG_ALLOWED_KEY *next;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "../lib/mem.h"
|
||||
|
||||
#define PRIV_HEXKEY_LEN 65 // 32 bytes * 2 hex chars + null
|
||||
#define PUB_HEXKEY_LEN 129 // 64 bytes * 2 hex chars + null
|
||||
#define PUB_HEXKEY_LEN 65 // 32 bytes * 2 hex chars + null
|
||||
#define HEXNODEID_LEN 17 // 8 bytes * 2 hex chars + null
|
||||
#define MAX_LINE_LEN 1024
|
||||
|
||||
@@ -41,8 +41,8 @@ static int is_valid_priv_key(const char *key) {
|
||||
}
|
||||
|
||||
static int is_valid_pub_key(const char *key) {
|
||||
if (!key || strlen(key) != 128) return 0;
|
||||
for (int i = 0; i < 128; i++) {
|
||||
if (!key || strlen(key) != 64) return 0;
|
||||
for (int i = 0; i < 64; i++) {
|
||||
if (!isxdigit((unsigned char)key[i])) return 0;
|
||||
}
|
||||
return 1;
|
||||
|
||||
+106
-654
@@ -1,4 +1,4 @@
|
||||
/* secure_channel.c - Secure Channel library implementation using TinyCrypt or OpenSSL */
|
||||
/* secure_channel.c - Secure Channel library using X25519 + AES-128-CCM (OpenSSL) */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
@@ -19,27 +19,9 @@
|
||||
#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/ctr_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};
|
||||
@@ -52,10 +34,9 @@ static void sc_init_random_seed(void)
|
||||
}
|
||||
}
|
||||
|
||||
// Конвертация hex строки в бинарный формат (common)
|
||||
// Конвертация hex строки в бинарный формат
|
||||
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;
|
||||
@@ -76,33 +57,21 @@ sc_status_t sc_init_ctx(sc_context_t *ctx, struct SC_MYKEYS *mykeys) {
|
||||
|
||||
// Common helper functions for input validation and CRC handling
|
||||
|
||||
static sc_status_t validate_encrypt_inputs(sc_context_t *ctx, const uint8_t *plaintext,
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -123,9 +92,7 @@ static sc_status_t verify_and_strip_crc32(uint8_t *plaintext_with_crc, size_t to
|
||||
((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;
|
||||
}
|
||||
if (received_crc != calc_crc) return SC_ERR_CRC_FAILED;
|
||||
memcpy(plaintext, plaintext_with_crc, data_len);
|
||||
*plaintext_len = data_len;
|
||||
return SC_OK;
|
||||
@@ -134,12 +101,10 @@ static sc_status_t verify_and_strip_crc32(uint8_t *plaintext_with_crc, size_t to
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -160,94 +125,38 @@ static void sc_stream_derive_nonce(const uint8_t *session_key, uint32_t stream_i
|
||||
memcpy(nonce_out, hash, SC_STREAM_NONCE_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;
|
||||
}
|
||||
|
||||
// X25519: any non-zero 32-byte value is a valid public key
|
||||
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);
|
||||
uint8_t zero[SC_PUBKEY_SIZE] = {0};
|
||||
if (memcmp(public_key, zero, SC_PUBKEY_SIZE) == 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_validate_key: all-zero public key");
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!pk) return SC_ERR_INVALID_ARG;
|
||||
|
||||
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL);
|
||||
if (!ctx) return SC_ERR_CRYPTO;
|
||||
if (EVP_PKEY_keygen_init(ctx) <= 0) { EVP_PKEY_CTX_free(ctx); return SC_ERR_CRYPTO; }
|
||||
|
||||
EVP_PKEY *pkey = NULL;
|
||||
if (EVP_PKEY_keygen(ctx, &pkey) <= 0) { EVP_PKEY_CTX_free(ctx); return SC_ERR_CRYPTO; }
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
|
||||
size_t priv_len = SC_PRIVKEY_SIZE, pub_len = SC_PUBKEY_SIZE;
|
||||
if (EVP_PKEY_get_raw_private_key(pkey, pk->private_key, &priv_len) <= 0
|
||||
|| priv_len != SC_PRIVKEY_SIZE
|
||||
|| EVP_PKEY_get_raw_public_key(pkey, pk->public_key, &pub_len) <= 0
|
||||
|| pub_len != SC_PUBKEY_SIZE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_generate_keypair: failed to extract raw keys");
|
||||
EVP_PKEY_free(pkey);
|
||||
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);
|
||||
EVP_PKEY_free(pkey);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_generate_keypair: generated valid X25519 keypair");
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
@@ -256,7 +165,7 @@ sc_status_t sc_init_local_keys(struct SC_MYKEYS *mykeys, const char *public_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",
|
||||
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");
|
||||
@@ -284,81 +193,46 @@ sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const uint8_t *peer_public
|
||||
} 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);
|
||||
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; }
|
||||
|
||||
EVP_PKEY *my_pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, ctx->pk->private_key, SC_PRIVKEY_SIZE);
|
||||
if (!my_pkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: EVP_PKEY_new_raw_private_key failed"); return SC_ERR_CRYPTO; }
|
||||
EVP_PKEY *peer_pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, peer_public_key, SC_PUBKEY_SIZE);
|
||||
if (!peer_pkey) { EVP_PKEY_free(my_pkey); DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: EVP_PKEY_new_raw_public_key failed"); return SC_ERR_CRYPTO; }
|
||||
|
||||
EVP_PKEY_CTX *derive_ctx = EVP_PKEY_CTX_new(my_pkey, NULL);
|
||||
if (!derive_ctx) { EVP_PKEY_free(my_pkey); EVP_PKEY_free(peer_pkey); return SC_ERR_CRYPTO; }
|
||||
if (EVP_PKEY_derive_init(derive_ctx) <= 0) {
|
||||
EVP_PKEY_CTX_free(derive_ctx); EVP_PKEY_free(my_pkey); EVP_PKEY_free(peer_pkey);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: EVP_PKEY_derive_init failed");
|
||||
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);
|
||||
if (EVP_PKEY_derive_set_peer(derive_ctx, peer_pkey) <= 0) {
|
||||
EVP_PKEY_CTX_free(derive_ctx); EVP_PKEY_free(my_pkey); EVP_PKEY_free(peer_pkey);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: EVP_PKEY_derive_set_peer failed");
|
||||
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);
|
||||
size_t secret_len = SC_SHARED_SECRET_SIZE;
|
||||
if (EVP_PKEY_derive(derive_ctx, shared_secret, &secret_len) <= 0 || secret_len != SC_SHARED_SECRET_SIZE) {
|
||||
EVP_PKEY_CTX_free(derive_ctx); EVP_PKEY_free(my_pkey); EVP_PKEY_free(peer_pkey);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: X25519 derive failed secret_len=%zu", secret_len);
|
||||
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);
|
||||
|
||||
EVP_PKEY_CTX_free(derive_ctx);
|
||||
EVP_PKEY_free(my_pkey);
|
||||
EVP_PKEY_free(peer_pkey);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: X25519 key exchange complete");
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
@@ -367,27 +241,17 @@ static void sc_build_nonce(uint64_t counter, uint8_t *nonce_out) {
|
||||
uint8_t hash[32];
|
||||
struct timeval tv;
|
||||
uint8_t data[24];
|
||||
if (!sc_urandom_initialized) {
|
||||
sc_init_random_seed();
|
||||
}
|
||||
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;
|
||||
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);
|
||||
@@ -405,39 +269,22 @@ sc_status_t sc_encrypt(sc_context_t *ctx, const uint8_t *plaintext, size_t plain
|
||||
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;
|
||||
if (EVP_EncryptInit_ex(ectx, EVP_aes_128_ccm(), NULL, NULL, NULL) != 1
|
||||
|| EVP_CIPHER_CTX_ctrl(ectx, EVP_CTRL_AEAD_SET_IVLEN, SC_NONCE_SIZE, NULL) != 1
|
||||
|| EVP_CIPHER_CTX_ctrl(ectx, EVP_CTRL_AEAD_SET_TAG, SC_TAG_SIZE, NULL) != 1
|
||||
|| 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;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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);
|
||||
@@ -459,79 +306,39 @@ sc_status_t sc_decrypt(sc_context_t *ctx, const uint8_t *ciphertext, size_t ciph
|
||||
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;
|
||||
if (EVP_DecryptInit_ex(dctx, EVP_aes_128_ccm(), NULL, NULL, NULL) != 1
|
||||
|| EVP_CIPHER_CTX_ctrl(dctx, EVP_CTRL_AEAD_SET_IVLEN, SC_NONCE_SIZE, NULL) != 1
|
||||
|| EVP_CIPHER_CTX_ctrl(dctx, EVP_CTRL_AEAD_SET_TAG, SC_TAG_SIZE, (void *)(encrypted_data + total_plaintext_len)) != 1
|
||||
|| 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;
|
||||
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;
|
||||
}
|
||||
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++;
|
||||
}
|
||||
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 (!private_key || !public_key) return SC_ERR_INVALID_ARG;
|
||||
EVP_PKEY *pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, private_key, SC_PRIVKEY_SIZE);
|
||||
if (!pkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_compute_public_key_from_private: EVP_PKEY_new_raw_private_key failed"); return SC_ERR_CRYPTO; }
|
||||
size_t pub_len = SC_PUBKEY_SIZE;
|
||||
if (EVP_PKEY_get_raw_public_key(pkey, public_key, &pub_len) <= 0 || pub_len != SC_PUBKEY_SIZE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_compute_public_key_from_private: EVP_PKEY_get_raw_public_key failed");
|
||||
EVP_PKEY_free(pkey); return SC_ERR_CRYPTO;
|
||||
}
|
||||
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);
|
||||
EVP_PKEY_free(pkey);
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
// --- OpenSSL streaming cipher (AES-128-CTR) ---
|
||||
// --- Streaming cipher (AES-128-CTR) ---
|
||||
|
||||
sc_status_t sc_stream_init(sc_context_t *ctx, struct sc_stream_state *state, uint32_t stream_id) {
|
||||
if (!ctx || !state) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_init: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
@@ -545,8 +352,7 @@ sc_status_t sc_stream_init(sc_context_t *ctx, struct sc_stream_state *state, uin
|
||||
if (!ectx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_init: EVP_CIPHER_CTX_new failed"); return SC_ERR_CRYPTO; }
|
||||
if (EVP_EncryptInit_ex(ectx, EVP_aes_128_ctr(), NULL, ctx->session_key, iv) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_init: EVP_EncryptInit_ex failed");
|
||||
EVP_CIPHER_CTX_free(ectx);
|
||||
return SC_ERR_CRYPTO;
|
||||
EVP_CIPHER_CTX_free(ectx); return SC_ERR_CRYPTO;
|
||||
}
|
||||
state->ectx = ectx;
|
||||
state->initialized = 1;
|
||||
@@ -575,385 +381,31 @@ void sc_stream_cleanup(struct sc_stream_state *state) {
|
||||
state->initialized = 0;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
// --- TinyCrypt streaming cipher (AES-128-CTR) ---
|
||||
|
||||
sc_status_t sc_stream_init(sc_context_t *ctx, struct sc_stream_state *state, uint32_t stream_id) {
|
||||
if (!ctx || !state) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_init: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!ctx->session_ready) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_init: session not ready"); return SC_ERR_NOT_INITIALIZED; }
|
||||
uint8_t nonce[SC_STREAM_NONCE_SIZE];
|
||||
sc_stream_derive_nonce(ctx->session_key, stream_id, nonce);
|
||||
memcpy(state->ctr_block, nonce, SC_STREAM_NONCE_SIZE);
|
||||
memset(state->ctr_block + SC_STREAM_NONCE_SIZE, 0, 4);
|
||||
if (tc_aes128_set_encrypt_key((TCAesKeySched_t)state->sched_buf, ctx->session_key) != TC_CRYPTO_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_init: tc_aes128_set_encrypt_key failed");
|
||||
return SC_ERR_CRYPTO;
|
||||
}
|
||||
state->initialized = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_stream_init: stream_id=%u nonce=%02x%02x%02x%02x...",
|
||||
stream_id, nonce[0], nonce[1], nonce[2], nonce[3]);
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
sc_status_t sc_stream_xor(struct sc_stream_state *state, uint8_t *data, size_t data_len) {
|
||||
if (data_len == 0) return SC_OK;
|
||||
if (!state || !data) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_xor: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state->initialized) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_xor: not initialized"); return SC_ERR_NOT_INITIALIZED; }
|
||||
if (tc_ctr_mode(data, (unsigned int)data_len, data, (unsigned int)data_len,
|
||||
state->ctr_block, (TCAesKeySched_t)state->sched_buf) != TC_CRYPTO_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_xor: tc_ctr_mode failed len=%zu", data_len);
|
||||
return SC_ERR_CRYPTO;
|
||||
}
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
void sc_stream_cleanup(struct sc_stream_state *state) {
|
||||
if (!state) return;
|
||||
memset(state->sched_buf, 0, sizeof(state->sched_buf));
|
||||
memset(state->ctr_block, 0, sizeof(state->ctr_block));
|
||||
state->initialized = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
// --- Common crypto utilities ---
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
+4
-14
@@ -7,7 +7,7 @@
|
||||
|
||||
// Размеры ключей
|
||||
#define SC_PRIVKEY_SIZE 32
|
||||
#define SC_PUBKEY_SIZE 64
|
||||
#define SC_PUBKEY_SIZE 32
|
||||
#define SC_HASH_SIZE 32
|
||||
#define SC_NONCE_SIZE 13 // CCM requires exactly 13 bytes
|
||||
#define SC_SHARED_SECRET_SIZE SC_HASH_SIZE
|
||||
@@ -15,7 +15,7 @@
|
||||
#define SC_TAG_SIZE 16
|
||||
#define SC_CRC32_SIZE 4
|
||||
|
||||
// Шифрование pubkey при передаче (salt + double SHA256 XOR)
|
||||
// Обфускация pubkey при передаче (salt + double SHA256 XOR)
|
||||
#define SC_PUBKEY_ENC_SALT_SIZE 8
|
||||
#define SC_PUBKEY_ENC_SIZE (SC_PUBKEY_SIZE + SC_PUBKEY_ENC_SALT_SIZE)
|
||||
|
||||
@@ -43,14 +43,10 @@ struct SC_MYKEYS {
|
||||
// Контекст защищенного канала
|
||||
struct secure_channel {
|
||||
struct SC_MYKEYS* pk;
|
||||
/* Ключи пира (после key exchange) */
|
||||
/* Ключ пира (после key exchange) */
|
||||
uint8_t peer_public_key[SC_PUBKEY_SIZE];
|
||||
uint8_t session_key[SC_SESSION_KEY_SIZE]; /* Derived session key */
|
||||
|
||||
/* Nonces для отправки и приема (теперь генерируются динамически, не используются counters для nonce) */
|
||||
uint8_t send_nonce[SC_NONCE_SIZE];
|
||||
uint8_t recv_nonce[SC_NONCE_SIZE];
|
||||
|
||||
uint8_t initialized;
|
||||
uint8_t peer_key_set;
|
||||
uint8_t session_ready;
|
||||
@@ -78,15 +74,9 @@ sc_status_t sc_obfuscate_pubkey(const uint8_t *salt, const uint8_t *peer_pubkey,
|
||||
// --- Streaming cipher (AES-128-CTR, confidentiality only) ---
|
||||
|
||||
#define SC_STREAM_NONCE_SIZE 12
|
||||
#define SC_STREAM_AES_SCHED_SIZE 176 // sizeof(struct tc_aes_key_sched_struct) = Nb*(Nr+1)*4 = 4*11*4
|
||||
|
||||
struct sc_stream_state {
|
||||
#ifdef USE_OPENSSL
|
||||
void *ectx; // EVP_CIPHER_CTX* (opaque, created/destroyed in .c)
|
||||
#else
|
||||
uint8_t sched_buf[SC_STREAM_AES_SCHED_SIZE]; // AES-128 key schedule
|
||||
uint8_t ctr_block[16]; // nonce(12) + counter(4, BE)
|
||||
#endif
|
||||
void *ectx; // EVP_CIPHER_CTX*
|
||||
uint8_t initialized;
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user