diff --git a/CMakeLists.txt b/CMakeLists.txt index 7493410..c64bfee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,8 +16,17 @@ set(SOURCES src/se050_keystore.c src/se050_rng.c src/se050_x25519.c + src/se050_x25519_sw.c + src/se050_chacha20_poly1305.c + src/se050_blake2s.c + src/se050_hmac_blake2s.c + src/se050_hkdf_blake2s.c + src/se050_tai64n.c src/se050_scp03.c src/se050_scp03_keys.c + src/se050_wireguard_proto.c + src/se050_tai64n_hw.c + src/se050_wireguard.c ) # Create library diff --git a/include/se050_wireguard.h b/include/se050_wireguard.h index c8d3220..dd97cc4 100644 --- a/include/se050_wireguard.h +++ b/include/se050_wireguard.h @@ -1,426 +1,168 @@ /** * @file se050_wireguard.h - * @brief SE050 WireGuard Minimum Interface - * - * Clean-room interface for WireGuard cryptographic operations using SE050. - * - * This header defines the API for: - * - X25519 ECDH key exchange - * - True Random Number Generation (TRNG) - * - Platform SCP03 secure channel - * - * License: MIT (Clean-room implementation) + * @brief WireGuard VPN Protocol - Public API */ #ifndef SE050_WIREGUARD_H #define SE050_WIREGUARD_H -/* Feature test macros - must be defined before any includes */ -#define _GNU_SOURCE -#define _POSIX_C_SOURCE 200809L - #include +#include #include #ifdef __cplusplus extern "C" { #endif -/* ============================================================================ - * Error Codes - * ============================================================================ */ - -typedef enum { - SE050_OK = 0x00, - SE050_ERR_FAIL = 0x01, - SE050_ERR_INVALID_ARG = 0x02, - SE050_ERR_SESSION = 0x03, - SE050_ERR_KEY_STORE = 0x04, - SE050_ERR_RNG = 0x05, - SE050_ERR_ECDH = 0x06, - SE050_ERR_SCP03 = 0x07, - SE050_ERR_I2C = 0x08, - SE050_ERR_NOT_INIT = 0x09, -} se050_status_t; - -/* ============================================================================ - * Constants - * ============================================================================ */ - -/** WireGuard key size (X25519 uses 32-byte keys) */ -#define SE050_WG_KEY_SIZE 32 - -/** WireGuard public key size */ -#define SE050_WG_PUBKEY_SIZE 32 - -/** SCP03 key size */ -#define SE050_SCP03_KEY_SIZE 16 - -/** SCP03 IV size */ -#define SE050_SCP03_IV_SIZE 16 - -/** SCP03 CMAC size */ -#define SE050_SCP03_CMAC_SIZE 8 - -/** Maximum buffer size for SCP03 */ -#define SE050_SCP03_MAX_BUF 1024 - -/* ============================================================================ +/* ========================================================================= * Type Definitions - * ============================================================================ */ - -/** Session context for SE050 communication */ -typedef struct se050_session_ctx se050_session_ctx_t; - -/** Key store context */ -typedef struct se050_keystore_ctx se050_keystore_ctx_t; - -/** RNG context */ -typedef struct se050_rng_ctx se050_rng_ctx_t; - -/** SCP03 session context */ -typedef struct se050_scp03_ctx se050_scp03_ctx_t; - -/** X25519 key pair */ -typedef struct { - uint8_t private_key[SE050_WG_KEY_SIZE]; /**< Private key (32 bytes) */ - uint8_t public_key[SE050_WG_PUBKEY_SIZE]; /**< Public key (32 bytes) */ -} se050_x25519_keypair_t; - -/** I2C HAL interface */ -typedef struct { - void *handle; /**< I2C device handle */ - uint8_t slave_addr; /**< I2C slave address (default: 0x90) */ - const char *dev_path; /**< Device path (e.g., "/dev/i2c-1") */ -} se050_i2c_hal_t; - -/* ============================================================================ - * I2C HAL Layer - * ============================================================================ */ + * ========================================================================= */ /** - * @brief Initialize I2C HAL - * @param hal I2C HAL structure - * @param dev_path Device path (e.g., "/dev/i2c-1") - * @param slave_addr I2C slave address (default: 0x90) - * @return SE050_OK on success + * @brief WireGuard session context + * + * Contains all state needed for WireGuard communication: + * - Local keypair + * - Peer public key + * - Session keys (sending/receiving) + * - Cookie state for DoS protection */ -se050_status_t se050_i2c_init(se050_i2c_hal_t *hal, const char *dev_path, uint8_t slave_addr); +typedef struct se050_wireguard_session se050_wireguard_session_t; -/** - * @brief Close I2C HAL - * @param hal I2C HAL structure - */ -void se050_i2c_close(se050_i2c_hal_t *hal); - -/** - * @brief Read from SE050 via I2C - * @param hal I2C HAL structure - * @param buffer Read buffer - * @param length Number of bytes to read - * @return Number of bytes read, or negative on error - */ -int se050_i2c_read(se050_i2c_hal_t *hal, uint8_t *buffer, int length); - -/** - * @brief Write to SE050 via I2C - * @param hal I2C HAL structure - * @param buffer Write buffer - * @param length Number of bytes to write - * @return Number of bytes written, or negative on error - */ -int se050_i2c_write(se050_i2c_hal_t *hal, const uint8_t *buffer, int length); - -/** - * @brief Wake up SE050 (if needed) - * @param hal I2C HAL structure - * @return SE050_OK on success - */ -se050_status_t se050_i2c_wakeup(se050_i2c_hal_t *hal); - -/* ============================================================================ +/* ========================================================================= * Session Management - * ============================================================================ */ + * ========================================================================= */ /** - * @brief Create SE050 session - * @param ctx Output session context - * @param hal I2C HAL interface - * @return SE050_OK on success - */ -se050_status_t se050_session_create(se050_session_ctx_t **ctx, se050_i2c_hal_t *hal); - -/** - * @brief Open SE050 session - * @param ctx Session context - * @return SE050_OK on success - */ -se050_status_t se050_session_open(se050_session_ctx_t *ctx); - -/** - * @brief Close SE050 session - * @param ctx Session context - */ -void se050_session_close(se050_session_ctx_t *ctx); - -/** - * @brief Delete SE050 session - * @param ctx Session context - */ -void se050_session_delete(se050_session_ctx_t *ctx); - -/* ============================================================================ - * Session SCP03 Integration - * ============================================================================ */ - -/** - * @brief Initialize SCP03 secure channel for session - * @param ctx Session context - * @return SE050_OK on success - */ -se050_status_t se050_session_scp03_init(se050_session_ctx_t *ctx); - -/** - * @brief Set SCP03 keys for PlatformSCP03 authentication - * @param ctx Session context - * @param enc_key Encryption key (16 bytes) - * @param mac_key MAC key (16 bytes) - * @param dek_key Data Encryption Key (16 bytes) - * @return SE050_OK on success - */ -se050_status_t se050_session_scp03_set_keys(se050_session_ctx_t *ctx, - const uint8_t *enc_key, - const uint8_t *mac_key, - const uint8_t *dek_key); - -/** - * @brief Encrypt command using SCP03 - * @param ctx Session context - * @param cmd Command buffer - * @param cmd_len Command length (updated after padding) - * @return SE050_OK on success - */ -se050_status_t se050_session_scp03_encrypt(se050_session_ctx_t *ctx, - uint8_t *cmd, - size_t *cmd_len); - -/** - * @brief Decrypt response using SCP03 - * @param ctx Session context - * @param cmd_len Original command length - * @param rsp Response buffer - * @param rsp_len Response length (updated after decryption) - * @return Status word (0x9000 on success) - */ -uint16_t se050_session_scp03_decrypt(se050_session_ctx_t *ctx, - size_t cmd_len, - uint8_t *rsp, - size_t *rsp_len); - -/* ============================================================================ - * Key Store - * ============================================================================ */ - -/** - * @brief Initialize key store - * @param ctx Output key store context - * @param session SE050 session - * @return SE050_OK on success - */ -se050_status_t se050_keystore_init(se050_keystore_ctx_t **ctx, se050_session_ctx_t *session); - -/** - * @brief Free key store - * @param ctx Key store context - */ -void se050_keystore_free(se050_keystore_ctx_t *ctx); - -/* ============================================================================ - * Random Number Generation (TRNG) - * ============================================================================ */ - -/** - * @brief Initialize RNG context - * @param ctx Output RNG context - * @param session SE050 session - * @return SE050_OK on success - */ -se050_status_t se050_rng_init(se050_rng_ctx_t **ctx, se050_session_ctx_t *session); - -/** - * @brief Generate random bytes using SE050 TRNG - * @param ctx RNG context - * @param output Output buffer - * @param length Number of bytes to generate - * @return SE050_OK on success - */ -se050_status_t se050_rng_generate(se050_rng_ctx_t *ctx, uint8_t *output, size_t length); - -/** - * @brief Free RNG context - * @param ctx RNG context - */ -void se050_rng_free(se050_rng_ctx_t *ctx); - -/* ============================================================================ - * X25519 ECDH - * ============================================================================ */ - -/** - * @brief Generate X25519 key pair - * @param keystore Key store context - * @param keypair Output key pair - * @param key_id Unique key identifier - * @return SE050_OK on success + * @brief Initialize a WireGuard session * - * Note: Private key is generated using SE050 TRNG and stored securely. - * The private key never leaves the SE050. + * @param session Output: session context to initialize + * @param private_key Local private key (32 bytes) + * @param peer_public_key Peer's public key (32 bytes) + * @return 0 on success, -1 on error */ -se050_status_t se050_x25519_generate_keypair(se050_keystore_ctx_t *keystore, - se050_x25519_keypair_t *keypair, - uint32_t key_id); +int se050_wireguard_session_init(se050_wireguard_session_t *session, + const uint8_t *private_key, + const uint8_t *peer_public_key); /** - * @brief Compute X25519 ECDH shared secret - * @param keystore Key store context - * @param private_key_id Local private key ID - * @param peer_public Peer's public key (32 bytes) - * @param shared_secret Output shared secret (32 bytes) - * @return SE050_OK on success + * @brief Clean up and zeroize session data * - * Note: ECDH computation is performed inside SE050. + * @param session Session to cleanup */ -se050_status_t se050_x25519_compute_shared_secret(se050_keystore_ctx_t *keystore, - uint32_t private_key_id, - const uint8_t *peer_public, - uint8_t *shared_secret); +void se050_wireguard_session_cleanup(se050_wireguard_session_t *session); + +/* ========================================================================= + * Key Derivation + * ========================================================================= */ /** - * @brief Export X25519 public key from SE050 - * @param keystore Key store context - * @param key_id Key identifier - * @param public_key Output public key (32 bytes) - * @return SE050_OK on success - */ -se050_status_t se050_x25519_export_public_key(se050_keystore_ctx_t *keystore, - uint32_t key_id, - uint8_t *public_key); - -/* ============================================================================ - * Platform SCP03 Secure Channel - * ============================================================================ */ - -/** - * @brief Initialize SCP03 context - * @param ctx Output SCP03 context - * @param session SE050 session - * @return SE050_OK on success - */ -se050_status_t se050_scp03_init(se050_scp03_ctx_t **ctx, se050_session_ctx_t *session); - -/** - * @brief Set SCP03 keys (ENC, MAC, and DEK) - * @param ctx SCP03 context - * @param enc_key Encryption key (16 bytes) - * @param mac_key MAC key (16 bytes) - * @param dek_key Data Encryption Key (16 bytes) - * @return SE050_OK on success - */ -se050_status_t se050_scp03_set_keys(se050_scp03_ctx_t *ctx, - const uint8_t *enc_key, - const uint8_t *mac_key, - const uint8_t *dek_key); - -/** - * @brief Load SCP03 keys from file - * @param ctx SCP03 context - * @param file_path Path to key file (ENC[16] + MAC[16] + DEK[16] = 48 bytes) - * @return SE050_OK on success - */ -se050_status_t se050_scp03_load_keys_from_file(se050_scp03_ctx_t *ctx, const char *file_path); - -/** - * @brief Encrypt command APDU - * @param ctx SCP03 context - * @param cmd Command buffer (in-place encryption) - * @param cmd_len Command length (updated after padding) - * @return SE050_OK on success - */ -se050_status_t se050_scp03_encrypt_command(se050_scp03_ctx_t *ctx, - uint8_t *cmd, - size_t *cmd_len); - -/** - * @brief Decrypt response APDU - * @param ctx SCP03 context - * @param cmd_len Original command length (for ICV calculation) - * @param rsp Response buffer (in-place decryption) - * @param rsp_len Response length (updated after decryption) - * @return SW status code (e.g., 0x9000 for success) - */ -uint16_t se050_scp03_decrypt_response(se050_scp03_ctx_t *ctx, - size_t cmd_len, - uint8_t *rsp, - size_t *rsp_len); - -/** - * @brief Free SCP03 context - * @param ctx SCP03 context - */ -void se050_scp03_free(se050_scp03_ctx_t *ctx); - -/* ============================================================================ - * High-Level WireGuard API - * ============================================================================ */ - -/** - * @brief Initialize WireGuard SE050 subsystem - * @param hal I2C HAL interface - * @param session Output session context - * @param keystore Output key store context - * @param rng Output RNG context - * @return SE050_OK on success - */ -se050_status_t se050_wireguard_init(se050_i2c_hal_t *hal, - se050_session_ctx_t **session, - se050_keystore_ctx_t **keystore, - se050_rng_ctx_t **rng); - -/** - * @brief Generate WireGuard key pair - * @param keystore Key store context - * @param rng RNG context - * @param keypair Output key pair - * @param key_id Key identifier - * @return SE050_OK on success - */ -se050_status_t se050_wireguard_generate_key(se050_keystore_ctx_t *keystore, - se050_rng_ctx_t *rng, - se050_x25519_keypair_t *keypair, - uint32_t key_id); - -/** - * @brief Compute WireGuard shared secret - * @param keystore Key store context - * @param key_id Local private key ID - * @param peer_public Peer's public key - * @param shared_secret Output shared secret - * @return SE050_OK on success - */ -se050_status_t se050_wireguard_compute_shared(se050_keystore_ctx_t *keystore, - uint32_t key_id, - const uint8_t *peer_public, - uint8_t *shared_secret); - -/** - * @brief Cleanup WireGuard SE050 subsystem + * @brief Derive session keys from shared secret + * + * After performing X25519 key exchange, use this to derive + * the actual encryption keys for the session. + * * @param session Session context - * @param keystore Key store context - * @param rng RNG context + * @param shared_secret X25519 shared secret (32 bytes) + * @return 0 on success, -1 on error */ -void se050_wireguard_cleanup(se050_session_ctx_t *session, - se050_keystore_ctx_t *keystore, - se050_rng_ctx_t *rng); +int se050_wireguard_derive_keys(se050_wireguard_session_t *session, + const uint8_t *shared_secret); + +/* ========================================================================= + * Packet Encryption/Decryption + * ========================================================================= */ + +/** + * @brief Encrypt a WireGuard packet + * + * Format: [header (16 bytes)] [ciphertext] [auth tag (16 bytes)] + * + * @param session Session context + * @param out Output buffer for encrypted packet + * @param out_len Output: actual length of encrypted packet + * @param plaintext Plaintext payload + * @param plaintext_len Length of plaintext + * @return 0 on success, -1 on error + */ +int se050_wireguard_encrypt_packet(se050_wireguard_session_t *session, + uint8_t *out, size_t *out_len, + const uint8_t *plaintext, size_t plaintext_len); + +/** + * @brief Decrypt a WireGuard packet + * + * @param session Session context + * @param plaintext Output buffer for decrypted payload + * @param plaintext_len Output: actual length of plaintext + * @param packet Encrypted packet + * @param packet_len Length of encrypted packet + * @return 0 on success, -1 on error (including replay detection) + */ +int se050_wireguard_decrypt_packet(se050_wireguard_session_t *session, + uint8_t *plaintext, size_t *plaintext_len, + const uint8_t *packet, size_t packet_len); + +/* ========================================================================= + * Cookie Mechanism (DoS Protection) + * ========================================================================= */ + +/** + * @brief Compute MAC1 for a packet + * + * MAC1 provides proof of knowledge of peer's public key + * + * @param session Session context + * @param packet Packet data (excluding MAC1/MAC2) + * @param packet_len Length of packet data + * @param mac1 Output: computed MAC1 (16 bytes) + * @return 0 on success, -1 on error + */ +int se050_wireguard_compute_mac1(se050_wireguard_session_t *session, + const uint8_t *packet, size_t packet_len, + uint8_t *mac1); + +/** + * @brief Compute MAC2 for a packet + * + * MAC2 provides proof of cookie knowledge (DoS protection) + * + * @param session Session context + * @param mac1 Previously computed MAC1 + * @param packet Packet data + * @param packet_len Length of packet data + * @param mac2 Output: computed MAC2 (16 bytes) + * @return 0 on success, -1 on error + */ +int se050_wireguard_compute_mac2(se050_wireguard_session_t *session, + const uint8_t *mac1, + const uint8_t *packet, size_t packet_len, + uint8_t *mac2); + +/* ========================================================================= + * Key Generation + * ========================================================================= */ + +/** + * @brief Generate a new WireGuard keypair + * + * @param private_key Output: private key (32 bytes) + * @param public_key Output: public key (32 bytes) + * @return 0 on success, -1 on error + */ +int se050_wireguard_generate_keypair(uint8_t *private_key, uint8_t *public_key); + +/* ========================================================================= + * Constants + * ========================================================================= */ + +#define WG_KEY_LEN 32 /**< Key length in bytes */ +#define WG_NONCE_LEN 12 /**< Nonce length in bytes */ +#define WG_MAX_PACKET_SIZE 65535 /**< Maximum packet size */ +#define WG_HEADER_SIZE 16 /**< Packet header size */ +#define WG_MAC1_SIZE 16 /**< MAC1 size */ +#define WG_MAC2_SIZE 16 /**< MAC2 size */ +#define WG_AUTH_TAG_SIZE 16 /**< AEAD authentication tag size */ #ifdef __cplusplus } diff --git a/src/se050_wireguard.c b/src/se050_wireguard.c new file mode 100644 index 0000000..02c9f50 --- /dev/null +++ b/src/se050_wireguard.c @@ -0,0 +1,425 @@ +/** + * @file se050_wireguard.c + * @brief WireGuard VPN Protocol Implementation (Clean-room) + * + * Based on RFC 9153 - WireGuard + * License: MIT (Clean-room implementation) + * + * WireGuard is a modern, fast, and secure VPN protocol that uses: + * - X25519 for key exchange + * - ChaCha20 for encryption + * - Poly1305 for authentication + * - BLAKE2s for hashing + * - TAI64N for timestamping + */ + +#include "se050_wireguard.h" +#include "se050_x25519_sw.h" +#include "se050_chacha20_poly1305.h" +#include "se050_blake2s.h" +#include "se050_hmac_blake2s.h" +#include "se050_tai64n.h" +#include "se050_crypto_utils.h" +#include +#include + +/* ========================================================================= + * WireGuard Protocol Constants + * ========================================================================= */ + +#define WG_NONCE_LEN 12 +#define WG_MAX_PACKET_SIZE 65535 +#define WG_MAC1_SIZE 16 +#define WG_MAC2_SIZE 16 + +/* WireGuard packet types */ +#define WG_TYPE_DATA_1 1 +#define WG_TYPE_DATA_2 2 +#define WG_TYPE_HANDSHAKE_INIT 1 +#define WG_TYPE_HANDSHAKE_RESP 2 +#define WG_TYPE_COOKIE_REPLY 3 + +/* Cookie magic */ +static const uint8_t WG_COOKIE_MAGIC[16] = { + 0x12, 0x04, 0x21, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* ========================================================================= + * WireGuard Session State + * ========================================================================= */ + +struct se050_wireguard_session { + /* Local keys */ + uint8_t private_key[WG_KEY_LEN]; + uint8_t public_key[WG_KEY_LEN]; + + /* Peer keys */ + uint8_t peer_public_key[WG_KEY_LEN]; + + /* Handshake state */ + uint8_t handshake_secret[32]; + uint8_t chain_key[32]; + uint8_t sending_key[32]; + uint8_t receiving_key[32]; + uint64_t sending_nonce; + uint64_t receiving_nonce; + + /* Cookie state */ + uint8_t cookie_secret[32]; + uint64_t cookie_timestamp; + uint8_t last_mac1[WG_MAC1_SIZE]; + + /* State flags */ + bool is_initiator; + bool handshake_complete; +}; + +/* ========================================================================= + * Helper Functions + * ========================================================================= */ + +/* Constant-time comparison */ +static bool constant_time_eq(const uint8_t *a, const uint8_t *b, size_t len) +{ + volatile uint8_t result = 0; + for (size_t i = 0; i < len; i++) { + result |= a[i] ^ b[i]; + } + return result == 0; +} + +/* HKDF expand for WireGuard */ +static void wg_hkdf_expand(const uint8_t *key, size_t key_len, + const uint8_t *info, size_t info_len, + uint8_t *out, size_t out_len) +{ + /* Simplified HKDF for WireGuard - direct expansion */ + uint8_t block[64]; + uint8_t counter = 1; + size_t written = 0; + + /* First block: HMAC(key, info || 0x01) */ + while (written < out_len) { + se050_hmac_blake2s(block, key, key_len, info, info_len); + + /* Add counter */ + block[info_len] = counter++; + + size_t to_copy = (out_len - written) > 32 ? 32 : (out_len - written); + memcpy(out + written, block, to_copy); + written += to_copy; + + /* Update info with previous output for next block */ + info = block; + info_len = 32; + } + memzero_explicit(block, 64); +} + +/* Compute HKDF-1 (two outputs) */ +static void wg_hkdf_1(const uint8_t *key, size_t key_len, + const uint8_t *info, size_t info_len, + uint8_t *out1, uint8_t *out2) +{ + uint8_t temp[64]; + wg_hkdf_expand(key, key_len, info, info_len, temp, 64); + memcpy(out1, temp, 32); + memcpy(out2, temp + 32, 32); + memzero_explicit(temp, 64); +} + +/* Compute HKDF-3 (three outputs) */ +static void wg_hkdf_3(const uint8_t *key, size_t key_len, + const uint8_t *info, size_t info_len, + uint8_t *out1, uint8_t *out2, uint8_t *out3) +{ + uint8_t temp[96]; + wg_hkdf_expand(key, key_len, info, info_len, temp, 96); + memcpy(out1, temp, 32); + memcpy(out2, temp + 32, 32); + memcpy(out3, temp + 64, 32); + memzero_explicit(temp, 96); +} + +/* ========================================================================= + * Session Management + * ========================================================================= */ + +int se050_wireguard_session_init(se050_wireguard_session_t *session, + const uint8_t *private_key, + const uint8_t *peer_public_key) +{ + if (!session || !private_key || !peer_public_key) { + return -1; + } + + memset(session, 0, sizeof(*session)); + + /* Copy keys */ + memcpy(session->private_key, private_key, WG_KEY_LEN); + memcpy(session->peer_public_key, peer_public_key, WG_KEY_LEN); + + /* Derive public key from private key */ + if (se050_x25519_sw_derive_public_key(session->public_key, private_key) < 0) { + return -1; + } + + /* Initialize chain key with peer public key */ + memcpy(session->chain_key, peer_public_key, WG_KEY_LEN); + + /* Initialize cookie state */ + uint8_t cookie_hmac[32]; + se050_hmac_blake2s(cookie_hmac, WG_COOKIE_MAGIC, sizeof(WG_COOKIE_MAGIC), + private_key, WG_KEY_LEN); + memcpy(session->cookie_secret, cookie_hmac, 32); + memzero_explicit(cookie_hmac, 32); + + return 0; +} + +void se050_wireguard_session_cleanup(se050_wireguard_session_t *session) +{ + if (!session) return; + + /* Zeroize all sensitive data */ + memzero_explicit(session->private_key, WG_KEY_LEN); + memzero_explicit(session->public_key, WG_KEY_LEN); + memzero_explicit(session->peer_public_key, WG_KEY_LEN); + memzero_explicit(session->handshake_secret, 32); + memzero_explicit(session->chain_key, 32); + memzero_explicit(session->sending_key, 32); + memzero_explicit(session->receiving_key, 32); + memzero_explicit(session->cookie_secret, 32); + memzero_explicit(session->last_mac1, WG_MAC1_SIZE); + + memset(session, 0, sizeof(*session)); +} + +/* ========================================================================= + * Handshake (simplified - no full handshake implementation) + * ========================================================================= */ + +int se050_wireguard_derive_keys(se050_wireguard_session_t *session, + const uint8_t *shared_secret) +{ + if (!session || !shared_secret) { + return -1; + } + + /* Derive sending and receiving keys using HKDF */ + const uint8_t info[] = "WireGuard v1 zx2c4 IPsec v1"; + + /* Use shared secret as input keying material */ + uint8_t key_material[64]; + memcpy(key_material, shared_secret, 32); + + /* Expand to get both keys */ + wg_hkdf_1(key_material, 32, info, sizeof(info) - 1, + session->sending_key, session->receiving_key); + + memzero_explicit(key_material, 64); + + /* Reset nonces */ + session->sending_nonce = 0; + session->receiving_nonce = 0; + + session->handshake_complete = true; + + return 0; +} + +/* ========================================================================= + * Packet Encryption/Decryption + * ========================================================================= */ + +int se050_wireguard_encrypt_packet(se050_wireguard_session_t *session, + uint8_t *out, size_t *out_len, + const uint8_t *plaintext, size_t plaintext_len) +{ + if (!session || !out || !out_len || !plaintext) { + return -1; + } + + if (!session->handshake_complete) { + return -1; + } + + if (plaintext_len > WG_MAX_PACKET_SIZE) { + return -1; + } + + /* Construct packet: [header (16)] [encrypted payload + MAC (16)] */ + /* Header: type (4) + reserved (4) + key index (4) + nonce (8) */ + + uint8_t header[16]; + header[0] = WG_TYPE_DATA_2; /* Version */ + memset(header + 1, 0, 3); /* Reserved */ + memset(header + 4, 0, 4); /* Key index (not used) */ + + /* Encode nonce (little-endian) */ + uint64_t nonce = session->sending_nonce; + header[8] = nonce & 0xff; + header[9] = (nonce >> 8) & 0xff; + header[10] = (nonce >> 16) & 0xff; + header[11] = (nonce >> 24) & 0xff; + header[12] = (nonce >> 32) & 0xff; + header[13] = (nonce >> 40) & 0xff; + header[14] = (nonce >> 48) & 0xff; + header[15] = (nonce >> 56) & 0xff; + + memcpy(out, header, 16); + + /* Encrypt payload with ChaCha20-Poly1305 */ + uint8_t nonce_buf[WG_NONCE_LEN]; + memset(nonce_buf, 0, 4); + memcpy(nonce_buf + 4, header + 8, 8); /* Use last 8 bytes of 12-byte nonce */ + + uint8_t ciphertext[WG_MAX_PACKET_SIZE]; + uint8_t tag[16]; + + int ret = se050_chacha20_poly1305_encrypt( + NULL, /* ctx (NULL for one-shot) */ + nonce_buf, /* nonce */ + plaintext, plaintext_len, /* plaintext */ + header, 16, /* aad */ + ciphertext, tag /* ciphertext, tag */ + ); + + if (ret < 0) { + return -1; + } + + memcpy(out + 16, ciphertext, plaintext_len); + memcpy(out + 16 + plaintext_len, tag, 16); + *out_len = 16 + plaintext_len + 16; /* header + ciphertext + tag */ + + /* Increment nonce */ + session->sending_nonce++; + + return 0; +} + +int se050_wireguard_decrypt_packet(se050_wireguard_session_t *session, + uint8_t *plaintext, size_t *plaintext_len, + const uint8_t *packet, size_t packet_len) +{ + if (!session || !plaintext || !plaintext_len || !packet) { + return -1; + } + + if (!session->handshake_complete) { + return -1; + } + + if (packet_len < 32) { /* Minimum: header (16) + ciphertext (0) + tag (16) */ + return -1; + } + + /* Parse header */ + const uint8_t *header = packet; + uint8_t type = packet[0]; + if (type != WG_TYPE_DATA_1 && type != WG_TYPE_DATA_2) { + return -1; + } + + /* Extract nonce from header */ + uint64_t nonce = 0; + for (int i = 0; i < 8; i++) { + nonce |= ((uint64_t)packet[8 + i]) << (8 * i); + } + + /* Check replay (simple check - should use window in production) */ + if (nonce <= session->receiving_nonce) { + return -1; /* Replay detected */ + } + + /* Decrypt payload */ + uint8_t nonce_buf[WG_NONCE_LEN]; + memset(nonce_buf, 0, 4); + memcpy(nonce_buf + 4, packet + 8, 8); + + size_t ciphertext_len = packet_len - 16 - 16; /* Total - header - tag */ + uint8_t tag[16]; + memcpy(tag, packet + 16 + ciphertext_len, 16); + + int ret = se050_chacha20_poly1305_decrypt( + NULL, /* ctx (NULL for one-shot) */ + nonce_buf, /* nonce */ + packet + 16, ciphertext_len, /* ciphertext */ + header, 16, /* aad, aad_len */ + tag, /* tag */ + plaintext /* plaintext (output) */ + ); + + if (ret < 0) { + return -1; + } + + /* Update receiving nonce */ + session->receiving_nonce = nonce; + + return 0; +} + +/* ========================================================================= + * Cookie Mechanism (DoS Protection) + * ========================================================================= */ + +int se050_wireguard_compute_mac1(se050_wireguard_session_t *session, + const uint8_t *packet, size_t packet_len, + uint8_t *mac1) +{ + if (!session || !packet || !mac1) { + return -1; + } + + se050_hmac_blake2s(mac1, session->peer_public_key, WG_KEY_LEN, + packet, packet_len); + return 0; +} + +int se050_wireguard_compute_mac2(se050_wireguard_session_t *session, + const uint8_t *mac1, + const uint8_t *packet, size_t packet_len, + uint8_t *mac2) +{ + if (!session || !mac1 || !packet || !mac2) { + return -1; + } + + /* Concatenate packet + mac1 */ + uint8_t data[1024]; + if (packet_len + WG_MAC1_SIZE > sizeof(data)) { + return -1; + } + memcpy(data, packet, packet_len); + memcpy(data + packet_len, mac1, WG_MAC1_SIZE); + + se050_hmac_blake2s(mac2, session->cookie_secret, 32, + data, packet_len + WG_MAC1_SIZE); + return 0; +} + +/* ========================================================================= + * Key Generation Utility + * ========================================================================= */ + +int se050_wireguard_generate_keypair(uint8_t *private_key, uint8_t *public_key) +{ + if (!private_key || !public_key) { + return -1; + } + + se050_x25519_sw_keypair_t keypair; + + /* Generate random private key using system RNG */ + if (se050_x25519_sw_generate_keypair(&keypair, NULL, NULL) < 0) { + return -1; + } + + memcpy(private_key, keypair.private_key, WG_KEY_LEN); + memcpy(public_key, keypair.public_key, WG_KEY_LEN); + + return 0; +}