feat: Add CSPRNG with SE050 seed for embedded platforms
- Implemented ChaCha20-based CSPRNG seeded from SE050 TRNG - Optimized for ESP32 and other embedded platforms - Single SE050 access at startup, then fast software RNG - All 10 CSPRNG tests passing Usage: Benefits: - Minimal I2C communication (only once at startup) - Fast random generation after seeding - Cryptographically secure (ChaCha20-based) - Suitable for resource-constrained devices
This commit is contained in:
@@ -28,6 +28,7 @@ set(SOURCES
|
||||
src/se050_tai64n_hw.c
|
||||
src/se050_wireguard.c
|
||||
src/se050_wireguard_se050_rng.c
|
||||
src/se050_rng_seed.c
|
||||
)
|
||||
|
||||
# Create library
|
||||
|
||||
@@ -172,6 +172,7 @@ int se050_wireguard_compute_mac2(se050_wireguard_session_t *session,
|
||||
*
|
||||
* Uses system RNG (/dev/urandom on POSIX).
|
||||
* For SE050 hardware RNG, use se050_wireguard_generate_keypair_se050().
|
||||
* For CSPRNG (seeded from SE050), use se050_wireguard_generate_keypair_csprng().
|
||||
*
|
||||
* @param private_key Output: private key (32 bytes)
|
||||
* @param public_key Output: public key (32 bytes)
|
||||
@@ -196,6 +197,44 @@ int se050_wireguard_generate_keypair_se050(se050_session_ctx_t *session,
|
||||
uint8_t *public_key);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize CSPRNG with seed from SE050
|
||||
*
|
||||
* This should be called once at system startup. After initialization,
|
||||
* the CSPRNG can generate random numbers without further SE050 access.
|
||||
*
|
||||
* @param seed_func Function to get seed from SE050 (called once)
|
||||
* @param seed_ctx Context for seed function
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int se050_csprng_init(int (*seed_func)(uint8_t *out, size_t len, void *ctx), void *seed_ctx);
|
||||
|
||||
/**
|
||||
* @brief Generate WireGuard keypair using CSPRNG
|
||||
*
|
||||
* After calling se050_csprng_init(), use this function to generate keypairs.
|
||||
* This is ideal for ESP32 and other embedded platforms where I2C access should be minimized.
|
||||
*
|
||||
* @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_csprng(uint8_t *private_key, uint8_t *public_key);
|
||||
|
||||
/**
|
||||
* @brief Generate random bytes using CSPRNG
|
||||
*
|
||||
* @param out Output buffer
|
||||
* @param len Number of bytes to generate
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int se050_csprng_random(uint8_t *out, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Cleanup CSPRNG and zeroize sensitive data
|
||||
*/
|
||||
void se050_csprng_cleanup(void);
|
||||
|
||||
/* =========================================================================
|
||||
* Constants
|
||||
* ========================================================================= */
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* @file se050_rng_seed.c
|
||||
* @brief SE050-based CSPRNG with initial seed
|
||||
*
|
||||
* This module provides a cryptographically secure pseudo-random number generator
|
||||
* that is seeded once from SE050 hardware TRNG at startup, then uses ChaCha20
|
||||
* to generate the rest of the random stream.
|
||||
*
|
||||
* Benefits:
|
||||
* - Minimal I2C communication (only once at startup)
|
||||
* - Fast random generation after seeding
|
||||
* - Cryptographically secure (ChaCha20-based)
|
||||
* - Suitable for ESP32 and other embedded platforms
|
||||
*
|
||||
* License: MIT (Clean-room implementation)
|
||||
*/
|
||||
|
||||
#include "se050_wireguard.h"
|
||||
#include "se050_x25519_sw.h"
|
||||
#include "se050_chacha20_poly1305.h"
|
||||
#include "se050_crypto_utils.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* ============================================================================
|
||||
* Constants
|
||||
* ============================================================================ */
|
||||
|
||||
#define SEED_SIZE 32
|
||||
#define COUNTER_SIZE 4
|
||||
#define NONCE_SIZE 12
|
||||
#define KEY_SIZE 32
|
||||
|
||||
/* ============================================================================
|
||||
* CSPRNG Context
|
||||
* ============================================================================ */
|
||||
|
||||
typedef struct {
|
||||
uint8_t key[KEY_SIZE]; /* ChaCha20 key (from SE050 seed) */
|
||||
uint32_t counter; /* Stream counter */
|
||||
uint8_t buffer[64]; /* Current block buffer */
|
||||
size_t buffer_pos; /* Current position in buffer */
|
||||
int initialized; /* Initialization flag */
|
||||
} se050_csprng_ctx_t;
|
||||
|
||||
/* Global CSPRNG instance */
|
||||
static se050_csprng_ctx_t g_rng;
|
||||
|
||||
/* ============================================================================
|
||||
* ChaCha20-based CSPRNG
|
||||
* ============================================================================ */
|
||||
|
||||
/**
|
||||
* @brief Generate a new ChaCha20 block
|
||||
*/
|
||||
static void csprng_generate_block(se050_csprng_ctx_t *ctx)
|
||||
{
|
||||
uint8_t nonce[NONCE_SIZE] = {0};
|
||||
|
||||
/* Set counter in nonce (last 4 bytes) */
|
||||
nonce[8] = (ctx->counter >> 0) & 0xff;
|
||||
nonce[9] = (ctx->counter >> 8) & 0xff;
|
||||
nonce[10] = (ctx->counter >> 16) & 0xff;
|
||||
nonce[11] = (ctx->counter >> 24) & 0xff;
|
||||
ctx->counter++;
|
||||
|
||||
/* Generate ChaCha20 block */
|
||||
se050_chacha20_block(ctx->buffer, ctx->key, 0, nonce);
|
||||
ctx->buffer_pos = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate random bytes using ChaCha20 stream
|
||||
*/
|
||||
static int csprng_generate(uint8_t *out, size_t len)
|
||||
{
|
||||
if (!g_rng.initialized) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!out || len == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (g_rng.buffer_pos >= 64) {
|
||||
csprng_generate_block(&g_rng);
|
||||
}
|
||||
out[i] = g_rng.buffer[g_rng.buffer_pos++];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* SE050 Seed Integration
|
||||
* ============================================================================ */
|
||||
|
||||
/**
|
||||
* @brief Callback type for SE050 RNG
|
||||
*/
|
||||
typedef int (*se050_rng_func_t)(uint8_t *out, size_t len, void *ctx);
|
||||
|
||||
/**
|
||||
* @brief Initialize CSPRNG with seed from SE050
|
||||
*
|
||||
* @param seed_func Function to get seed from SE050
|
||||
* @param seed_ctx Context for seed function
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int se050_csprng_init(se050_rng_func_t seed_func, void *seed_ctx)
|
||||
{
|
||||
if (!seed_func) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Zeroize context first */
|
||||
memset(&g_rng, 0, sizeof(g_rng));
|
||||
|
||||
/* Get seed from SE050 */
|
||||
uint8_t seed[SEED_SIZE];
|
||||
if (seed_func(seed, SEED_SIZE, seed_ctx) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Use seed as ChaCha20 key */
|
||||
memcpy(g_rng.key, seed, KEY_SIZE);
|
||||
|
||||
/* Clear seed from memory */
|
||||
memzero_explicit(seed, SEED_SIZE);
|
||||
|
||||
/* Initialize counter and buffer */
|
||||
g_rng.counter = 0;
|
||||
g_rng.buffer_pos = 64; /* Force initial block generation */
|
||||
g_rng.initialized = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate random bytes (public API)
|
||||
*/
|
||||
int se050_csprng_generate(uint8_t *out, size_t len)
|
||||
{
|
||||
return csprng_generate(out, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Securely zeroize and cleanup CSPRNG
|
||||
*/
|
||||
void se050_csprng_cleanup(void)
|
||||
{
|
||||
if (g_rng.initialized) {
|
||||
memzero_explicit(g_rng.key, KEY_SIZE);
|
||||
memzero_explicit(g_rng.buffer, 64);
|
||||
g_rng.initialized = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* WireGuard Key Generation with CSPRNG
|
||||
* ============================================================================ */
|
||||
|
||||
/**
|
||||
* @brief RNG wrapper for x25519 keypair generation using CSPRNG
|
||||
*/
|
||||
static int csprng_wrapper(uint8_t *out, size_t len, void *ctx)
|
||||
{
|
||||
(void)ctx; /* Unused */
|
||||
return csprng_generate(out, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate WireGuard keypair using seeded CSPRNG
|
||||
*
|
||||
* @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_csprng(uint8_t *private_key, uint8_t *public_key)
|
||||
{
|
||||
if (!private_key || !public_key) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!g_rng.initialized) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
se050_x25519_sw_keypair_t keypair;
|
||||
|
||||
if (se050_x25519_sw_generate_keypair(&keypair, csprng_wrapper, NULL) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(private_key, keypair.private_key, 32);
|
||||
memcpy(public_key, keypair.public_key, 32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate random bytes for general use
|
||||
*/
|
||||
int se050_csprng_random(uint8_t *out, size_t len)
|
||||
{
|
||||
return csprng_generate(out, len);
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* @file test_csprng.c
|
||||
* @brief CSPRNG Tests
|
||||
*/
|
||||
|
||||
#include "se050_wireguard.h"
|
||||
#include "se050_chacha20_poly1305.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static int passed = 0;
|
||||
static int failed = 0;
|
||||
|
||||
#define TEST_ASSERT(cond, msg) do { \
|
||||
if (cond) { \
|
||||
printf("[PASS] %s\n", msg); \
|
||||
passed++; \
|
||||
} else { \
|
||||
printf("[FAIL] %s\n", msg); \
|
||||
failed++; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* Mock SE050 RNG for testing */
|
||||
static int mock_se050_rng(uint8_t *out, size_t len, void *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
/* Generate deterministic "seed" for testing */
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
out[i] = (uint8_t)(i + 0x42);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Test: CSPRNG initialization */
|
||||
static void test_csprng_init(void)
|
||||
{
|
||||
printf("\n--- Test CSPRNG Initialization ---\n");
|
||||
|
||||
int ret = se050_csprng_init(mock_se050_rng, NULL);
|
||||
TEST_ASSERT(ret == 0, "CSPRNG init returns 0");
|
||||
|
||||
se050_csprng_cleanup();
|
||||
}
|
||||
|
||||
/* Test: CSPRNG generates non-zero data */
|
||||
static void test_csprng_output(void)
|
||||
{
|
||||
printf("\n--- Test CSPRNG Output ---\n");
|
||||
|
||||
se050_csprng_init(mock_se050_rng, NULL);
|
||||
|
||||
uint8_t rand1[32];
|
||||
int ret = se050_csprng_random(rand1, 32);
|
||||
TEST_ASSERT(ret == 0, "Random generation returns 0");
|
||||
|
||||
uint8_t all_zero = 1;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (rand1[i] != 0) all_zero = 0;
|
||||
}
|
||||
TEST_ASSERT(all_zero == 0, "Random data is non-zero");
|
||||
|
||||
se050_csprng_cleanup();
|
||||
}
|
||||
|
||||
/* Test: CSPRNG generates different values */
|
||||
static void test_csprng_uniqueness(void)
|
||||
{
|
||||
printf("\n--- Test CSPRNG Uniqueness ---\n");
|
||||
|
||||
se050_csprng_init(mock_se050_rng, NULL);
|
||||
|
||||
uint8_t rand1[32], rand2[32];
|
||||
se050_csprng_random(rand1, 32);
|
||||
se050_csprng_random(rand2, 32);
|
||||
|
||||
TEST_ASSERT(memcmp(rand1, rand2, 32) != 0, "Successive calls produce different values");
|
||||
|
||||
se050_csprng_cleanup();
|
||||
}
|
||||
|
||||
/* Test: CSPRNG keypair generation */
|
||||
static void test_csprng_keypair(void)
|
||||
{
|
||||
printf("\n--- Test CSPRNG Keypair ---\n");
|
||||
|
||||
se050_csprng_init(mock_se050_rng, NULL);
|
||||
|
||||
uint8_t priv[32], pub[32];
|
||||
int ret = se050_wireguard_generate_keypair_csprng(priv, pub);
|
||||
TEST_ASSERT(ret == 0, "Keypair generation returns 0");
|
||||
|
||||
uint8_t priv_zero = 1, pub_zero = 1;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (priv[i] != 0) priv_zero = 0;
|
||||
if (pub[i] != 0) pub_zero = 0;
|
||||
}
|
||||
TEST_ASSERT(priv_zero == 0, "Private key is non-zero");
|
||||
TEST_ASSERT(pub_zero == 0, "Public key is non-zero");
|
||||
|
||||
se050_csprng_cleanup();
|
||||
}
|
||||
|
||||
/* Test: CSPRNG cleanup zeros memory */
|
||||
static void test_csprng_cleanup(void)
|
||||
{
|
||||
printf("\n--- Test CSPRNG Cleanup ---\n");
|
||||
|
||||
se050_csprng_init(mock_se050_rng, NULL);
|
||||
se050_csprng_cleanup();
|
||||
|
||||
/* Try to generate after cleanup - should fail */
|
||||
uint8_t rand[32];
|
||||
int ret = se050_csprng_random(rand, 32);
|
||||
TEST_ASSERT(ret != 0, "Generation fails after cleanup");
|
||||
}
|
||||
|
||||
/* Test: Multiple keypairs from same seed */
|
||||
static void test_multiple_keypairs(void)
|
||||
{
|
||||
printf("\n--- Test Multiple Keypairs ---\n");
|
||||
|
||||
se050_csprng_init(mock_se050_rng, NULL);
|
||||
|
||||
uint8_t priv1[32], pub1[32];
|
||||
uint8_t priv2[32], pub2[32];
|
||||
|
||||
se050_wireguard_generate_keypair_csprng(priv1, pub1);
|
||||
se050_wireguard_generate_keypair_csprng(priv2, pub2);
|
||||
|
||||
TEST_ASSERT(memcmp(priv1, priv2, 32) != 0, "Multiple private keys are different");
|
||||
TEST_ASSERT(memcmp(pub1, pub2, 32) != 0, "Multiple public keys are different");
|
||||
|
||||
se050_csprng_cleanup();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("========================================\n");
|
||||
printf(" CSPRNG Test Suite\n");
|
||||
printf("========================================\n");
|
||||
|
||||
test_csprng_init();
|
||||
test_csprng_output();
|
||||
test_csprng_uniqueness();
|
||||
test_csprng_keypair();
|
||||
test_csprng_cleanup();
|
||||
test_multiple_keypairs();
|
||||
|
||||
printf("\n========================================\n");
|
||||
printf(" Results: %d passed, %d failed\n", passed, failed);
|
||||
printf("========================================\n");
|
||||
|
||||
return failed == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
Reference in New Issue
Block a user