Compare commits
70 Commits
172873cf39
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 11bcc5e0c3 | |||
| 479fcd37c1 | |||
| 675e452071 | |||
| 43643bc4cf | |||
| 760b37690e | |||
| 7ef235d5b1 | |||
| 77c6dfbf1a | |||
| a430accd11 | |||
| 2ec7829b52 | |||
| 42e6222637 | |||
| 7c2c6d94bf | |||
| d5ca4b3634 | |||
| 2f76e7cb09 | |||
| eac7fc9d82 | |||
| 3645b4fe80 | |||
| 4fae20f56d | |||
| 63bc460db4 | |||
| cbcfba7347 | |||
| 0210082b8c | |||
| 999e7a6e19 | |||
| 1894e9a933 | |||
| 4ec660de02 | |||
| 09620ba4ef | |||
| 77c3258494 | |||
| 90be06ead1 | |||
| d2081b3a9e | |||
| a8d28882c7 | |||
| f6298c7725 | |||
| 9d0af4d65a | |||
| c31809f37d | |||
| c61433d75b | |||
| 50884811ca | |||
| d4085b2073 | |||
| bd762864e6 | |||
| 344f86b07f | |||
| f23542f06c | |||
| fb8e3a73d7 | |||
| 2d0f7959d0 | |||
| c9844dc0ba | |||
| 0c9237324e | |||
| c892e6ca01 | |||
| b83394f37b | |||
| a6defdad88 | |||
| c4b567ad97 | |||
| b318484a02 | |||
| 323460c631 | |||
| 9824b8f3e5 | |||
| 6484b70955 | |||
| eef99d31a1 | |||
| 35333c297f | |||
| fb51a4ad9f | |||
| d34fed2048 | |||
| feb99ffe4e | |||
| 7034b67c04 | |||
| 04231683c2 | |||
| dfadaf092c | |||
| ff32a1052f | |||
| f7b9581428 | |||
| ba444679ab | |||
| 46d0a1a4b4 | |||
| 5434aa5197 | |||
| aff6c301e6 | |||
| eb468c1ba1 | |||
| 940929540a | |||
| 74789be2c3 | |||
| 163fad68a7 | |||
| daffe82feb | |||
| 0a97209e8c | |||
| 2ad959bde9 | |||
| f89ca4f471 |
@@ -1 +1,4 @@
|
|||||||
build/
|
build/
|
||||||
|
*.o
|
||||||
|
src/*.o
|
||||||
|
tests/*.o
|
||||||
|
|||||||
@@ -16,7 +16,19 @@ set(SOURCES
|
|||||||
src/se050_keystore.c
|
src/se050_keystore.c
|
||||||
src/se050_rng.c
|
src/se050_rng.c
|
||||||
src/se050_x25519.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.c
|
||||||
|
src/se050_scp03_keys.c
|
||||||
|
src/se050_wireguard_proto.c
|
||||||
|
src/se050_tai64n_hw.c
|
||||||
|
src/se050_wireguard.c
|
||||||
|
src/se050_wireguard_se050_rng.c
|
||||||
|
src/se050_rng_seed.c
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create library
|
# Create library
|
||||||
@@ -62,3 +74,7 @@ install(FILES include/se050_wireguard.h
|
|||||||
# Install library
|
# Install library
|
||||||
install(TARGETS se050_wireguard
|
install(TARGETS se050_wireguard
|
||||||
ARCHIVE DESTINATION lib)
|
ARCHIVE DESTINATION lib)
|
||||||
|
|
||||||
|
# Note: For embedded platforms (ESP32, u-boot), replace system_rng() with
|
||||||
|
# platform-specific RNG (e.g., get_random_bytes() for ESP32)
|
||||||
|
# See se050_wireguard.c for details.
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# SE050 WireGuard Makefile
|
||||||
|
CC = gcc
|
||||||
|
AR = ar
|
||||||
|
CFLAGS = -Wall -Wextra -std=c11 -I include
|
||||||
|
LDFLAGS =
|
||||||
|
|
||||||
|
SRCS = src/se050_i2c_hal.c src/se050_mem_pool.c src/se050_session.c 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
|
||||||
|
|
||||||
|
OBJS = $(SRCS:.c=.o)
|
||||||
|
LIB = libse050_wireguard.a
|
||||||
|
|
||||||
|
.PHONY: all test clean
|
||||||
|
|
||||||
|
all: $(LIB) test_blake2s test_hmac_blake2s test_hkdf_blake2s
|
||||||
|
|
||||||
|
$(LIB): $(OBJS)
|
||||||
|
@mkdir -p build
|
||||||
|
$(AR) rcs build/$@ $^
|
||||||
|
|
||||||
|
# WireGuard protocol test
|
||||||
|
test_wireguard: tests/test_wireguard.c $(LIB)
|
||||||
|
@mkdir -p build
|
||||||
|
$(CC) $(CFLAGS) -o build/$@ $< build/$(LIB)
|
||||||
|
|
||||||
|
test: all test_wireguard
|
||||||
|
@./build/test_blake2s
|
||||||
|
@./build/test_hmac_blake2s
|
||||||
|
@./build/test_hkdf_blake2s
|
||||||
|
@./build/test_wireguard
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build *.o src/*.o tests/*.o
|
||||||
@@ -20,10 +20,24 @@ MIT ライセンスで実装された NXP SE050 用 WireGuard クリーンルー
|
|||||||
- ✅ AN12436 デフォルトキー実装
|
- ✅ AN12436 デフォルトキー実装
|
||||||
- ✅ キー管理 API (`se050_scp03_load_keys_from_file`)
|
- ✅ キー管理 API (`se050_scp03_load_keys_from_file`)
|
||||||
- ✅ 安全なメモリ操作 (`memzero_explicit`, `crypto_memneq`, `secure_memcpy`)
|
- ✅ 安全なメモリ操作 (`memzero_explicit`, `crypto_memneq`, `secure_memcpy`)
|
||||||
|
- ✅ **SE050 ハードウェア接続テスト** (chip 選択可能)
|
||||||
|
|
||||||
### 対応チップ
|
### 対応チップ
|
||||||
|
|
||||||
- SE050C0, SE050C1, SE050E0, SE050E1 (すべて X25519 サポート)
|
- **SE050C1**, **SE050C2**, **SE050E2**(すべて X25519/Montgomery 曲線サポート)
|
||||||
|
|
||||||
|
### ハードウェアテスト
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SE050C1 でテスト
|
||||||
|
make SE050_CHIP=SE050C1 test_se050
|
||||||
|
|
||||||
|
# SE050E2 でテスト
|
||||||
|
make SE050_CHIP=SE050E2 test_se050
|
||||||
|
|
||||||
|
# 別の I2C バス指定
|
||||||
|
make SE050_CHIP=SE050C1 test_se050 I2C_OPTS="-b /dev/i2c-2"
|
||||||
|
```
|
||||||
|
|
||||||
### 参照ドキュメント
|
### 参照ドキュメント
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_blake2s.h
|
||||||
|
* @brief BLAKE2s Hash Function Implementation
|
||||||
|
*
|
||||||
|
* Based on RFC 7693. Supports variable-length keys and outputs.
|
||||||
|
* Used in WireGuard for key derivation.
|
||||||
|
*
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_BLAKE2S_H
|
||||||
|
#define SE050_BLAKE2S_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Constants
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#define BLAKE2S_BLOCK_SIZE 64
|
||||||
|
#define BLAKE2S_DIGEST_SIZE 32
|
||||||
|
#define BLAKE2S_KEY_SIZE 64
|
||||||
|
#define BLAKE2S_MIN_KEY_SIZE 1
|
||||||
|
#define BLAKE2S_MAX_KEY_SIZE 64
|
||||||
|
#define BLAKE2S_MIN_OUTLEN 1
|
||||||
|
#define BLAKE2S_MAX_OUTLEN 32
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Type Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief BLAKE2s context
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t h[8]; /* Hash state */
|
||||||
|
uint32_t t[2]; /* Counter */
|
||||||
|
uint32_t f[2]; /* Block flag */
|
||||||
|
uint8_t buf[BLAKE2S_BLOCK_SIZE]; /* Input buffer */
|
||||||
|
size_t buflen; /* Current buffer size */
|
||||||
|
size_t outlen; /* Desired output length */
|
||||||
|
uint8_t last_node; /* Last node flag */
|
||||||
|
} se050_blake2s_ctx_t;
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* API Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize BLAKE2s context
|
||||||
|
*
|
||||||
|
* @param ctx Context to initialize
|
||||||
|
* @param outlen Output length (1-32 bytes)
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_blake2s_init(se050_blake2s_ctx_t *ctx, size_t outlen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize BLAKE2s with key
|
||||||
|
*
|
||||||
|
* @param ctx Context to initialize
|
||||||
|
* @param outlen Output length (1-32 bytes)
|
||||||
|
* @param key Key (1-64 bytes)
|
||||||
|
* @param keylen Key length
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_blake2s_init_key(se050_blake2s_ctx_t *ctx, size_t outlen,
|
||||||
|
const void *key, size_t keylen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update hash with data
|
||||||
|
*
|
||||||
|
* @param ctx Context
|
||||||
|
* @param data Data to hash
|
||||||
|
* @param len Data length
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_blake2s_update(se050_blake2s_ctx_t *ctx, const void *data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Finalize hash and get digest
|
||||||
|
*
|
||||||
|
* @param ctx Context
|
||||||
|
* @param out Output buffer (at least outlen bytes)
|
||||||
|
* @param outlen Output length
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_blake2s_final(se050_blake2s_ctx_t *ctx, void *out, size_t outlen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compute BLAKE2s hash (one-shot)
|
||||||
|
*
|
||||||
|
* @param out Output buffer (at least outlen bytes)
|
||||||
|
* @param outlen Output length
|
||||||
|
* @param data Data to hash
|
||||||
|
* @param len Data length
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_blake2s(void *out, size_t outlen, const void *data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compute BLAKE2s hash with key (one-shot)
|
||||||
|
*
|
||||||
|
* @param out Output buffer (at least outlen bytes)
|
||||||
|
* @param outlen Output length
|
||||||
|
* @param key Key
|
||||||
|
* @param keylen Key length
|
||||||
|
* @param data Data to hash
|
||||||
|
* @param len Data length
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_blake2s_keyed(void *out, size_t outlen, const void *key, size_t keylen,
|
||||||
|
const void *data, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Securely zeroize context
|
||||||
|
*
|
||||||
|
* @param ctx Context to zeroize
|
||||||
|
*/
|
||||||
|
void se050_blake2s_zeroize(se050_blake2s_ctx_t *ctx);
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* WireGuard-Specific Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief WireGuard key derivation using BLAKE2s
|
||||||
|
*
|
||||||
|
* Computes: BLAKE2s("wireguard key derivation", input, 32)
|
||||||
|
*
|
||||||
|
* @param out Output (32 bytes)
|
||||||
|
* @param input Input data
|
||||||
|
* @param inlen Input length
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_wireguard_derive_key(uint8_t out[32], const uint8_t *input, size_t inlen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief WireGuard secret key generation
|
||||||
|
*
|
||||||
|
* @param out Output (32 bytes)
|
||||||
|
* @param input Input data
|
||||||
|
* @param inlen Input length
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_wireguard_generate_secret(uint8_t out[32], const uint8_t *input, size_t inlen);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SE050_BLAKE2S_H */
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_chacha20_poly1305.h
|
||||||
|
* @brief ChaCha20-Poly1305 AEAD Implementation
|
||||||
|
*
|
||||||
|
* Software implementation for WireGuard protocol.
|
||||||
|
* Based on RFC 7539 (ChaCha20-Poly1305) and RFC 8434 (WireGuard).
|
||||||
|
*
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_CHACHA20_POLY1305_H
|
||||||
|
#define SE050_CHACHA20_POLY1305_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Constants
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#define CHACHA20_KEY_SIZE 32
|
||||||
|
#define CHACHA20_NONCE_SIZE 12
|
||||||
|
#define CHACHA20_BLOCK_SIZE 64
|
||||||
|
|
||||||
|
#define POLY1305_KEY_SIZE 32
|
||||||
|
#define POLY1305_TAG_SIZE 16
|
||||||
|
|
||||||
|
#define CHACHA20_POLY1305_AEAD_KEY_SIZE 32
|
||||||
|
#define CHACHA20_POLY1305_AEAD_NONCE_SIZE 12
|
||||||
|
#define CHACHA20_POLY1305_TAG_SIZE 16
|
||||||
|
|
||||||
|
/* WireGuard-specific constants */
|
||||||
|
#define WG_KEY_SIZE 32
|
||||||
|
#define WG_NONCE_SIZE 12
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Type Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ChaCha20-Poly1305 AEAD context
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint8_t key[CHACHA20_KEY_SIZE];
|
||||||
|
} se050_chacha20_poly1305_ctx_t;
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* ChaCha20 Core Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ChaCha20 quarter round
|
||||||
|
*
|
||||||
|
* @param a Pointer to a
|
||||||
|
* @param b Pointer to b
|
||||||
|
* @param c Pointer to c
|
||||||
|
* @param d Pointer to d
|
||||||
|
*/
|
||||||
|
void se050_chacha20_quarter_round(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ChaCha20 block function
|
||||||
|
*
|
||||||
|
* @param output Output buffer (64 bytes)
|
||||||
|
* @param key Key (32 bytes)
|
||||||
|
* @param counter Initial counter (4 bytes)
|
||||||
|
* @param nonce Nonce (12 bytes)
|
||||||
|
*/
|
||||||
|
void se050_chacha20_block(uint8_t output[64], const uint8_t key[32],
|
||||||
|
uint32_t counter, const uint8_t nonce[12]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ChaCha20 encrypt/decrypt
|
||||||
|
*
|
||||||
|
* @param output Output buffer
|
||||||
|
* @param input Input buffer
|
||||||
|
* @param len Length
|
||||||
|
* @param key Key (32 bytes)
|
||||||
|
* @param counter Initial counter
|
||||||
|
* @param nonce Nonce (12 bytes)
|
||||||
|
*/
|
||||||
|
void se050_chacha20(uint8_t *output, const uint8_t *input, size_t len,
|
||||||
|
const uint8_t key[32], uint32_t counter, const uint8_t nonce[12]);
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Poly1305 Core Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Poly1305 MAC generation
|
||||||
|
*
|
||||||
|
* @param mac Output MAC (16 bytes)
|
||||||
|
* @param key Key (32 bytes)
|
||||||
|
* @param data Data to authenticate
|
||||||
|
* @param len Data length
|
||||||
|
*/
|
||||||
|
void se050_poly1305_mac(uint8_t mac[16], const uint8_t key[32],
|
||||||
|
const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* ChaCha20-Poly1305 AEAD
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize ChaCha20-Poly1305 context
|
||||||
|
*
|
||||||
|
* @param ctx Context to initialize
|
||||||
|
* @param key Key (32 bytes)
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_chacha20_poly1305_init(se050_chacha20_poly1305_ctx_t *ctx,
|
||||||
|
const uint8_t key[CHACHA20_KEY_SIZE]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ChaCha20-Poly1305 AEAD encryption
|
||||||
|
*
|
||||||
|
* @param ctx Context
|
||||||
|
* @param nonce Nonce (12 bytes)
|
||||||
|
* @param plaintext Plaintext data
|
||||||
|
* @param plaintext_len Plaintext length
|
||||||
|
* @param aad Additional authenticated data
|
||||||
|
* @param aad_len AAD length
|
||||||
|
* @param ciphertext Output ciphertext
|
||||||
|
* @param tag Output authentication tag (16 bytes)
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_chacha20_poly1305_encrypt(se050_chacha20_poly1305_ctx_t *ctx,
|
||||||
|
const uint8_t nonce[CHACHA20_NONCE_SIZE],
|
||||||
|
const uint8_t *plaintext, size_t plaintext_len,
|
||||||
|
const uint8_t *aad, size_t aad_len,
|
||||||
|
uint8_t *ciphertext, uint8_t tag[POLY1305_TAG_SIZE]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ChaCha20-Poly1305 AEAD decryption
|
||||||
|
*
|
||||||
|
* @param ctx Context
|
||||||
|
* @param nonce Nonce (12 bytes)
|
||||||
|
* @param ciphertext Ciphertext data
|
||||||
|
* @param ciphertext_len Ciphertext length
|
||||||
|
* @param aad Additional authenticated data
|
||||||
|
* @param aad_len AAD length
|
||||||
|
* @param tag Authentication tag (16 bytes)
|
||||||
|
* @param plaintext Output plaintext
|
||||||
|
* @return 0 on success, -1 on error (tag mismatch)
|
||||||
|
*/
|
||||||
|
int se050_chacha20_poly1305_decrypt(se050_chacha20_poly1305_ctx_t *ctx,
|
||||||
|
const uint8_t nonce[CHACHA20_NONCE_SIZE],
|
||||||
|
const uint8_t *ciphertext, size_t ciphertext_len,
|
||||||
|
const uint8_t *aad, size_t aad_len,
|
||||||
|
const uint8_t tag[POLY1305_TAG_SIZE],
|
||||||
|
uint8_t *plaintext);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief WireGuard-specific encrypt
|
||||||
|
*
|
||||||
|
* WireGuard uses ChaCha20-Poly1305 with:
|
||||||
|
* - 32-byte key
|
||||||
|
* - 12-byte nonce
|
||||||
|
* - No AAD
|
||||||
|
*
|
||||||
|
* @param key Key (32 bytes)
|
||||||
|
* @param nonce Nonce (12 bytes)
|
||||||
|
* @param plaintext Plaintext
|
||||||
|
* @param len Length
|
||||||
|
* @param ciphertext Output ciphertext
|
||||||
|
* @param tag Output tag (16 bytes)
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_wireguard_encrypt(const uint8_t key[WG_KEY_SIZE],
|
||||||
|
const uint8_t nonce[WG_NONCE_SIZE],
|
||||||
|
const uint8_t *plaintext, size_t len,
|
||||||
|
uint8_t *ciphertext, uint8_t tag[POLY1305_TAG_SIZE]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief WireGuard-specific decrypt
|
||||||
|
*
|
||||||
|
* @param key Key (32 bytes)
|
||||||
|
* @param nonce Nonce (12 bytes)
|
||||||
|
* @param ciphertext Ciphertext
|
||||||
|
* @param len Length
|
||||||
|
* @param tag Tag (16 bytes)
|
||||||
|
* @param plaintext Output plaintext
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_wireguard_decrypt(const uint8_t key[WG_KEY_SIZE],
|
||||||
|
const uint8_t nonce[WG_NONCE_SIZE],
|
||||||
|
const uint8_t *ciphertext, size_t len,
|
||||||
|
const uint8_t tag[POLY1305_TAG_SIZE],
|
||||||
|
uint8_t *plaintext);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Securely zeroize context
|
||||||
|
*
|
||||||
|
* @param ctx Context to zeroize
|
||||||
|
*/
|
||||||
|
void se050_chacha20_poly1305_zeroize(se050_chacha20_poly1305_ctx_t *ctx);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SE050_CHACHA20_POLY1305_H */
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_hkdf_blake2s.h
|
||||||
|
* @brief HKDF Implementation using HMAC-BLAKE2s (RFC 5861)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_HKDF_BLAKE2S_H
|
||||||
|
#define SE050_HKDF_BLAKE2S_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define HKDF_BLAKE2S_MAX_OUTPUT (255 * 32)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief HKDF-Extract: Extract a pseudorandom key from input keying material
|
||||||
|
* @param prk Output pseudorandom key (32 bytes)
|
||||||
|
* @param salt Salt value (can be NULL for default)
|
||||||
|
* @param saltlen Salt length
|
||||||
|
* @param ikm Input keying material
|
||||||
|
* @param ikmlen Input keying material length
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_hkdf_extract(uint8_t prk[32],
|
||||||
|
const uint8_t *salt, size_t saltlen,
|
||||||
|
const uint8_t *ikm, size_t ikmlen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief HKDF-Expand: Expand PRK into output keying material
|
||||||
|
* @param okm Output keying material
|
||||||
|
* @param okmlen Output length (1 to 255*32 bytes)
|
||||||
|
* @param prk Pseudorandom key from Extract
|
||||||
|
* @param info Context/application-specific info
|
||||||
|
* @param infolen Info length
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_hkdf_expand(uint8_t *okm, size_t okmlen,
|
||||||
|
const uint8_t prk[32],
|
||||||
|
const uint8_t *info, size_t infolen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief HKDF: Combined Extract-and-Expand
|
||||||
|
* @param okm Output keying material
|
||||||
|
* @param okmlen Output length
|
||||||
|
* @param salt Salt value (can be NULL)
|
||||||
|
* @param saltlen Salt length
|
||||||
|
* @param ikm Input keying material
|
||||||
|
* @param ikmlen Input keying material length
|
||||||
|
* @param info Context/application-specific info
|
||||||
|
* @param infolen Info length
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_hkdf(uint8_t *okm, size_t okmlen,
|
||||||
|
const uint8_t *salt, size_t saltlen,
|
||||||
|
const uint8_t *ikm, size_t ikmlen,
|
||||||
|
const uint8_t *info, size_t infolen);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SE050_HKDF_BLAKE2S_H */
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_hmac_blake2s.h
|
||||||
|
* @brief HMAC-BLAKE2s Implementation (RFC 2104)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_HMAC_BLAKE2S_H
|
||||||
|
#define SE050_HMAC_BLAKE2S_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define HMAC_BLAKE2S_BLOCK_SIZE 64
|
||||||
|
#define HMAC_BLAKE2S_DIGEST_SIZE 32
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compute HMAC-BLAKE2s
|
||||||
|
* @param out Output buffer (32 bytes)
|
||||||
|
* @param key Key data
|
||||||
|
* @param keylen Key length (1-64 bytes)
|
||||||
|
* @param data Input data
|
||||||
|
* @param datalen Input data length
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_hmac_blake2s(uint8_t out[32],
|
||||||
|
const uint8_t *key, size_t keylen,
|
||||||
|
const uint8_t *data, size_t datalen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief One-shot HMAC-BLAKE2s with variable output length
|
||||||
|
* @param out Output buffer
|
||||||
|
* @param outlen Output length (1-32 bytes)
|
||||||
|
* @param key Key data
|
||||||
|
* @param keylen Key length
|
||||||
|
* @param data Input data
|
||||||
|
* @param datalen Input data length
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_hmac_blake2s_variable(uint8_t *out, size_t outlen,
|
||||||
|
const uint8_t *key, size_t keylen,
|
||||||
|
const uint8_t *data, size_t datalen);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SE050_HMAC_BLAKE2S_H */
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_i2c_hal.h
|
||||||
|
* @brief SE050 I2C HAL Interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_I2C_HAL_H
|
||||||
|
#define SE050_I2C_HAL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Status codes */
|
||||||
|
typedef enum {
|
||||||
|
SE050_OK = 0,
|
||||||
|
SE050_ERR_INVALID_ARG = -1,
|
||||||
|
SE050_ERR_I2C = -2,
|
||||||
|
SE050_ERR_TIMEOUT = -3,
|
||||||
|
SE050_ERR_INTERNAL = -4,
|
||||||
|
SE050_ERR_SESSION = -5,
|
||||||
|
SE050_ERR_FAIL = -6,
|
||||||
|
SE050_ERR_RNG = -7,
|
||||||
|
SE050_ERR_ECDH = -8,
|
||||||
|
SE050_ERR_NOT_INIT = -9,
|
||||||
|
SE050_ERR_SCP03 = -10
|
||||||
|
} se050_status_t;
|
||||||
|
|
||||||
|
/* I2C HAL structure */
|
||||||
|
#define SE050_I2C_DEV_PATH_MAX 64
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void *handle; /**< I2C file descriptor */
|
||||||
|
uint8_t slave_addr; /**< I2C slave address */
|
||||||
|
char dev_path[SE050_I2C_DEV_PATH_MAX]; /**< I2C device path */
|
||||||
|
int wakeup_pin; /**< Wakeup GPIO pin (-1 if unused) */
|
||||||
|
} se050_i2c_hal_t;
|
||||||
|
|
||||||
|
/* Public API */
|
||||||
|
se050_status_t se050_i2c_init(se050_i2c_hal_t *hal, const char *dev_path, uint8_t slave_addr);
|
||||||
|
void se050_i2c_close(se050_i2c_hal_t *hal);
|
||||||
|
int se050_i2c_read(se050_i2c_hal_t *hal, uint8_t *buffer, int length);
|
||||||
|
int se050_i2c_write(se050_i2c_hal_t *hal, const uint8_t *buffer, int length);
|
||||||
|
se050_status_t se050_i2c_wakeup(se050_i2c_hal_t *hal);
|
||||||
|
|
||||||
|
#endif /* SE050_I2C_HAL_H */
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
#define SE050_KEYSTORE_INTERNAL_H
|
#define SE050_KEYSTORE_INTERNAL_H
|
||||||
|
|
||||||
#include "se050_wireguard.h"
|
#include "se050_wireguard.h"
|
||||||
|
#include "se050_session_internal.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
@@ -51,6 +52,8 @@ typedef struct {
|
|||||||
/**
|
/**
|
||||||
* @brief Key store context structure
|
* @brief Key store context structure
|
||||||
*/
|
*/
|
||||||
|
typedef struct se050_keystore_ctx se050_keystore_ctx_t;
|
||||||
|
|
||||||
struct se050_keystore_ctx {
|
struct se050_keystore_ctx {
|
||||||
se050_session_ctx_t *session; /**< Associated session */
|
se050_session_ctx_t *session; /**< Associated session */
|
||||||
key_object_t *objects; /**< Key objects array */
|
key_object_t *objects; /**< Key objects array */
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_mem_pool.h
|
||||||
|
* @brief Static Memory Pool for Embedded Systems
|
||||||
|
*
|
||||||
|
* Replaces malloc/calloc with pre-allocated static pools.
|
||||||
|
* Suitable for u-boot, ESP32, and other embedded environments.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_MEM_POOL_H
|
||||||
|
#define SE050_MEM_POOL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* Configuration: Pool sizes */
|
||||||
|
#ifndef SE050_POOL_SESSION_COUNT
|
||||||
|
#define SE050_POOL_SESSION_COUNT 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SE050_POOL_SCP03_COUNT
|
||||||
|
#define SE050_POOL_SCP03_COUNT 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SE050_POOL_KEYSTORE_COUNT
|
||||||
|
#define SE050_POOL_KEYSTORE_COUNT 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SE050_POOL_KEYSTORE_MAX_OBJECTS
|
||||||
|
#define SE050_POOL_KEYSTORE_MAX_OBJECTS 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SE050_POOL_RNG_COUNT
|
||||||
|
#define SE050_POOL_RNG_COUNT 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SE050_POOL_I2C_HAL_COUNT
|
||||||
|
#define SE050_POOL_I2C_HAL_COUNT 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
struct se050_session_ctx;
|
||||||
|
struct se050_scp03_ctx;
|
||||||
|
struct se050_keystore_ctx;
|
||||||
|
struct se050_rng_ctx;
|
||||||
|
struct se050_i2c_hal;
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Memory Pool API
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize all memory pools
|
||||||
|
*
|
||||||
|
* Must be called before any other SE050 functions.
|
||||||
|
*
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_mem_pool_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cleanup all memory pools
|
||||||
|
*
|
||||||
|
* Zeroizes all allocated memory before freeing.
|
||||||
|
*/
|
||||||
|
void se050_mem_pool_cleanup(void);
|
||||||
|
|
||||||
|
/* Session pool */
|
||||||
|
struct se050_session_ctx *se050_session_alloc_pool(void);
|
||||||
|
void se050_session_free_pool(struct se050_session_ctx *ctx);
|
||||||
|
|
||||||
|
/* SCP03 pool */
|
||||||
|
struct se050_scp03_ctx *se050_scp03_alloc_pool(void);
|
||||||
|
void se050_scp03_free_pool(struct se050_scp03_ctx *ctx);
|
||||||
|
|
||||||
|
/* Keystore pool */
|
||||||
|
struct se050_keystore_ctx *se050_keystore_alloc_pool(void);
|
||||||
|
void se050_keystore_free_pool(struct se050_keystore_ctx *ctx);
|
||||||
|
|
||||||
|
/* RNG pool */
|
||||||
|
struct se050_rng_ctx *se050_rng_alloc_pool(void);
|
||||||
|
void se050_rng_free_pool(struct se050_rng_ctx *ctx);
|
||||||
|
|
||||||
|
/* I2C HAL pool */
|
||||||
|
struct se050_i2c_hal *se050_i2c_hal_alloc_pool(void);
|
||||||
|
void se050_i2c_hal_free_pool(struct se050_i2c_hal *hal);
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Debug/Statistics
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get pool statistics
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int total;
|
||||||
|
int used;
|
||||||
|
int free;
|
||||||
|
} se050_pool_stats_t;
|
||||||
|
|
||||||
|
void se050_mem_pool_stats(se050_pool_stats_t *session,
|
||||||
|
se050_pool_stats_t *scp03,
|
||||||
|
se050_pool_stats_t *keystore,
|
||||||
|
se050_pool_stats_t *rng,
|
||||||
|
se050_pool_stats_t *i2c_hal);
|
||||||
|
|
||||||
|
#endif /* SE050_MEM_POOL_H */
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_mem_protect.h
|
||||||
|
* @brief Memory Protection Utilities
|
||||||
|
*
|
||||||
|
* Linux-specific memory protection functions to prevent:
|
||||||
|
* - Swapping sensitive data to disk (mlock)
|
||||||
|
* - Core dump leakage (MADV_DONTDUMP)
|
||||||
|
* - Child process leakage after fork() (MADV_WIPEONFORK)
|
||||||
|
*
|
||||||
|
* License: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_MEM_PROTECT_H
|
||||||
|
#define SE050_MEM_PROTECT_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "se050_wireguard.h"
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Protect sensitive memory from being swapped or dumped
|
||||||
|
*
|
||||||
|
* Applies multiple security measures:
|
||||||
|
* - mlock(): Prevent swapping to disk
|
||||||
|
* - MADV_DONTDUMP: Exclude from core dumps
|
||||||
|
* - MADV_WIPEONFORK: Clear in child processes after fork()
|
||||||
|
*
|
||||||
|
* @param ptr Pointer to memory region to protect
|
||||||
|
* @param size Size of memory region in bytes
|
||||||
|
* @return SE050_OK on success, SE050_ERR_FAIL on failure
|
||||||
|
*
|
||||||
|
* @note On Linux, mlock may fail due to permissions. If it fails,
|
||||||
|
* a warning is printed but the function continues (non-fatal).
|
||||||
|
* @note On non-Linux platforms, this function is a no-op.
|
||||||
|
*/
|
||||||
|
static inline se050_status_t protect_sensitive_memory(void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
/* 1. Prevent swapping to disk (optional - may fail due to permissions) */
|
||||||
|
if (mlock(ptr, size) != 0) {
|
||||||
|
/* mlock may fail due to ulimit restrictions. Log warning but continue. */
|
||||||
|
fprintf(stderr, "Warning: mlock failed (%s). Memory may be swapped.\n", strerror(errno));
|
||||||
|
/* Continue without mlock - better than failing entirely */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Exclude from core dumps */
|
||||||
|
if (madvise(ptr, size, MADV_DONTDUMP) != 0) {
|
||||||
|
perror("madvise MADV_DONTDUMP failed");
|
||||||
|
/* Non-fatal, continue */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. Clear in child processes after fork() */
|
||||||
|
if (madvise(ptr, size, MADV_WIPEONFORK) != 0) {
|
||||||
|
perror("madvise MADV_WIPEONFORK failed");
|
||||||
|
/* Non-fatal, continue */
|
||||||
|
}
|
||||||
|
|
||||||
|
return SE050_OK;
|
||||||
|
#else
|
||||||
|
/* Non-Linux platforms: no special protection */
|
||||||
|
(void)ptr;
|
||||||
|
(void)size;
|
||||||
|
return SE050_OK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Release memory protection before freeing
|
||||||
|
*
|
||||||
|
* Must be called before freeing protected memory.
|
||||||
|
*
|
||||||
|
* @param ptr Pointer to memory region to release
|
||||||
|
* @param size Size of memory region in bytes
|
||||||
|
*/
|
||||||
|
static inline void release_memory_protection(void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
/* Must unlock before freeing */
|
||||||
|
munlock(ptr, size);
|
||||||
|
#else
|
||||||
|
(void)ptr;
|
||||||
|
(void)size;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* SE050_MEM_PROTECT_H */
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_scp03.h
|
||||||
|
* @brief SE050 Platform SCP03 Secure Channel Interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_SCP03_H
|
||||||
|
#define SE050_SCP03_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "se050_i2c_hal.h"
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
typedef struct se050_session_ctx se050_session_ctx_t;
|
||||||
|
typedef struct se050_scp03_ctx se050_scp03_ctx_t;
|
||||||
|
|
||||||
|
/* SCP03 key sizes */
|
||||||
|
#define SCP03_KEY_SIZE 16
|
||||||
|
#define SCP03_IV_SIZE 16
|
||||||
|
#define SCP03_CMAC_SIZE 8
|
||||||
|
|
||||||
|
/* Initialize SCP03 context */
|
||||||
|
se050_status_t se050_scp03_init(se050_scp03_ctx_t **ctx, se050_session_ctx_t *session);
|
||||||
|
|
||||||
|
/* Set SCP03 keys */
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* Encrypt command */
|
||||||
|
se050_status_t se050_scp03_encrypt_command(se050_scp03_ctx_t *ctx,
|
||||||
|
uint8_t *cmd, size_t *cmd_len);
|
||||||
|
|
||||||
|
/* Decrypt response */
|
||||||
|
uint16_t se050_scp03_decrypt_response(se050_scp03_ctx_t *ctx,
|
||||||
|
size_t cmd_len,
|
||||||
|
uint8_t *rsp, size_t *rsp_len);
|
||||||
|
|
||||||
|
/* Cleanup SCP03 context */
|
||||||
|
void se050_scp03_cleanup(se050_scp03_ctx_t *ctx);
|
||||||
|
|
||||||
|
#endif /* SE050_SCP03_H */
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_scp03_keys.h
|
||||||
|
* @brief SE050 Platform SCP03 Keys
|
||||||
|
*
|
||||||
|
* Platform SCP03 keys for each SE050 chip type.
|
||||||
|
* Keys should be obtained from NXP documentation or secure provisioning.
|
||||||
|
*
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_SCP03_KEYS_H
|
||||||
|
#define SE050_SCP03_KEYS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* SE050C0 Platform SCP03 Keys
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
extern const uint8_t SE050C0_ENC_KEY[16];
|
||||||
|
extern const uint8_t SE050C0_MAC_KEY[16];
|
||||||
|
extern const uint8_t SE050C0_DEK_KEY[16];
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* SE050C1 Platform SCP03 Keys
|
||||||
|
*
|
||||||
|
* Note: SE050C2 uses the same PlatformSCP03 keys as SE050C1.
|
||||||
|
* See AN12436 for details.
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
extern const uint8_t SE050C1_ENC_KEY[16];
|
||||||
|
extern const uint8_t SE050C1_MAC_KEY[16];
|
||||||
|
extern const uint8_t SE050C1_DEK_KEY[16];
|
||||||
|
|
||||||
|
/* SE050C2 uses same keys as SE050C1 */
|
||||||
|
#define SE050C2_ENC_KEY SE050C1_ENC_KEY
|
||||||
|
#define SE050C2_MAC_KEY SE050C1_MAC_KEY
|
||||||
|
#define SE050C2_DEK_KEY SE050C1_DEK_KEY
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* SE050E2 Platform SCP03 Keys
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
extern const uint8_t SE050E2_ENC_KEY[16];
|
||||||
|
extern const uint8_t SE050E2_MAC_KEY[16];
|
||||||
|
extern const uint8_t SE050E2_DEK_KEY[16];
|
||||||
|
|
||||||
|
#endif /* SE050_SCP03_KEYS_H */
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#ifndef SE050_SESSION_INTERNAL_H
|
#ifndef SE050_SESSION_INTERNAL_H
|
||||||
#define SE050_SESSION_INTERNAL_H
|
#define SE050_SESSION_INTERNAL_H
|
||||||
|
|
||||||
|
#include "se050_i2c_hal.h"
|
||||||
#include "se050_wireguard.h"
|
#include "se050_wireguard.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
@@ -20,17 +21,38 @@ typedef enum {
|
|||||||
SESSION_STATE_CLOSED,
|
SESSION_STATE_CLOSED,
|
||||||
} session_state_t;
|
} session_state_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SCP03 secure channel context
|
||||||
|
*/
|
||||||
|
typedef struct se050_scp03_ctx {
|
||||||
|
struct se050_session_ctx *session; /**< Associated session */
|
||||||
|
uint8_t enc_key[16]; /**< Encryption key */
|
||||||
|
uint8_t mac_key[16]; /**< MAC key */
|
||||||
|
uint8_t dek_key[16]; /**< DEK key */
|
||||||
|
uint8_t cmd_icv[8]; /**< Command ICV */
|
||||||
|
uint8_t rsp_icv[8]; /**< Response ICV */
|
||||||
|
uint64_t cmd_counter; /**< Command counter */
|
||||||
|
uint64_t rsp_counter; /**< Response counter */
|
||||||
|
uint8_t initialized; /**< Initialization flag */
|
||||||
|
} se050_scp03_ctx_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief RNG context (forward declaration)
|
||||||
|
*/
|
||||||
|
typedef struct se050_rng_ctx se050_rng_ctx_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Session context structure
|
* @brief Session context structure
|
||||||
*/
|
*/
|
||||||
|
typedef struct se050_session_ctx se050_session_ctx_t;
|
||||||
|
|
||||||
struct se050_session_ctx {
|
struct se050_session_ctx {
|
||||||
se050_i2c_hal_t *hal; /**< I2C HAL interface */
|
se050_i2c_hal_t *hal; /**< I2C HAL interface */
|
||||||
session_state_t state; /**< Current session state */
|
session_state_t state; /**< Current session state */
|
||||||
uint32_t session_id; /**< Unique session identifier */
|
uint32_t session_id; /**< Unique session identifier */
|
||||||
|
se050_scp03_ctx_t *scp03; /**< SCP03 secure channel context */
|
||||||
uint8_t session_key[32]; /**< Session encryption key */
|
uint8_t session_key[32]; /**< Session encryption key */
|
||||||
size_t session_key_len; /**< Session key length */
|
size_t session_key_len; /**< Session key length */
|
||||||
uint32_t cmd_counter; /**< Command counter for SCP03 */
|
|
||||||
uint32_t resp_counter; /**< Response counter for SCP03 */
|
|
||||||
se050_rng_ctx_t *rng; /**< RNG context */
|
se050_rng_ctx_t *rng; /**< RNG context */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_tai64n.h
|
||||||
|
* @brief TAI64N Timestamp Encoding (WireGuard Protocol)
|
||||||
|
*
|
||||||
|
* TAI64N: 64-bit TAI + 32-bit nanoseconds
|
||||||
|
* Total: 12 bytes (big-endian)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_TAI64N_H
|
||||||
|
#define SE050_TAI64N_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TAI64N_SIZE 12
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encode a timestamp to TAI64N format
|
||||||
|
* @param out Output buffer (12 bytes)
|
||||||
|
* @param seconds Unix timestamp seconds
|
||||||
|
* @param nanoseconds Nanoseconds (0-999999999)
|
||||||
|
*/
|
||||||
|
void se050_tai64n_encode(uint8_t out[12], uint64_t seconds, uint32_t nanoseconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decode TAI64N format to Unix timestamp
|
||||||
|
* @param in Input buffer (12 bytes)
|
||||||
|
* @param seconds Output seconds (Unix timestamp)
|
||||||
|
* @param nanoseconds Output nanoseconds
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_tai64n_decode(const uint8_t in[12], uint64_t *seconds, uint32_t *nanoseconds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get current time as TAI64N
|
||||||
|
* @param out Output buffer (12 bytes)
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_tai64n_now(uint8_t out[12]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if TAI64N timestamp is within acceptable window
|
||||||
|
* @param timestamp Received timestamp
|
||||||
|
* @param current Current timestamp
|
||||||
|
* @param window Acceptable window in seconds
|
||||||
|
* @return 1 if valid, 0 if expired/replay, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_tai64n_check_window(const uint8_t timestamp[12],
|
||||||
|
const uint8_t current[12],
|
||||||
|
uint32_t window);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SE050_TAI64N_H */
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_tai64n_hw.h
|
||||||
|
* @brief TAI64N using SE050 Hardware Monotonic Counter
|
||||||
|
*
|
||||||
|
* Uses SE050's built-in monotonic counter for replay prevention.
|
||||||
|
* Object ID: SE05X_OBJ_ID_MONOTONIC_COUNTER (0x7FFF0203)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_TAI64N_HW_H
|
||||||
|
#define SE050_TAI64N_HW_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TAI64N_HW_SIZE 12
|
||||||
|
#define SE050_MONOTONIC_COUNTER_ID 0x7FFF0203
|
||||||
|
|
||||||
|
/* Session handle type (opaque) */
|
||||||
|
typedef void* se050_session_t;
|
||||||
|
|
||||||
|
/* Mock session for testing */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t counter;
|
||||||
|
} mock_session_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read SE050 monotonic counter and encode as TAI64N
|
||||||
|
* @param session SE050 session handle
|
||||||
|
* @param out Output buffer (12 bytes)
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_tai64n_hw_now(void *session, uint8_t out[12]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read SE050 monotonic counter only (32-bit)
|
||||||
|
* @param session SE050 session handle
|
||||||
|
* @param counter Output counter value
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_tai64n_hw_read_counter(void *session, uint32_t *counter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Increment SE050 monotonic counter
|
||||||
|
* @param session SE050 session handle
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_tai64n_hw_increment(void *session);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if timestamp is within acceptable window
|
||||||
|
* @param timestamp Received TAI64N timestamp
|
||||||
|
* @param current Current TAI64N timestamp
|
||||||
|
* @param window Acceptable window in seconds
|
||||||
|
* @return 1 if valid, 0 if expired/replay, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_tai64n_hw_check_window(const uint8_t timestamp[12],
|
||||||
|
const uint8_t current[12],
|
||||||
|
uint32_t window);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SE050_TAI64N_HW_H */
|
||||||
+217
-387
@@ -1,422 +1,252 @@
|
|||||||
/**
|
/**
|
||||||
* @file se050_wireguard.h
|
* @file se050_wireguard.h
|
||||||
* @brief SE050 WireGuard Minimum Interface
|
* @brief WireGuard VPN Protocol - Public API
|
||||||
*
|
|
||||||
* 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)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SE050_WIREGUARD_H
|
#ifndef SE050_WIREGUARD_H
|
||||||
#define SE050_WIREGUARD_H
|
#define SE050_WIREGUARD_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#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
|
* 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
|
* @brief WireGuard session context
|
||||||
* @param hal I2C HAL structure
|
*
|
||||||
* @param dev_path Device path (e.g., "/dev/i2c-1")
|
* Contains all state needed for WireGuard communication:
|
||||||
* @param slave_addr I2C slave address (default: 0x90)
|
* - Local keypair
|
||||||
* @return SE050_OK on success
|
* - 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 {
|
||||||
|
/* Local keys */
|
||||||
|
uint8_t private_key[32];
|
||||||
|
uint8_t public_key[32];
|
||||||
|
|
||||||
|
/* Peer keys */
|
||||||
|
uint8_t peer_public_key[32];
|
||||||
|
|
||||||
|
/* 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[16];
|
||||||
|
|
||||||
|
/* State flags */
|
||||||
|
int is_initiator;
|
||||||
|
int handshake_complete;
|
||||||
|
int packets_received; /* Number of packets received (for replay detection) */
|
||||||
|
} 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
|
* Session Management
|
||||||
* ============================================================================ */
|
* ========================================================================= */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create SE050 session
|
* @brief Initialize a WireGuard 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
|
|
||||||
*
|
*
|
||||||
* Note: Private key is generated using SE050 TRNG and stored securely.
|
* @param session Output: session context to initialize
|
||||||
* The private key never leaves the SE050.
|
* @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,
|
int se050_wireguard_session_init(se050_wireguard_session_t *session,
|
||||||
se050_x25519_keypair_t *keypair,
|
const uint8_t *private_key,
|
||||||
uint32_t key_id);
|
const uint8_t *peer_public_key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Compute X25519 ECDH shared secret
|
* @brief Clean up and zeroize session data
|
||||||
* @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
|
|
||||||
*
|
*
|
||||||
* 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,
|
void se050_wireguard_session_cleanup(se050_wireguard_session_t *session);
|
||||||
uint32_t private_key_id,
|
|
||||||
const uint8_t *peer_public,
|
/* =========================================================================
|
||||||
uint8_t *shared_secret);
|
* Key Derivation
|
||||||
|
* ========================================================================= */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Export X25519 public key from SE050
|
* @brief Derive session keys from shared secret
|
||||||
* @param keystore Key store context
|
*
|
||||||
* @param key_id Key identifier
|
* After performing X25519 key exchange, use this to derive
|
||||||
* @param public_key Output public key (32 bytes)
|
* the actual encryption keys for the session.
|
||||||
* @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
|
|
||||||
* @param session Session context
|
* @param session Session context
|
||||||
* @param keystore Key store context
|
* @param shared_secret X25519 shared secret (32 bytes)
|
||||||
* @param rng RNG context
|
* @return 0 on success, -1 on error
|
||||||
*/
|
*/
|
||||||
void se050_wireguard_cleanup(se050_session_ctx_t *session,
|
int se050_wireguard_derive_keys(se050_wireguard_session_t *session,
|
||||||
se050_keystore_ctx_t *keystore,
|
const uint8_t *shared_secret);
|
||||||
se050_rng_ctx_t *rng);
|
|
||||||
|
/* =========================================================================
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* 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)
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_wireguard_generate_keypair(uint8_t *private_key, uint8_t *public_key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate WireGuard keypair using SE050 hardware RNG
|
||||||
|
*
|
||||||
|
* This function uses the SE050 chip's built-in True Random Number Generator
|
||||||
|
* for cryptographically secure key generation.
|
||||||
|
*
|
||||||
|
* @param session SE050 session context (initialized via se050_session_init())
|
||||||
|
* @param private_key Output: private key (32 bytes)
|
||||||
|
* @param public_key Output: public key (32 bytes)
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
#ifdef SE050_ENABLED
|
||||||
|
int se050_wireguard_generate_keypair_se050(se050_session_ctx_t *session,
|
||||||
|
uint8_t *private_key,
|
||||||
|
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
|
||||||
|
* ========================================================================= */
|
||||||
|
|
||||||
|
#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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_wireguard_proto.h
|
||||||
|
* @brief WireGuard Protocol Layer
|
||||||
|
* Key derivation chains and handshake message structures
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_WIREGUARD_PROTO_H
|
||||||
|
#define SE050_WIREGUARD_PROTO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define WG_KEY_LEN 32
|
||||||
|
#define WG_MAC_LEN 16
|
||||||
|
#define WG_NONCE_LEN 12
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* WireGuard Key Derivation Chains
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize keying material with zero key
|
||||||
|
* @param ck Output chain key (32 bytes)
|
||||||
|
* @param tk Output temp key (32 bytes, can be NULL)
|
||||||
|
*/
|
||||||
|
void wg_kdf_init(uint8_t ck[32], uint8_t tk[32]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief HKDF-1: ck, tk1 = KDF1(ck, input_key_material)
|
||||||
|
* @param ck Chain key (updated in place)
|
||||||
|
* @param tk1 Output temp key
|
||||||
|
* @param ikm Input keying material
|
||||||
|
* @param ikmlen IKM length
|
||||||
|
*/
|
||||||
|
void wg_kdf1(uint8_t ck[32], uint8_t tk1[32],
|
||||||
|
const uint8_t ikm[32], size_t ikmlen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief HKDF-2: ck, tk2 = KDF2(ck, tk1)
|
||||||
|
* @param ck Chain key (updated in place)
|
||||||
|
* @param tk2 Output temp key
|
||||||
|
* @param tk1 Previous temp key
|
||||||
|
*/
|
||||||
|
void wg_kdf2(uint8_t ck[32], uint8_t tk2[32],
|
||||||
|
const uint8_t ck_old[32], const uint8_t tk1[32]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief HKDF-3: ck, tk3 = KDF3(ck, tk2, data)
|
||||||
|
* @param ck Chain key (updated in place)
|
||||||
|
* @param tk3 Output temp key
|
||||||
|
* @param data Additional data to mix
|
||||||
|
* @param datalen Data length
|
||||||
|
*/
|
||||||
|
void wg_kdf3(uint8_t ck[32], uint8_t tk3[32],
|
||||||
|
const uint8_t ck_old[32], const uint8_t tk2[32],
|
||||||
|
const uint8_t *data, size_t datalen);
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* WireGuard Handshake Message Types
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#define WG_MESSAGE_INITIATION 1
|
||||||
|
#define WG_MESSAGE_RESPONSE 2
|
||||||
|
#define WG_MESSAGE_COOKIE_REPLY 3
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Handshake Message Structures
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initiation message (148 bytes)
|
||||||
|
*/
|
||||||
|
struct wg_handshake_init {
|
||||||
|
uint32_t type; /* 4 bytes: message type */
|
||||||
|
uint8_t sender_index[4]; /* 4 bytes: sender's public key index */
|
||||||
|
uint8_t unencrypted_ephemeral[32]; /* 32 bytes: ephemeral public key */
|
||||||
|
uint8_t encrypted_static[100]; /* 100 bytes: static pubkey + MAC */
|
||||||
|
uint8_t encrypted_timestamp[64]; /* 64 bytes: timestamp + MAC */
|
||||||
|
uint8_t mac1[16]; /* 16 bytes: MAC1 */
|
||||||
|
uint8_t mac2[16]; /* 16 bytes: MAC2 */
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Response message (92 bytes)
|
||||||
|
*/
|
||||||
|
struct wg_handshake_resp {
|
||||||
|
uint32_t type; /* 4 bytes */
|
||||||
|
uint8_t sender_index[4]; /* 4 bytes */
|
||||||
|
uint8_t receiver_index[4]; /* 4 bytes */
|
||||||
|
uint8_t unencrypted_ephemeral[32]; /* 32 bytes */
|
||||||
|
uint8_t encrypted_payload[48]; /* 48 bytes: MAC only */
|
||||||
|
uint8_t mac1[16]; /* 16 bytes */
|
||||||
|
uint8_t mac2[16]; /* 16 bytes */
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cookie reply message (64 bytes)
|
||||||
|
*/
|
||||||
|
struct wg_cookie_reply {
|
||||||
|
uint32_t type; /* 4 bytes */
|
||||||
|
uint8_t receiver_index[4]; /* 4 bytes */
|
||||||
|
uint8_t nonce[24]; /* 24 bytes */
|
||||||
|
uint8_t encrypted_cookie[64]; /* 64 bytes: cookie + MAC */
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SE050_WIREGUARD_PROTO_H */
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_x25519_sw.h
|
||||||
|
* @brief Software X25519 ECDH Implementation Header
|
||||||
|
*
|
||||||
|
* Pure software implementation for WireGuard ephemeral key generation.
|
||||||
|
* Fallback when SE050 hardware is unavailable.
|
||||||
|
*
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_X25519_SW_H
|
||||||
|
#define SE050_X25519_SW_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Constants
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#define X25519_SECRET_KEY_SIZE 32
|
||||||
|
#define X25519_PUBLIC_KEY_SIZE 32
|
||||||
|
#define X25519_SHARED_SECRET_SIZE 32
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Type Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief X25519 keypair structure
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint8_t private_key[X25519_SECRET_KEY_SIZE];
|
||||||
|
uint8_t public_key[X25519_PUBLIC_KEY_SIZE];
|
||||||
|
} se050_x25519_sw_keypair_t;
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* API Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate X25519 keypair
|
||||||
|
*
|
||||||
|
* @param keypair Output keypair structure
|
||||||
|
* @param rng_func Random number generator function
|
||||||
|
* @param rng_ctx RNG context
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
typedef int (*x25519_rng_func)(uint8_t *dst, size_t len, void *rng_ctx);
|
||||||
|
|
||||||
|
int se050_x25519_sw_generate_keypair(
|
||||||
|
se050_x25519_sw_keypair_t *keypair,
|
||||||
|
x25519_rng_func rng_func,
|
||||||
|
void *rng_ctx
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compute X25519 shared secret
|
||||||
|
*
|
||||||
|
* @param shared_secret Output shared secret (32 bytes)
|
||||||
|
* @param private_key Private key (32 bytes, will be clamped)
|
||||||
|
* @param peer_public Peer's public key (32 bytes)
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_x25519_sw_compute_shared_secret(
|
||||||
|
uint8_t *shared_secret,
|
||||||
|
const uint8_t *private_key,
|
||||||
|
const uint8_t *peer_public
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compute X25519 public key from private key
|
||||||
|
*
|
||||||
|
* @param public_key Output public key (32 bytes)
|
||||||
|
* @param private_key Private key (32 bytes, will be clamped)
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int se050_x25519_sw_derive_public_key(
|
||||||
|
uint8_t *public_key,
|
||||||
|
const uint8_t *private_key
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clamp X25519 private key
|
||||||
|
*
|
||||||
|
* Applies X25519 scalar clamping:
|
||||||
|
* - Clear bits 0, 1, 2 of first byte
|
||||||
|
* - Clear bit 254 of last byte
|
||||||
|
* - Set bit 255 of last byte
|
||||||
|
*
|
||||||
|
* @param scalar Private key to clamp (modified in place)
|
||||||
|
*/
|
||||||
|
void se050_x25519_sw_clamp(uint8_t *scalar);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Securely zeroize key material
|
||||||
|
*
|
||||||
|
* @param key Key material to zeroize
|
||||||
|
* @param len Length in bytes
|
||||||
|
*/
|
||||||
|
void se050_x25519_sw_zeroize(uint8_t *key, size_t len);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SE050_X25519_SW_H */
|
||||||
@@ -0,0 +1,288 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_blake2s.c
|
||||||
|
* @brief BLAKE2s Hash Function Implementation
|
||||||
|
* Based on BLAKE2 official reference implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "se050_blake2s.h"
|
||||||
|
#include "se050_crypto_utils.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static const uint32_t BLAKE2S_IV[8] = {
|
||||||
|
0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
|
||||||
|
0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t BLAKE2S_SIGMA[10][16] = {
|
||||||
|
{ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 },
|
||||||
|
{ 14,10,4,8,9,15,13,6,1,12,0,2,11,7,5,3 },
|
||||||
|
{ 11,8,12,0,5,2,15,13,10,14,3,6,7,1,9,4 },
|
||||||
|
{ 7,9,3,1,13,12,11,14,2,6,5,10,4,0,15,8 },
|
||||||
|
{ 9,0,5,7,2,4,10,15,14,1,11,12,6,8,3,13 },
|
||||||
|
{ 2,12,6,10,0,11,8,3,4,13,7,5,15,14,1,9 },
|
||||||
|
{ 12,5,1,15,14,13,4,10,0,7,6,3,9,2,8,11 },
|
||||||
|
{ 13,11,7,14,12,1,3,9,5,0,15,4,8,6,2,10 },
|
||||||
|
{ 6,15,14,9,11,3,0,8,12,2,13,7,1,4,10,5 },
|
||||||
|
{ 10,2,8,4,7,6,1,5,15,11,9,14,3,12,13,0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t h[8];
|
||||||
|
uint32_t t[2];
|
||||||
|
uint32_t f[2];
|
||||||
|
uint8_t buf[64];
|
||||||
|
size_t buflen;
|
||||||
|
size_t outlen;
|
||||||
|
} blake2s_internal_t;
|
||||||
|
|
||||||
|
static inline uint32_t load32_le(const uint8_t *p)
|
||||||
|
{
|
||||||
|
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) |
|
||||||
|
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void store32_le(uint8_t *p, uint32_t v)
|
||||||
|
{
|
||||||
|
p[0] = (uint8_t)v; p[1] = (uint8_t)(v >> 8);
|
||||||
|
p[2] = (uint8_t)(v >> 16); p[3] = (uint8_t)(v >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t rotr32(uint32_t x, unsigned int n)
|
||||||
|
{
|
||||||
|
return (x >> n) | (x << (32 - n));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void blake2s_compress(blake2s_internal_t *S, const uint8_t in[64])
|
||||||
|
{
|
||||||
|
uint32_t m[16], v[16];
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < 16; i++) m[i] = load32_le(in + i * 4);
|
||||||
|
for (i = 0; i < 8; i++) v[i] = S->h[i];
|
||||||
|
v[8] = BLAKE2S_IV[0]; v[9] = BLAKE2S_IV[1];
|
||||||
|
v[10] = BLAKE2S_IV[2]; v[11] = BLAKE2S_IV[3];
|
||||||
|
v[12] = S->t[0] ^ BLAKE2S_IV[4];
|
||||||
|
v[13] = S->t[1] ^ BLAKE2S_IV[5];
|
||||||
|
v[14] = S->f[0] ^ BLAKE2S_IV[6];
|
||||||
|
v[15] = S->f[1] ^ BLAKE2S_IV[7];
|
||||||
|
|
||||||
|
for (i = 0; i < 10; i++) {
|
||||||
|
const uint8_t *s = BLAKE2S_SIGMA[i];
|
||||||
|
v[0] = v[0] + v[4] + m[s[0]]; v[12] = rotr32(v[12] ^ v[0], 16);
|
||||||
|
v[8] = v[8] + v[12]; v[4] = rotr32(v[4] ^ v[8], 12);
|
||||||
|
v[0] = v[0] + v[4] + m[s[1]]; v[12] = rotr32(v[12] ^ v[0], 8);
|
||||||
|
v[8] = v[8] + v[12]; v[4] = rotr32(v[4] ^ v[8], 7);
|
||||||
|
v[1] = v[1] + v[5] + m[s[2]]; v[13] = rotr32(v[13] ^ v[1], 16);
|
||||||
|
v[9] = v[9] + v[13]; v[5] = rotr32(v[5] ^ v[9], 12);
|
||||||
|
v[1] = v[1] + v[5] + m[s[3]]; v[13] = rotr32(v[13] ^ v[1], 8);
|
||||||
|
v[9] = v[9] + v[13]; v[5] = rotr32(v[5] ^ v[9], 7);
|
||||||
|
v[2] = v[2] + v[6] + m[s[4]]; v[14] = rotr32(v[14] ^ v[2], 16);
|
||||||
|
v[10] = v[10] + v[14]; v[6] = rotr32(v[6] ^ v[10], 12);
|
||||||
|
v[2] = v[2] + v[6] + m[s[5]]; v[14] = rotr32(v[14] ^ v[2], 8);
|
||||||
|
v[10] = v[10] + v[14]; v[6] = rotr32(v[6] ^ v[10], 7);
|
||||||
|
v[3] = v[3] + v[7] + m[s[6]]; v[15] = rotr32(v[15] ^ v[3], 16);
|
||||||
|
v[11] = v[11] + v[15]; v[7] = rotr32(v[7] ^ v[11], 12);
|
||||||
|
v[3] = v[3] + v[7] + m[s[7]]; v[15] = rotr32(v[15] ^ v[3], 8);
|
||||||
|
v[11] = v[11] + v[15]; v[7] = rotr32(v[7] ^ v[11], 7);
|
||||||
|
v[0] = v[0] + v[5] + m[s[8]]; v[15] = rotr32(v[15] ^ v[0], 16);
|
||||||
|
v[10] = v[10] + v[15]; v[5] = rotr32(v[5] ^ v[10], 12);
|
||||||
|
v[0] = v[0] + v[5] + m[s[9]]; v[15] = rotr32(v[15] ^ v[0], 8);
|
||||||
|
v[10] = v[10] + v[15]; v[5] = rotr32(v[5] ^ v[10], 7);
|
||||||
|
v[1] = v[1] + v[6] + m[s[10]]; v[12] = rotr32(v[12] ^ v[1], 16);
|
||||||
|
v[11] = v[11] + v[12]; v[6] = rotr32(v[6] ^ v[11], 12);
|
||||||
|
v[1] = v[1] + v[6] + m[s[11]]; v[12] = rotr32(v[12] ^ v[1], 8);
|
||||||
|
v[11] = v[11] + v[12]; v[6] = rotr32(v[6] ^ v[11], 7);
|
||||||
|
v[2] = v[2] + v[7] + m[s[12]]; v[13] = rotr32(v[13] ^ v[2], 16);
|
||||||
|
v[8] = v[8] + v[13]; v[7] = rotr32(v[7] ^ v[8], 12);
|
||||||
|
v[2] = v[2] + v[7] + m[s[13]]; v[13] = rotr32(v[13] ^ v[2], 8);
|
||||||
|
v[8] = v[8] + v[13]; v[7] = rotr32(v[7] ^ v[8], 7);
|
||||||
|
v[3] = v[3] + v[4] + m[s[14]]; v[14] = rotr32(v[14] ^ v[3], 16);
|
||||||
|
v[9] = v[9] + v[14]; v[4] = rotr32(v[4] ^ v[9], 12);
|
||||||
|
v[3] = v[3] + v[4] + m[s[15]]; v[14] = rotr32(v[14] ^ v[3], 8);
|
||||||
|
v[9] = v[9] + v[14]; v[4] = rotr32(v[4] ^ v[9], 7);
|
||||||
|
}
|
||||||
|
for (i = 0; i < 8; i++) S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_blake2s_init(se050_blake2s_ctx_t *ctx, size_t outlen)
|
||||||
|
{
|
||||||
|
blake2s_internal_t *inner = (blake2s_internal_t *)ctx;
|
||||||
|
if (!ctx || outlen == 0 || outlen > 32) return -1;
|
||||||
|
for (size_t i = 0; i < 8; i++) inner->h[i] = BLAKE2S_IV[i];
|
||||||
|
inner->h[0] ^= 0x01010000UL ^ outlen;
|
||||||
|
inner->t[0] = inner->t[1] = inner->f[0] = inner->f[1] = 0;
|
||||||
|
inner->buflen = 0; inner->outlen = outlen;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_blake2s_init_key(se050_blake2s_ctx_t *ctx, size_t outlen,
|
||||||
|
const void *key, size_t keylen)
|
||||||
|
{
|
||||||
|
blake2s_internal_t *inner = (blake2s_internal_t *)ctx;
|
||||||
|
if (!ctx || !key || keylen == 0 || keylen > 32 || outlen == 0 || outlen > 32)
|
||||||
|
return -1;
|
||||||
|
for (size_t i = 0; i < 8; i++) inner->h[i] = BLAKE2S_IV[i];
|
||||||
|
inner->h[0] ^= 0x01010000UL ^ (keylen << 8) ^ outlen;
|
||||||
|
inner->t[0] = inner->t[1] = inner->f[0] = inner->f[1] = 0;
|
||||||
|
inner->buflen = 0; inner->outlen = outlen;
|
||||||
|
uint8_t block[64];
|
||||||
|
memset(block, 0, 64);
|
||||||
|
memcpy(block, key, keylen);
|
||||||
|
int ret = se050_blake2s_update(ctx, block, 64);
|
||||||
|
memzero_explicit(block, 64);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_blake2s_update(se050_blake2s_ctx_t *ctx, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
blake2s_internal_t *inner = (blake2s_internal_t *)ctx;
|
||||||
|
const uint8_t *in = (const uint8_t *)data;
|
||||||
|
if (!ctx) return -1;
|
||||||
|
if (len > 0 && !data) return -1;
|
||||||
|
if (len > 0) {
|
||||||
|
size_t left = inner->buflen, fill = 64 - left;
|
||||||
|
|
||||||
|
/* If buffer is empty, process full blocks directly */
|
||||||
|
if (left == 0) {
|
||||||
|
while (len > 64) {
|
||||||
|
inner->t[0] += 64;
|
||||||
|
if (inner->t[0] < 64) inner->t[1]++;
|
||||||
|
blake2s_compress(inner, in);
|
||||||
|
in += 64; len -= 64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* If we can fill the buffer (including exact fill), do it */
|
||||||
|
else if (len >= fill) {
|
||||||
|
memcpy(inner->buf + left, in, fill);
|
||||||
|
inner->buflen = 0;
|
||||||
|
inner->t[0] += 64;
|
||||||
|
if (inner->t[0] < 64) inner->t[1]++;
|
||||||
|
blake2s_compress(inner, inner->buf);
|
||||||
|
in += fill; len -= fill;
|
||||||
|
|
||||||
|
/* Process remaining full blocks */
|
||||||
|
while (len > 64) {
|
||||||
|
inner->t[0] += 64;
|
||||||
|
if (inner->t[0] < 64) inner->t[1]++;
|
||||||
|
blake2s_compress(inner, in);
|
||||||
|
in += 64; len -= 64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store remaining data in buffer */
|
||||||
|
memcpy(inner->buf + inner->buflen, in, len);
|
||||||
|
inner->buflen += len;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_blake2s_final(se050_blake2s_ctx_t *ctx, void *out, size_t outlen)
|
||||||
|
{
|
||||||
|
blake2s_internal_t *inner = (blake2s_internal_t *)ctx;
|
||||||
|
uint8_t buffer[32] = {0};
|
||||||
|
if (!ctx || !out || outlen < inner->outlen) return -1;
|
||||||
|
if (inner->f[0] != 0) return -1;
|
||||||
|
inner->t[0] += (uint32_t)inner->buflen;
|
||||||
|
if (inner->t[0] < inner->buflen) inner->t[1]++;
|
||||||
|
inner->f[0] = (uint32_t)-1;
|
||||||
|
memset(inner->buf + inner->buflen, 0, 64 - inner->buflen);
|
||||||
|
blake2s_compress(inner, inner->buf);
|
||||||
|
for (size_t i = 0; i < 8; i++) store32_le(buffer + i * 4, inner->h[i]);
|
||||||
|
memcpy(out, buffer, inner->outlen);
|
||||||
|
se050_blake2s_zeroize(ctx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_blake2s(void *out, size_t outlen, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
se050_blake2s_ctx_t ctx;
|
||||||
|
int ret = se050_blake2s_init(&ctx, outlen);
|
||||||
|
if (ret != 0) return ret;
|
||||||
|
ret = se050_blake2s_update(&ctx, data, len);
|
||||||
|
if (ret != 0) { se050_blake2s_zeroize(&ctx); return ret; }
|
||||||
|
return se050_blake2s_final(&ctx, out, outlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_blake2s_keyed(void *out, size_t outlen, const void *key, size_t keylen,
|
||||||
|
const void *data, size_t len)
|
||||||
|
{
|
||||||
|
se050_blake2s_ctx_t ctx;
|
||||||
|
int ret = se050_blake2s_init_key(&ctx, outlen, key, keylen);
|
||||||
|
if (ret != 0) return ret;
|
||||||
|
ret = se050_blake2s_update(&ctx, data, len);
|
||||||
|
if (ret != 0) { se050_blake2s_zeroize(&ctx); return ret; }
|
||||||
|
return se050_blake2s_final(&ctx, out, outlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void se050_blake2s_zeroize(se050_blake2s_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
if (ctx) {
|
||||||
|
blake2s_internal_t *inner = (blake2s_internal_t *)ctx;
|
||||||
|
memzero_explicit(inner, sizeof(blake2s_internal_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_wireguard_derive_key(uint8_t out[32], const uint8_t *input, size_t inlen)
|
||||||
|
{
|
||||||
|
if (!out || !input) return -1;
|
||||||
|
static const uint8_t LABEL[24] = {
|
||||||
|
0x77,0x69,0x72,0x65,0x67,0x75,0x61,0x72,
|
||||||
|
0x64,0x20,0x6b,0x65,0x79,0x20,0x64,0x65,
|
||||||
|
0x72,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e
|
||||||
|
};
|
||||||
|
return se050_blake2s_keyed(out, 32, LABEL, 24, input, inlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_wireguard_generate_secret(uint8_t out[32], const uint8_t *input, size_t inlen)
|
||||||
|
{
|
||||||
|
if (!out || !input) return -1;
|
||||||
|
static const uint8_t LABEL[22] = {
|
||||||
|
0x77,0x69,0x72,0x65,0x67,0x75,0x61,0x72,
|
||||||
|
0x64,0x20,0x67,0x65,0x6e,0x65,0x72,0x61,
|
||||||
|
0x74,0x65,0x20,0x73,0x65,0x63
|
||||||
|
};
|
||||||
|
return se050_blake2s_keyed(out, 32, LABEL, 22, input, inlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BLAKE2S_TEST
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* RFC 7693 Test Vector (page 15) - BLAKE2s-256("abc") */
|
||||||
|
/* Note: The value 508c5e8c... is the correct BLAKE2s-256("abc") digest */
|
||||||
|
static const uint8_t BLAKE2S_ABC_DIGEST[32] = {
|
||||||
|
0x50,0x8c,0x5e,0x8c,0x32,0x7c,0x14,0xe2,
|
||||||
|
0xe1,0xa7,0x2b,0xa3,0x4e,0xeb,0x45,0x2f,
|
||||||
|
0x37,0x45,0x8b,0x20,0x9e,0xd6,0x3a,0x29,
|
||||||
|
0x4d,0x99,0x9b,0x4c,0x86,0x67,0x59,0x82
|
||||||
|
};
|
||||||
|
|
||||||
|
static void print_hex(const char *label, const uint8_t *buf, size_t len)
|
||||||
|
{
|
||||||
|
printf("%s: ", label);
|
||||||
|
for (size_t i = 0; i < len; i++) printf("%02x", buf[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
uint8_t digest[32];
|
||||||
|
|
||||||
|
printf("BLAKE2s Test Suite\n==================\n\n");
|
||||||
|
|
||||||
|
printf("Test: RFC 7693 \"abc\" (page 15)\n");
|
||||||
|
se050_blake2s(digest, 32, (const uint8_t*)"abc", 3);
|
||||||
|
print_hex("Expected", BLAKE2S_ABC_DIGEST, 32);
|
||||||
|
print_hex("Computed", digest, 32);
|
||||||
|
|
||||||
|
if (memcmp(digest, BLAKE2S_ABC_DIGEST, 32) == 0) {
|
||||||
|
printf("[PASS] RFC 7693 \"abc\" test vector\n");
|
||||||
|
printf("==================\n");
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
printf("[FAIL] RFC 7693 \"abc\" test vector\n");
|
||||||
|
printf("==================\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,642 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_chacha20_poly1305.c
|
||||||
|
* @brief ChaCha20-Poly1305 AEAD Implementation
|
||||||
|
*
|
||||||
|
* Based on RFC 8439 (ChaCha20-Poly1305) and the WireGuard protocol spec.
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*
|
||||||
|
* Verified against RFC 8439 Section 2.8.2 test vector.
|
||||||
|
*
|
||||||
|
* Design notes
|
||||||
|
* ------------
|
||||||
|
* Poly1305 state uses the 5×26-bit limb representation throughout.
|
||||||
|
* The same poly1305_* implementation is used for both ESP32 and 64-bit
|
||||||
|
* targets; 64-bit intermediates (uint64_t) keep carry arithmetic simple
|
||||||
|
* and correct on both platforms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "se050_chacha20_poly1305.h"
|
||||||
|
#include "se050_crypto_utils.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* ChaCha20
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
static inline uint32_t rotl32(uint32_t x, unsigned n)
|
||||||
|
{
|
||||||
|
return (x << n) | (x >> (32u - n));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define QR(a, b, c, d) \
|
||||||
|
a += b; d ^= a; d = rotl32(d, 16); \
|
||||||
|
c += d; b ^= c; b = rotl32(b, 12); \
|
||||||
|
a += b; d ^= a; d = rotl32(d, 8); \
|
||||||
|
c += d; b ^= c; b = rotl32(b, 7);
|
||||||
|
|
||||||
|
static inline uint32_t load32_le(const uint8_t *p)
|
||||||
|
{
|
||||||
|
return (uint32_t)p[0]
|
||||||
|
| ((uint32_t)p[1] << 8)
|
||||||
|
| ((uint32_t)p[2] << 16)
|
||||||
|
| ((uint32_t)p[3] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ChaCha20 block function (RFC 8439 §2.1)
|
||||||
|
* @param output 64-byte keystream block
|
||||||
|
* @param key 32-byte key
|
||||||
|
* @param counter 32-bit block counter (little-endian in the state)
|
||||||
|
* @param nonce 12-byte nonce
|
||||||
|
*/
|
||||||
|
void se050_chacha20_block(uint8_t output[64], const uint8_t key[32],
|
||||||
|
uint32_t counter, const uint8_t nonce[12])
|
||||||
|
{
|
||||||
|
uint32_t s[16] = {
|
||||||
|
/* "expand 32-byte k" */
|
||||||
|
0x61707865u, 0x3320646eu, 0x79622d32u, 0x6b206574u,
|
||||||
|
/* key words 0-7 */
|
||||||
|
load32_le(key + 0), load32_le(key + 4),
|
||||||
|
load32_le(key + 8), load32_le(key + 12),
|
||||||
|
load32_le(key + 16), load32_le(key + 20),
|
||||||
|
load32_le(key + 24), load32_le(key + 28),
|
||||||
|
/* counter, nonce words 0-2 */
|
||||||
|
counter,
|
||||||
|
load32_le(nonce + 0), load32_le(nonce + 4), load32_le(nonce + 8)
|
||||||
|
};
|
||||||
|
uint32_t w[16];
|
||||||
|
memcpy(w, s, sizeof(s));
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
/* column rounds */
|
||||||
|
QR(w[ 0], w[ 4], w[ 8], w[12]);
|
||||||
|
QR(w[ 1], w[ 5], w[ 9], w[13]);
|
||||||
|
QR(w[ 2], w[ 6], w[10], w[14]);
|
||||||
|
QR(w[ 3], w[ 7], w[11], w[15]);
|
||||||
|
/* diagonal rounds */
|
||||||
|
QR(w[ 0], w[ 5], w[10], w[15]);
|
||||||
|
QR(w[ 1], w[ 6], w[11], w[12]);
|
||||||
|
QR(w[ 2], w[ 7], w[ 8], w[13]);
|
||||||
|
QR(w[ 3], w[ 4], w[ 9], w[14]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
uint32_t v = w[i] + s[i];
|
||||||
|
output[i*4+0] = (uint8_t)(v);
|
||||||
|
output[i*4+1] = (uint8_t)(v >> 8);
|
||||||
|
output[i*4+2] = (uint8_t)(v >> 16);
|
||||||
|
output[i*4+3] = (uint8_t)(v >> 24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ChaCha20 stream cipher (encrypt or decrypt; symmetric)
|
||||||
|
* @param counter Starting block counter (use 1 for AEAD payload)
|
||||||
|
*/
|
||||||
|
void se050_chacha20(uint8_t *output, const uint8_t *input, size_t len,
|
||||||
|
const uint8_t key[32], uint32_t counter,
|
||||||
|
const uint8_t nonce[12])
|
||||||
|
{
|
||||||
|
uint8_t block[64];
|
||||||
|
while (len > 0) {
|
||||||
|
se050_chacha20_block(block, key, counter++, nonce);
|
||||||
|
size_t chunk = len < 64u ? len : 64u;
|
||||||
|
for (size_t j = 0; j < chunk; j++)
|
||||||
|
output[j] = input[j] ^ block[j];
|
||||||
|
output += chunk;
|
||||||
|
input += chunk;
|
||||||
|
len -= chunk;
|
||||||
|
}
|
||||||
|
memzero_explicit(block, sizeof(block));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Poly1305 — 5 × 26-bit limb representation
|
||||||
|
*
|
||||||
|
* State: h = h[0] + h[1]*2^26 + h[2]*2^52 + h[3]*2^78 + h[4]*2^104
|
||||||
|
* r, s loaded from the 32-byte one-time key.
|
||||||
|
*
|
||||||
|
* Reference: RFC 8439 §2.5, D.J. Bernstein's original paper.
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t r[5]; /* clamped r, 26-bit limbs */
|
||||||
|
uint64_t h[5]; /* accumulator, 26-bit limbs */
|
||||||
|
uint32_t s[4]; /* s = key[16..31], four 32-bit words */
|
||||||
|
uint8_t buf[16];
|
||||||
|
size_t left; /* bytes in buf (0..15) */
|
||||||
|
} poly1305_state_t;
|
||||||
|
|
||||||
|
static void poly1305_init(poly1305_state_t *st, const uint8_t key[32])
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* r = key[0..15], split into 26-bit limbs with clamping.
|
||||||
|
* Clamping clears specific bits per RFC 8439 §2.5.1.
|
||||||
|
*
|
||||||
|
* The 128 bits of r span key[0..15]:
|
||||||
|
* r[0] = bits 0..25 of r → key[0..3] & 0x3ffffff
|
||||||
|
* r[1] = bits 26..51 of r → clamp mask 0x3ffff03
|
||||||
|
* r[2] = bits 52..77 of r → clamp mask 0x3ffc0ff
|
||||||
|
* r[3] = bits 78..103 of r → clamp mask 0x3f03fff
|
||||||
|
* r[4] = bits 104..127 of r → clamp mask 0x00fffff
|
||||||
|
*/
|
||||||
|
uint64_t t0 = (uint64_t)key[ 0] | ((uint64_t)key[ 1] << 8)
|
||||||
|
| ((uint64_t)key[ 2] << 16) | ((uint64_t)key[ 3] << 24);
|
||||||
|
uint64_t t1 = (uint64_t)key[ 4] | ((uint64_t)key[ 5] << 8)
|
||||||
|
| ((uint64_t)key[ 6] << 16) | ((uint64_t)key[ 7] << 24);
|
||||||
|
uint64_t t2 = (uint64_t)key[ 8] | ((uint64_t)key[ 9] << 8)
|
||||||
|
| ((uint64_t)key[10] << 16) | ((uint64_t)key[11] << 24);
|
||||||
|
uint64_t t3 = (uint64_t)key[12] | ((uint64_t)key[13] << 8)
|
||||||
|
| ((uint64_t)key[14] << 16) | ((uint64_t)key[15] << 24);
|
||||||
|
|
||||||
|
st->r[0] = t0 & 0x3ffffffULL;
|
||||||
|
st->r[1] = ((t0 >> 26) | (t1 << 6)) & 0x3ffff03ULL;
|
||||||
|
st->r[2] = ((t1 >> 20) | (t2 << 12)) & 0x3ffc0ffULL;
|
||||||
|
st->r[3] = ((t2 >> 14) | (t3 << 18)) & 0x3f03fffULL;
|
||||||
|
st->r[4] = (t3 >> 8) & 0x00fffffULL;
|
||||||
|
|
||||||
|
/* s = key[16..31], four little-endian 32-bit words */
|
||||||
|
st->s[0] = (uint32_t)key[16] | ((uint32_t)key[17] << 8)
|
||||||
|
| ((uint32_t)key[18] << 16) | ((uint32_t)key[19] << 24);
|
||||||
|
st->s[1] = (uint32_t)key[20] | ((uint32_t)key[21] << 8)
|
||||||
|
| ((uint32_t)key[22] << 16) | ((uint32_t)key[23] << 24);
|
||||||
|
st->s[2] = (uint32_t)key[24] | ((uint32_t)key[25] << 8)
|
||||||
|
| ((uint32_t)key[26] << 16) | ((uint32_t)key[27] << 24);
|
||||||
|
st->s[3] = (uint32_t)key[28] | ((uint32_t)key[29] << 8)
|
||||||
|
| ((uint32_t)key[30] << 16) | ((uint32_t)key[31] << 24);
|
||||||
|
|
||||||
|
st->h[0] = st->h[1] = st->h[2] = st->h[3] = st->h[4] = 0;
|
||||||
|
st->left = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process one 16-byte block: h = (h + m) * r mod 2^130 - 5
|
||||||
|
*
|
||||||
|
* @param pad128 true → full block, append bit 2^128 (complete message chunk)
|
||||||
|
* false → partial final block, append 2^(8*actual_len) already
|
||||||
|
* encoded in the loaded words (handled by caller)
|
||||||
|
*/
|
||||||
|
static void poly1305_block(poly1305_state_t *st,
|
||||||
|
const uint8_t m[16], int pad128)
|
||||||
|
{
|
||||||
|
/* Load 16 bytes as four 32-bit LE words, split into 26-bit limbs */
|
||||||
|
uint64_t d0 = (uint64_t)m[ 0] | ((uint64_t)m[ 1] << 8)
|
||||||
|
| ((uint64_t)m[ 2] << 16) | ((uint64_t)m[ 3] << 24);
|
||||||
|
uint64_t d1 = (uint64_t)m[ 4] | ((uint64_t)m[ 5] << 8)
|
||||||
|
| ((uint64_t)m[ 6] << 16) | ((uint64_t)m[ 7] << 24);
|
||||||
|
uint64_t d2 = (uint64_t)m[ 8] | ((uint64_t)m[ 9] << 8)
|
||||||
|
| ((uint64_t)m[10] << 16) | ((uint64_t)m[11] << 24);
|
||||||
|
uint64_t d3 = (uint64_t)m[12] | ((uint64_t)m[13] << 8)
|
||||||
|
| ((uint64_t)m[14] << 16) | ((uint64_t)m[15] << 24);
|
||||||
|
|
||||||
|
/* Split into 26-bit limbs */
|
||||||
|
uint64_t m0 = d0 & 0x3ffffffULL;
|
||||||
|
uint64_t m1 = ((d0 >> 26) | (d1 << 6)) & 0x3ffffffULL;
|
||||||
|
uint64_t m2 = ((d1 >> 20) | (d2 << 12)) & 0x3ffffffULL;
|
||||||
|
uint64_t m3 = ((d2 >> 14) | (d3 << 18)) & 0x3ffffffULL;
|
||||||
|
uint64_t m4 = (d3 >> 8);
|
||||||
|
if (pad128) m4 |= (1ULL << 24); /* 2^128 in 26-bit limb representation */
|
||||||
|
|
||||||
|
/* h += m */
|
||||||
|
uint64_t h0 = st->h[0] + m0;
|
||||||
|
uint64_t h1 = st->h[1] + m1;
|
||||||
|
uint64_t h2 = st->h[2] + m2;
|
||||||
|
uint64_t h3 = st->h[3] + m3;
|
||||||
|
uint64_t h4 = st->h[4] + m4;
|
||||||
|
|
||||||
|
/* h = h * r mod 2^130 - 5
|
||||||
|
* Using the identity: x * 2^130 ≡ x * 5 (mod 2^130 - 5)
|
||||||
|
* so r[i]*5 terms fold the high limbs back in.
|
||||||
|
*/
|
||||||
|
uint64_t r0 = st->r[0], r1 = st->r[1], r2 = st->r[2],
|
||||||
|
r3 = st->r[3], r4 = st->r[4];
|
||||||
|
uint64_t r1_5 = r1 * 5, r2_5 = r2 * 5, r3_5 = r3 * 5, r4_5 = r4 * 5;
|
||||||
|
|
||||||
|
uint64_t t0 = h0*r0 + h1*r4_5 + h2*r3_5 + h3*r2_5 + h4*r1_5;
|
||||||
|
uint64_t t1 = h0*r1 + h1*r0 + h2*r4_5 + h3*r3_5 + h4*r2_5;
|
||||||
|
uint64_t t2 = h0*r2 + h1*r1 + h2*r0 + h3*r4_5 + h4*r3_5;
|
||||||
|
uint64_t t3 = h0*r3 + h1*r2 + h2*r1 + h3*r0 + h4*r4_5;
|
||||||
|
uint64_t t4 = h0*r4 + h1*r3 + h2*r2 + h3*r1 + h4*r0;
|
||||||
|
|
||||||
|
/* Carry propagation (all limbs → 26 bits) */
|
||||||
|
uint64_t c;
|
||||||
|
c = t0 >> 26; st->h[0] = t0 & 0x3ffffffULL; t1 += c;
|
||||||
|
c = t1 >> 26; st->h[1] = t1 & 0x3ffffffULL; t2 += c;
|
||||||
|
c = t2 >> 26; st->h[2] = t2 & 0x3ffffffULL; t3 += c;
|
||||||
|
c = t3 >> 26; st->h[3] = t3 & 0x3ffffffULL; t4 += c;
|
||||||
|
c = t4 >> 26; st->h[4] = t4 & 0x3ffffffULL;
|
||||||
|
/* Wrap-around carry: h[4] overflow → h[0] += 5 * overflow */
|
||||||
|
st->h[0] += c * 5;
|
||||||
|
c = st->h[0] >> 26; st->h[0] &= 0x3ffffffULL;
|
||||||
|
st->h[1] += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void poly1305_update(poly1305_state_t *st, const uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
/* Fill partial buffer first */
|
||||||
|
if (st->left > 0) {
|
||||||
|
size_t need = 16 - st->left;
|
||||||
|
if (len < need) {
|
||||||
|
memcpy(st->buf + st->left, data, len);
|
||||||
|
st->left += len;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(st->buf + st->left, data, need);
|
||||||
|
poly1305_block(st, st->buf, 1);
|
||||||
|
st->left = 0;
|
||||||
|
data += need;
|
||||||
|
len -= need;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Full blocks */
|
||||||
|
while (len >= 16) {
|
||||||
|
poly1305_block(st, data, 1);
|
||||||
|
data += 16;
|
||||||
|
len -= 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buffer remainder */
|
||||||
|
if (len > 0) {
|
||||||
|
memcpy(st->buf, data, len);
|
||||||
|
st->left = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void poly1305_final(poly1305_state_t *st, uint8_t mac[16])
|
||||||
|
{
|
||||||
|
/* Process partial final block (if any) with 2^(8*n) pad instead of 2^128 */
|
||||||
|
if (st->left > 0) {
|
||||||
|
uint8_t padded[16];
|
||||||
|
memcpy(padded, st->buf, st->left);
|
||||||
|
padded[st->left] = 0x01;
|
||||||
|
memset(padded + st->left + 1, 0, 16 - st->left - 1);
|
||||||
|
/* The 0x01 byte IS the pad bit; do NOT set pad128 flag */
|
||||||
|
poly1305_block(st, padded, 0);
|
||||||
|
memzero_explicit(padded, sizeof(padded));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Final reduction: bring h fully into [0, 2^130-5).
|
||||||
|
*
|
||||||
|
* Two passes:
|
||||||
|
* 1. Propagate any carry from h[4] back through h[0..4].
|
||||||
|
* 2. Conditionally subtract p = 2^130 - 5 if h >= p.
|
||||||
|
* Done in constant-time using a mask derived from the borrow.
|
||||||
|
*/
|
||||||
|
uint64_t h0 = st->h[0], h1 = st->h[1], h2 = st->h[2],
|
||||||
|
h3 = st->h[3], h4 = st->h[4];
|
||||||
|
|
||||||
|
/* Pass 1: full carry propagation */
|
||||||
|
uint64_t c;
|
||||||
|
c = h4 >> 26; h4 &= 0x3ffffffULL; h0 += c * 5;
|
||||||
|
c = h0 >> 26; h0 &= 0x3ffffffULL; h1 += c;
|
||||||
|
c = h1 >> 26; h1 &= 0x3ffffffULL; h2 += c;
|
||||||
|
c = h2 >> 26; h2 &= 0x3ffffffULL; h3 += c;
|
||||||
|
c = h3 >> 26; h3 &= 0x3ffffffULL; h4 += c;
|
||||||
|
|
||||||
|
/* Pass 2: try h - p = h + 5 - 2^130; keep if no borrow */
|
||||||
|
uint64_t g0 = h0 + 5;
|
||||||
|
c = g0 >> 26; g0 &= 0x3ffffffULL;
|
||||||
|
uint64_t g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffffULL;
|
||||||
|
uint64_t g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffffULL;
|
||||||
|
uint64_t g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffffULL;
|
||||||
|
uint64_t g4 = h4 + c - (1ULL << 26);
|
||||||
|
|
||||||
|
/* mask = 0xfff...fff if g4 did NOT underflow (i.e. h >= p), else 0 */
|
||||||
|
uint64_t mask = (uint64_t)(-(int64_t)(1 - (g4 >> 63)));
|
||||||
|
h0 = (h0 & ~mask) | (g0 & mask);
|
||||||
|
h1 = (h1 & ~mask) | (g1 & mask);
|
||||||
|
h2 = (h2 & ~mask) | (g2 & mask);
|
||||||
|
h3 = (h3 & ~mask) | (g3 & mask);
|
||||||
|
h4 = h4 & ~mask; /* when h>=p, h4 reduces to 0 after subtract */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reconstruct 128-bit h from five 26-bit limbs, then add s = key[16..31].
|
||||||
|
*
|
||||||
|
* After reduction h4 == 0, so h fits in 104 bits.
|
||||||
|
* We extract four 32-bit words from h before adding s to keep carry
|
||||||
|
* propagation correct (each f-word carry is at most 1 bit).
|
||||||
|
*
|
||||||
|
* h bits 0.. 31 → w0 = h0 | (h1 << 26) [bits 0..51, take lower 32]
|
||||||
|
* h bits 32.. 63 → w1 = (h1 >> 6) | (h2 << 20) [bits 32..77, take lower 32]
|
||||||
|
* h bits 64.. 95 → w2 = (h2 >> 12) | (h3 << 14) [bits 64..103, take lower 32]
|
||||||
|
* h bits 96..127 → w3 = (h3 >> 18) | (h4 << 8) [bits 96..129, take lower 32]
|
||||||
|
*
|
||||||
|
* Each w-value must be masked to 32 bits BEFORE adding s so that the
|
||||||
|
* carry into the next word is exactly 0 or 1.
|
||||||
|
*/
|
||||||
|
uint64_t w0 = (h0 | (h1 << 26)) & 0xffffffffULL;
|
||||||
|
uint64_t w1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffffULL;
|
||||||
|
uint64_t w2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffffULL;
|
||||||
|
uint64_t w3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffffULL;
|
||||||
|
|
||||||
|
uint64_t f0 = w0 + (uint64_t)st->s[0];
|
||||||
|
uint64_t f1 = w1 + (uint64_t)st->s[1] + (f0 >> 32);
|
||||||
|
uint64_t f2 = w2 + (uint64_t)st->s[2] + (f1 >> 32);
|
||||||
|
uint64_t f3 = w3 + (uint64_t)st->s[3] + (f2 >> 32);
|
||||||
|
|
||||||
|
mac[ 0] = (uint8_t)(f0); mac[ 1] = (uint8_t)(f0 >> 8);
|
||||||
|
mac[ 2] = (uint8_t)(f0 >> 16); mac[ 3] = (uint8_t)(f0 >> 24);
|
||||||
|
mac[ 4] = (uint8_t)(f1); mac[ 5] = (uint8_t)(f1 >> 8);
|
||||||
|
mac[ 6] = (uint8_t)(f1 >> 16); mac[ 7] = (uint8_t)(f1 >> 24);
|
||||||
|
mac[ 8] = (uint8_t)(f2); mac[ 9] = (uint8_t)(f2 >> 8);
|
||||||
|
mac[10] = (uint8_t)(f2 >> 16); mac[11] = (uint8_t)(f2 >> 24);
|
||||||
|
mac[12] = (uint8_t)(f3); mac[13] = (uint8_t)(f3 >> 8);
|
||||||
|
mac[14] = (uint8_t)(f3 >> 16); mac[15] = (uint8_t)(f3 >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* ChaCha20-Poly1305 AEAD (RFC 8439 §2.8)
|
||||||
|
* ============================================================================
|
||||||
|
*
|
||||||
|
* Poly1305 input layout:
|
||||||
|
* AAD || pad(AAD,16) || ciphertext || pad(CT,16)
|
||||||
|
* || LE64(len(AAD)) || LE64(len(ciphertext))
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
static void store64_le(uint8_t *p, uint64_t v)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 8; i++) { p[i] = (uint8_t)v; v >>= 8; }
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aead_poly1305_input(poly1305_state_t *st,
|
||||||
|
const uint8_t *aad, size_t aad_len,
|
||||||
|
const uint8_t *ct, size_t ct_len)
|
||||||
|
{
|
||||||
|
static const uint8_t zeros[16] = {0};
|
||||||
|
|
||||||
|
poly1305_update(st, aad, aad_len);
|
||||||
|
size_t aad_pad = (16u - (aad_len & 15u)) & 15u;
|
||||||
|
if (aad_pad) poly1305_update(st, zeros, aad_pad);
|
||||||
|
|
||||||
|
poly1305_update(st, ct, ct_len);
|
||||||
|
size_t ct_pad = (16u - (ct_len & 15u)) & 15u;
|
||||||
|
if (ct_pad) poly1305_update(st, zeros, ct_pad);
|
||||||
|
|
||||||
|
uint8_t lengths[16];
|
||||||
|
store64_le(lengths + 0, (uint64_t)aad_len);
|
||||||
|
store64_le(lengths + 8, (uint64_t)ct_len);
|
||||||
|
poly1305_update(st, lengths, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Context init / zeroize --- */
|
||||||
|
|
||||||
|
int se050_chacha20_poly1305_init(se050_chacha20_poly1305_ctx_t *ctx,
|
||||||
|
const uint8_t key[CHACHA20_KEY_SIZE])
|
||||||
|
{
|
||||||
|
if (!ctx || !key) return -1;
|
||||||
|
memcpy(ctx->key, key, CHACHA20_KEY_SIZE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void se050_chacha20_poly1305_zeroize(se050_chacha20_poly1305_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
if (ctx) memzero_explicit(ctx->key, CHACHA20_KEY_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Encrypt --- */
|
||||||
|
|
||||||
|
int se050_chacha20_poly1305_encrypt(se050_chacha20_poly1305_ctx_t *ctx,
|
||||||
|
const uint8_t nonce[CHACHA20_NONCE_SIZE],
|
||||||
|
const uint8_t *plaintext, size_t pt_len,
|
||||||
|
const uint8_t *aad, size_t aad_len,
|
||||||
|
uint8_t *ciphertext,
|
||||||
|
uint8_t tag[POLY1305_TAG_SIZE])
|
||||||
|
{
|
||||||
|
if (!ctx || !nonce || !ciphertext || !tag) return -1;
|
||||||
|
if (pt_len > 0 && !plaintext) return -1;
|
||||||
|
if (aad_len > 0 && !aad) return -1;
|
||||||
|
|
||||||
|
/* 1. Derive one-time Poly1305 key from counter=0 block */
|
||||||
|
uint8_t otk[64];
|
||||||
|
se050_chacha20_block(otk, ctx->key, 0, nonce);
|
||||||
|
|
||||||
|
/* 2. Encrypt plaintext (counter starts at 1) */
|
||||||
|
se050_chacha20(ciphertext, plaintext, pt_len, ctx->key, 1, nonce);
|
||||||
|
|
||||||
|
/* 3. Compute tag over AAD + ciphertext */
|
||||||
|
poly1305_state_t st;
|
||||||
|
poly1305_init(&st, otk); /* otk[0..31] is the Poly1305 key */
|
||||||
|
aead_poly1305_input(&st, aad, aad_len, ciphertext, pt_len);
|
||||||
|
poly1305_final(&st, tag);
|
||||||
|
|
||||||
|
memzero_explicit(otk, sizeof(otk));
|
||||||
|
memzero_explicit(&st, sizeof(st));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Decrypt --- */
|
||||||
|
|
||||||
|
int se050_chacha20_poly1305_decrypt(se050_chacha20_poly1305_ctx_t *ctx,
|
||||||
|
const uint8_t nonce[CHACHA20_NONCE_SIZE],
|
||||||
|
const uint8_t *ciphertext, size_t ct_len,
|
||||||
|
const uint8_t *aad, size_t aad_len,
|
||||||
|
const uint8_t tag[POLY1305_TAG_SIZE],
|
||||||
|
uint8_t *plaintext)
|
||||||
|
{
|
||||||
|
if (!ctx || !nonce || !ciphertext || !tag || !plaintext) return -1;
|
||||||
|
if (ct_len > 0 && !ciphertext) return -1;
|
||||||
|
if (aad_len > 0 && !aad) return -1;
|
||||||
|
|
||||||
|
/* 1. Derive one-time Poly1305 key */
|
||||||
|
uint8_t otk[64];
|
||||||
|
se050_chacha20_block(otk, ctx->key, 0, nonce);
|
||||||
|
|
||||||
|
/* 2. Verify tag BEFORE decrypting (authenticate-then-decrypt) */
|
||||||
|
poly1305_state_t st;
|
||||||
|
poly1305_init(&st, otk);
|
||||||
|
aead_poly1305_input(&st, aad, aad_len, ciphertext, ct_len);
|
||||||
|
uint8_t expected[POLY1305_TAG_SIZE];
|
||||||
|
poly1305_final(&st, expected);
|
||||||
|
|
||||||
|
/* Constant-time comparison */
|
||||||
|
int ok = crypto_memneq(expected, tag, POLY1305_TAG_SIZE) == 0 ? 0 : -1;
|
||||||
|
|
||||||
|
/* 3. Decrypt only if tag is valid */
|
||||||
|
if (ok == 0)
|
||||||
|
se050_chacha20(plaintext, ciphertext, ct_len, ctx->key, 1, nonce);
|
||||||
|
|
||||||
|
memzero_explicit(otk, sizeof(otk));
|
||||||
|
memzero_explicit(&st, sizeof(st));
|
||||||
|
memzero_explicit(expected, sizeof(expected));
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- WireGuard convenience wrappers (no AAD) --- */
|
||||||
|
|
||||||
|
int se050_wireguard_encrypt(const uint8_t key[WG_KEY_SIZE],
|
||||||
|
const uint8_t nonce[WG_NONCE_SIZE],
|
||||||
|
const uint8_t *plaintext, size_t len,
|
||||||
|
uint8_t *ciphertext, uint8_t tag[POLY1305_TAG_SIZE])
|
||||||
|
{
|
||||||
|
se050_chacha20_poly1305_ctx_t ctx;
|
||||||
|
int ret = se050_chacha20_poly1305_init(&ctx, key);
|
||||||
|
if (ret != 0) return ret;
|
||||||
|
ret = se050_chacha20_poly1305_encrypt(&ctx, nonce,
|
||||||
|
plaintext, len,
|
||||||
|
NULL, 0,
|
||||||
|
ciphertext, tag);
|
||||||
|
se050_chacha20_poly1305_zeroize(&ctx);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_wireguard_decrypt(const uint8_t key[WG_KEY_SIZE],
|
||||||
|
const uint8_t nonce[WG_NONCE_SIZE],
|
||||||
|
const uint8_t *ciphertext, size_t len,
|
||||||
|
const uint8_t tag[POLY1305_TAG_SIZE],
|
||||||
|
uint8_t *plaintext)
|
||||||
|
{
|
||||||
|
se050_chacha20_poly1305_ctx_t ctx;
|
||||||
|
int ret = se050_chacha20_poly1305_init(&ctx, key);
|
||||||
|
if (ret != 0) return ret;
|
||||||
|
ret = se050_chacha20_poly1305_decrypt(&ctx, nonce,
|
||||||
|
ciphertext, len,
|
||||||
|
NULL, 0,
|
||||||
|
tag, plaintext);
|
||||||
|
se050_chacha20_poly1305_zeroize(&ctx);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Self-test (compile with -DCHACHA20_POLY1305_TEST)
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#ifdef CHACHA20_POLY1305_TEST
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* RFC 8439 §2.8.2 */
|
||||||
|
static const uint8_t TV_KEY[32] = {
|
||||||
|
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
|
||||||
|
0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
|
||||||
|
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
|
||||||
|
0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f
|
||||||
|
};
|
||||||
|
static const uint8_t TV_NONCE[12] = {
|
||||||
|
0x07,0x00,0x00,0x00,0x40,0x41,0x42,0x43,
|
||||||
|
0x44,0x45,0x46,0x47
|
||||||
|
};
|
||||||
|
/* RFC 8439 §2.8.2: AAD is 12 bytes */
|
||||||
|
static const uint8_t TV_AAD[12] = {
|
||||||
|
0x50,0x51,0x52,0x53,0xc0,0xc1,0xc2,0xc3,
|
||||||
|
0xc4,0xc5,0xc6,0xc7
|
||||||
|
};
|
||||||
|
static const uint8_t TV_PT[114] = {
|
||||||
|
0x4c,0x61,0x64,0x69,0x65,0x73,0x20,0x61,0x6e,0x64,0x20,0x47,0x65,0x6e,
|
||||||
|
0x74,0x6c,0x65,0x6d,0x65,0x6e,0x20,0x6f,0x66,0x20,0x74,0x68,0x65,0x20,
|
||||||
|
0x63,0x6c,0x61,0x73,0x73,0x20,0x6f,0x66,0x20,0x27,0x39,0x39,0x3a,0x20,
|
||||||
|
0x49,0x66,0x20,0x49,0x20,0x63,0x6f,0x75,0x6c,0x64,0x20,0x6f,0x66,0x66,
|
||||||
|
0x65,0x72,0x20,0x79,0x6f,0x75,0x20,0x6f,0x6e,0x6c,0x79,0x20,0x6f,0x6e,
|
||||||
|
0x65,0x20,0x74,0x69,0x70,0x20,0x66,0x6f,0x72,0x20,0x74,0x68,0x65,0x20,
|
||||||
|
0x66,0x75,0x74,0x75,0x72,0x65,0x2c,0x20,0x73,0x75,0x6e,0x73,0x63,0x72,
|
||||||
|
0x65,0x65,0x6e,0x20,0x77,0x6f,0x75,0x6c,0x64,0x20,0x62,0x65,0x20,0x69,
|
||||||
|
0x74,0x2e
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
* Verified with Python reference implementation against RFC 8439 §2.8.2.
|
||||||
|
* PT = "Ladies and Gentlemen of the class of '99: If I could offer you only
|
||||||
|
* one tip for the future, sunscreen would be it." (114 bytes)
|
||||||
|
*/
|
||||||
|
static const uint8_t TV_CT[114] = {
|
||||||
|
0xd3,0x1a,0x8d,0x34,0x64,0x8e,0x60,0xdb,0x7b,0x86,0xaf,0xbc,0x53,0xef,
|
||||||
|
0x7e,0xc2,0xa4,0xad,0xed,0x51,0x29,0x6e,0x08,0xfe,0xa9,0xe2,0xb5,0xa7,
|
||||||
|
0x36,0xee,0x62,0xd6,0x3d,0xbe,0xa4,0x5e,0x8c,0xa9,0x67,0x12,0x82,0xfa,
|
||||||
|
0xfb,0x69,0xda,0x92,0x72,0x8b,0x1a,0x71,0xde,0x0a,0x9e,0x06,0x0b,0x29,
|
||||||
|
0x05,0xd6,0xa5,0xb6,0x7e,0xcd,0x3b,0x36,0x92,0xdd,0xbd,0x7f,0x2d,0x77,
|
||||||
|
0x8b,0x8c,0x98,0x03,0xae,0xe3,0x28,0x09,0x1b,0x58,0xfa,0xb3,0x24,0xe4,
|
||||||
|
0xfa,0xd6,0x75,0x94,0x55,0x85,0x80,0x8b,0x48,0x31,0xd7,0xbc,0x3f,0xf4,
|
||||||
|
0xde,0xf0,0x8e,0x4b,0x7a,0x9d,0xe5,0x76,0xd2,0x65,0x86,0xce,0xc6,0x4b,
|
||||||
|
0x61,0x16
|
||||||
|
};
|
||||||
|
static const uint8_t TV_TAG[16] = {
|
||||||
|
0x1a,0xe1,0x0b,0x59,0x4f,0x09,0xe2,0x6a,
|
||||||
|
0x7e,0x90,0x2e,0xcb,0xd0,0x60,0x06,0x91
|
||||||
|
};
|
||||||
|
|
||||||
|
static void print_hex(const char *label, const uint8_t *b, size_t n)
|
||||||
|
{
|
||||||
|
printf(" %-12s: ", label);
|
||||||
|
for (size_t i = 0; i < n; i++) printf("%02x", b[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int fail = 0;
|
||||||
|
printf("ChaCha20-Poly1305 Test Suite\n============================\n\n");
|
||||||
|
|
||||||
|
/* Test 1: RFC 8439 §2.8.2 encrypt */
|
||||||
|
{
|
||||||
|
printf("Test 1: RFC 8439 §2.8.2 encrypt\n");
|
||||||
|
uint8_t ct[114], tag[16];
|
||||||
|
se050_chacha20_poly1305_ctx_t ctx;
|
||||||
|
se050_chacha20_poly1305_init(&ctx, TV_KEY);
|
||||||
|
se050_chacha20_poly1305_encrypt(&ctx, TV_NONCE,
|
||||||
|
TV_PT, sizeof(TV_PT),
|
||||||
|
TV_AAD, sizeof(TV_AAD),
|
||||||
|
ct, tag);
|
||||||
|
int ct_ok = memcmp(ct, TV_CT, sizeof(TV_CT)) == 0;
|
||||||
|
int tag_ok = memcmp(tag, TV_TAG, 16) == 0;
|
||||||
|
print_hex("tag expected", TV_TAG, 16);
|
||||||
|
print_hex("tag computed", tag, 16);
|
||||||
|
if (ct_ok && tag_ok) printf(" [PASS]\n\n");
|
||||||
|
else { printf(" [FAIL] ct_ok=%d tag_ok=%d\n\n", ct_ok, tag_ok); fail++; }
|
||||||
|
se050_chacha20_poly1305_zeroize(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 2: RFC 8439 §2.8.2 decrypt */
|
||||||
|
{
|
||||||
|
printf("Test 2: RFC 8439 §2.8.2 decrypt\n");
|
||||||
|
uint8_t pt[114];
|
||||||
|
se050_chacha20_poly1305_ctx_t ctx;
|
||||||
|
se050_chacha20_poly1305_init(&ctx, TV_KEY);
|
||||||
|
int ret = se050_chacha20_poly1305_decrypt(&ctx, TV_NONCE,
|
||||||
|
TV_CT, sizeof(TV_CT),
|
||||||
|
TV_AAD, sizeof(TV_AAD),
|
||||||
|
TV_TAG, pt);
|
||||||
|
int pt_ok = (ret == 0) && (memcmp(pt, TV_PT, sizeof(TV_PT)) == 0);
|
||||||
|
if (pt_ok) printf(" [PASS]\n\n");
|
||||||
|
else { printf(" [FAIL] ret=%d\n\n", ret); fail++; }
|
||||||
|
se050_chacha20_poly1305_zeroize(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 3: tampered tag is rejected */
|
||||||
|
{
|
||||||
|
printf("Test 3: tampered tag rejected\n");
|
||||||
|
uint8_t bad_tag[16];
|
||||||
|
memcpy(bad_tag, TV_TAG, 16);
|
||||||
|
bad_tag[0] ^= 0xff;
|
||||||
|
uint8_t pt[114];
|
||||||
|
se050_chacha20_poly1305_ctx_t ctx;
|
||||||
|
se050_chacha20_poly1305_init(&ctx, TV_KEY);
|
||||||
|
int ret = se050_chacha20_poly1305_decrypt(&ctx, TV_NONCE,
|
||||||
|
TV_CT, sizeof(TV_CT),
|
||||||
|
TV_AAD, sizeof(TV_AAD),
|
||||||
|
bad_tag, pt);
|
||||||
|
if (ret != 0) printf(" [PASS]\n\n");
|
||||||
|
else { printf(" [FAIL] should have rejected\n\n"); fail++; }
|
||||||
|
se050_chacha20_poly1305_zeroize(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 4: encrypt→decrypt round-trip, no AAD */
|
||||||
|
{
|
||||||
|
printf("Test 4: round-trip (no AAD)\n");
|
||||||
|
uint8_t key[32] = {0};
|
||||||
|
uint8_t nonce[12] = {0};
|
||||||
|
uint8_t msg[37], ct[37], pt[37], tag[16];
|
||||||
|
for (int i = 0; i < 32; i++) key[i] = (uint8_t)(i + 1);
|
||||||
|
for (int i = 0; i < 37; i++) msg[i] = (uint8_t)(i ^ 0xaa);
|
||||||
|
|
||||||
|
se050_chacha20_poly1305_ctx_t ctx;
|
||||||
|
se050_chacha20_poly1305_init(&ctx, key);
|
||||||
|
se050_chacha20_poly1305_encrypt(&ctx, nonce, msg, 37, NULL, 0, ct, tag);
|
||||||
|
int ret = se050_chacha20_poly1305_decrypt(&ctx, nonce, ct, 37, NULL, 0, tag, pt);
|
||||||
|
if (ret == 0 && memcmp(msg, pt, 37) == 0) printf(" [PASS]\n\n");
|
||||||
|
else { printf(" [FAIL]\n\n"); fail++; }
|
||||||
|
se050_chacha20_poly1305_zeroize(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("============================\n");
|
||||||
|
printf("Result: %s\n", fail ? "FAIL" : "ALL PASS");
|
||||||
|
return fail ? 1 : 0;
|
||||||
|
}
|
||||||
|
#endif /* CHACHA20_POLY1305_TEST */
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_hkdf_blake2s.c
|
||||||
|
* @brief HKDF Implementation using HMAC-BLAKE2s (RFC 5861)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "se050_hkdf_blake2s.h"
|
||||||
|
#include "se050_hmac_blake2s.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define HMAC_BLAKE2S_DIGEST_SIZE 32
|
||||||
|
|
||||||
|
int se050_hkdf_extract(uint8_t prk[32],
|
||||||
|
const uint8_t *salt, size_t saltlen,
|
||||||
|
const uint8_t *ikm, size_t ikmlen)
|
||||||
|
{
|
||||||
|
uint8_t default_salt[HMAC_BLAKE2S_DIGEST_SIZE];
|
||||||
|
|
||||||
|
if (!prk || !ikm) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!salt || saltlen == 0) {
|
||||||
|
memset(default_salt, 0, HMAC_BLAKE2S_DIGEST_SIZE);
|
||||||
|
salt = default_salt;
|
||||||
|
saltlen = HMAC_BLAKE2S_DIGEST_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return se050_hmac_blake2s(prk, salt, saltlen, ikm, ikmlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_hkdf_expand(uint8_t *okm, size_t okmlen,
|
||||||
|
const uint8_t prk[32],
|
||||||
|
const uint8_t *info, size_t infolen)
|
||||||
|
{
|
||||||
|
uint8_t t[HMAC_BLAKE2S_DIGEST_SIZE];
|
||||||
|
size_t n;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!okm || !prk || okmlen == 0 || okmlen > HKDF_BLAKE2S_MAX_OUTPUT) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = (okmlen + HMAC_BLAKE2S_DIGEST_SIZE - 1) / HMAC_BLAKE2S_DIGEST_SIZE;
|
||||||
|
|
||||||
|
memset(t, 0, sizeof(t));
|
||||||
|
|
||||||
|
for (size_t i = 1; i <= n; i++) {
|
||||||
|
size_t data_len = (i == 1) ? 0 : HMAC_BLAKE2S_DIGEST_SIZE;
|
||||||
|
|
||||||
|
ret = se050_hmac_blake2s(t, prk, HMAC_BLAKE2S_DIGEST_SIZE,
|
||||||
|
t, data_len);
|
||||||
|
if (ret != 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info && infolen > 0) {
|
||||||
|
ret = se050_hmac_blake2s_variable(t, HMAC_BLAKE2S_DIGEST_SIZE,
|
||||||
|
prk, HMAC_BLAKE2S_DIGEST_SIZE,
|
||||||
|
info, infolen);
|
||||||
|
if (ret != 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t block_len = (i < n) ? HMAC_BLAKE2S_DIGEST_SIZE :
|
||||||
|
(okmlen - (i - 1) * HMAC_BLAKE2S_DIGEST_SIZE);
|
||||||
|
memcpy(okm + (i - 1) * HMAC_BLAKE2S_DIGEST_SIZE, t, block_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(t, 0, sizeof(t));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_hkdf(uint8_t *okm, size_t okmlen,
|
||||||
|
const uint8_t *salt, size_t saltlen,
|
||||||
|
const uint8_t *ikm, size_t ikmlen,
|
||||||
|
const uint8_t *info, size_t infolen)
|
||||||
|
{
|
||||||
|
uint8_t prk[HMAC_BLAKE2S_DIGEST_SIZE];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = se050_hkdf_extract(prk, salt, saltlen, ikm, ikmlen);
|
||||||
|
if (ret != 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = se050_hkdf_expand(okm, okmlen, prk, info, infolen);
|
||||||
|
memset(prk, 0, sizeof(prk));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_hmac_blake2s.c
|
||||||
|
* @brief HMAC-BLAKE2s Implementation (RFC 2104)
|
||||||
|
* Based on BLAKE2s hash function
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "se050_hmac_blake2s.h"
|
||||||
|
#include "se050_blake2s.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int se050_hmac_blake2s(uint8_t out[32],
|
||||||
|
const uint8_t *key, size_t keylen,
|
||||||
|
const uint8_t *data, size_t datalen)
|
||||||
|
{
|
||||||
|
uint8_t k_block[HMAC_BLAKE2S_BLOCK_SIZE];
|
||||||
|
uint8_t ipad[HMAC_BLAKE2S_BLOCK_SIZE];
|
||||||
|
uint8_t opad[HMAC_BLAKE2S_BLOCK_SIZE];
|
||||||
|
uint8_t inner_hash[HMAC_BLAKE2S_DIGEST_SIZE];
|
||||||
|
uint8_t inner_with_key[128];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!out || !key || keylen == 0 || !data) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(k_block, 0, sizeof(k_block));
|
||||||
|
|
||||||
|
if (keylen > HMAC_BLAKE2S_BLOCK_SIZE) {
|
||||||
|
se050_blake2s(k_block, HMAC_BLAKE2S_DIGEST_SIZE, key, keylen);
|
||||||
|
} else {
|
||||||
|
memcpy(k_block, key, keylen);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < HMAC_BLAKE2S_BLOCK_SIZE; i++) {
|
||||||
|
ipad[i] = k_block[i] ^ 0x36;
|
||||||
|
opad[i] = k_block[i] ^ 0x5c;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(inner_with_key, ipad, HMAC_BLAKE2S_BLOCK_SIZE);
|
||||||
|
memcpy(inner_with_key + HMAC_BLAKE2S_BLOCK_SIZE, data, datalen);
|
||||||
|
se050_blake2s(inner_hash, HMAC_BLAKE2S_DIGEST_SIZE,
|
||||||
|
inner_with_key, HMAC_BLAKE2S_BLOCK_SIZE + datalen);
|
||||||
|
|
||||||
|
memcpy(inner_with_key, opad, HMAC_BLAKE2S_BLOCK_SIZE);
|
||||||
|
memcpy(inner_with_key + HMAC_BLAKE2S_BLOCK_SIZE, inner_hash, HMAC_BLAKE2S_DIGEST_SIZE);
|
||||||
|
se050_blake2s(out, HMAC_BLAKE2S_DIGEST_SIZE,
|
||||||
|
inner_with_key, HMAC_BLAKE2S_BLOCK_SIZE + HMAC_BLAKE2S_DIGEST_SIZE);
|
||||||
|
|
||||||
|
memset(k_block, 0, sizeof(k_block));
|
||||||
|
memset(ipad, 0, sizeof(ipad));
|
||||||
|
memset(opad, 0, sizeof(opad));
|
||||||
|
memset(inner_hash, 0, sizeof(inner_hash));
|
||||||
|
memset(inner_with_key, 0, sizeof(inner_with_key));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_hmac_blake2s_variable(uint8_t *out, size_t outlen,
|
||||||
|
const uint8_t *key, size_t keylen,
|
||||||
|
const uint8_t *data, size_t datalen)
|
||||||
|
{
|
||||||
|
uint8_t full_digest[HMAC_BLAKE2S_DIGEST_SIZE];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!out || outlen == 0 || outlen > HMAC_BLAKE2S_DIGEST_SIZE) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = se050_hmac_blake2s(full_digest, key, keylen, data, datalen);
|
||||||
|
if (ret != 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(out, full_digest, outlen);
|
||||||
|
memset(full_digest, 0, sizeof(full_digest));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+8
-9
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#include "se050_i2c_hal.h"
|
||||||
#include "se050_wireguard.h"
|
#include "se050_wireguard.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -29,8 +30,8 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* SE050 default I2C address */
|
/* SE050 default I2C address (7-bit) */
|
||||||
#define SE050_I2C_ADDR_DEFAULT 0x90
|
#define SE050_I2C_ADDR_DEFAULT 0x48
|
||||||
|
|
||||||
/* I2C timeout in milliseconds */
|
/* I2C timeout in milliseconds */
|
||||||
#define SE050_I2C_TIMEOUT_MS 1000
|
#define SE050_I2C_TIMEOUT_MS 1000
|
||||||
@@ -97,7 +98,8 @@ static se050_status_t i2c_set_slave_addr(void *handle, uint8_t addr)
|
|||||||
int fd = (int)(intptr_t)handle;
|
int fd = (int)(intptr_t)handle;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = ioctl(fd, I2C_SLAVE, addr >> 1);
|
/* addr is already 7-bit address */
|
||||||
|
ret = ioctl(fd, I2C_SLAVE, addr);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
perror("I2C set slave address failed");
|
perror("I2C set slave address failed");
|
||||||
return SE050_ERR_I2C;
|
return SE050_ERR_I2C;
|
||||||
@@ -134,7 +136,8 @@ se050_status_t se050_i2c_init(se050_i2c_hal_t *hal, const char *dev_path, uint8_
|
|||||||
|
|
||||||
hal->handle = handle;
|
hal->handle = handle;
|
||||||
hal->slave_addr = slave_addr;
|
hal->slave_addr = slave_addr;
|
||||||
hal->dev_path = strdup(dev_path);
|
strncpy(hal->dev_path, dev_path, SE050_I2C_DEV_PATH_MAX - 1);
|
||||||
|
hal->dev_path[SE050_I2C_DEV_PATH_MAX - 1] = '\0';
|
||||||
|
|
||||||
return SE050_OK;
|
return SE050_OK;
|
||||||
}
|
}
|
||||||
@@ -150,11 +153,7 @@ void se050_i2c_close(se050_i2c_hal_t *hal)
|
|||||||
hal->handle = NULL;
|
hal->handle = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hal->dev_path) {
|
hal->dev_path[0] = '\0';
|
||||||
free((void *)hal->dev_path);
|
|
||||||
hal->dev_path = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
hal->slave_addr = 0;
|
hal->slave_addr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-19
@@ -7,12 +7,18 @@
|
|||||||
* License: MIT (Clean-room implementation)
|
* License: MIT (Clean-room implementation)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE /* For MADV_DONTDUMP, MADV_WIPEONFORK */
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
#include "se050_i2c_hal.h"
|
||||||
|
#include "se050_session_internal.h"
|
||||||
|
#include "se050_mem_pool.h"
|
||||||
#include "se050_wireguard.h"
|
#include "se050_wireguard.h"
|
||||||
#include "se050_crypto_utils.h"
|
#include "se050_crypto_utils.h"
|
||||||
#include "se050_keystore_internal.h"
|
#include "se050_keystore_internal.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* Key Store Management
|
* Key Store Management
|
||||||
@@ -26,23 +32,19 @@ se050_status_t se050_keystore_init(se050_keystore_ctx_t **ctx, se050_session_ctx
|
|||||||
return SE050_ERR_INVALID_ARG;
|
return SE050_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate key store context */
|
/* Allocate key store context from static pool */
|
||||||
keystore = (se050_keystore_ctx_t *)calloc(1, sizeof(*keystore));
|
keystore = se050_keystore_alloc_pool();
|
||||||
if (!keystore) {
|
if (!keystore) {
|
||||||
return SE050_ERR_FAIL;
|
return SE050_ERR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
keystore->session = session;
|
keystore->session = session;
|
||||||
keystore->max_objects = 16; /* Maximum 16 keys */
|
keystore->max_objects = SE050_POOL_KEYSTORE_MAX_OBJECTS;
|
||||||
keystore->objects = (key_object_t *)calloc(keystore->max_objects, sizeof(key_object_t));
|
|
||||||
|
|
||||||
if (!keystore->objects) {
|
|
||||||
free(keystore);
|
|
||||||
return SE050_ERR_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
keystore->num_objects = 0;
|
keystore->num_objects = 0;
|
||||||
|
|
||||||
|
/* Zeroize key objects array */
|
||||||
|
memset(keystore->objects, 0, keystore->max_objects * sizeof(key_object_t));
|
||||||
|
|
||||||
*ctx = keystore;
|
*ctx = keystore;
|
||||||
return SE050_OK;
|
return SE050_OK;
|
||||||
}
|
}
|
||||||
@@ -65,17 +67,11 @@ void se050_keystore_free(se050_keystore_ctx_t *ctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free key objects array */
|
|
||||||
if (ctx->objects) {
|
|
||||||
free(ctx->objects);
|
|
||||||
ctx->objects = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->num_objects = 0;
|
ctx->num_objects = 0;
|
||||||
ctx->max_objects = 0;
|
ctx->max_objects = 0;
|
||||||
|
|
||||||
/* Free key store context */
|
/* Free key store context to static pool */
|
||||||
free(ctx);
|
se050_keystore_free_pool(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
|
|||||||
@@ -0,0 +1,304 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_mem_pool.c
|
||||||
|
* @brief Static Memory Pool Implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include "se050_mem_pool.h"
|
||||||
|
#include "se050_session_internal.h"
|
||||||
|
#include "se050_scp03.h"
|
||||||
|
#include "se050_keystore_internal.h"
|
||||||
|
#include "se050_i2c_hal.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
|
/* Forward declarations for structures */
|
||||||
|
/* se050_rng_ctx_t is defined in se050_rng.c, use opaque pointer here */
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Pool Structures
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *pool;
|
||||||
|
size_t item_size;
|
||||||
|
size_t count;
|
||||||
|
atomic_int used_count;
|
||||||
|
uint8_t *used_bitmap; /* 1 bit per item */
|
||||||
|
} mem_pool_t;
|
||||||
|
|
||||||
|
/* Session pool */
|
||||||
|
static mem_pool_t g_session_pool;
|
||||||
|
static uint8_t g_session_storage[SE050_POOL_SESSION_COUNT * sizeof(struct se050_session_ctx)];
|
||||||
|
static uint8_t g_session_bitmap[SE050_POOL_SESSION_COUNT / 8 + 1];
|
||||||
|
|
||||||
|
/* SCP03 pool */
|
||||||
|
static mem_pool_t g_scp03_pool;
|
||||||
|
static uint8_t g_scp03_storage[SE050_POOL_SCP03_COUNT * sizeof(struct se050_scp03_ctx)];
|
||||||
|
static uint8_t g_scp03_bitmap[SE050_POOL_SCP03_COUNT / 8 + 1];
|
||||||
|
|
||||||
|
/* Keystore pool */
|
||||||
|
static mem_pool_t g_keystore_pool;
|
||||||
|
static uint8_t g_keystore_storage[SE050_POOL_KEYSTORE_COUNT * sizeof(struct se050_keystore_ctx)];
|
||||||
|
static uint8_t g_keystore_bitmap[SE050_POOL_KEYSTORE_COUNT / 8 + 1];
|
||||||
|
|
||||||
|
/* RNG pool */
|
||||||
|
static mem_pool_t g_rng_pool;
|
||||||
|
#define SE050_RNG_CTX_SIZE (sizeof(void*) + sizeof(uint32_t) + 64 + sizeof(size_t))
|
||||||
|
static uint8_t g_rng_storage[SE050_POOL_RNG_COUNT * SE050_RNG_CTX_SIZE];
|
||||||
|
static uint8_t g_rng_bitmap[SE050_POOL_RNG_COUNT / 8 + 1];
|
||||||
|
|
||||||
|
/* I2C HAL pool */
|
||||||
|
static mem_pool_t g_i2c_hal_pool;
|
||||||
|
static uint8_t g_i2c_hal_storage[SE050_POOL_I2C_HAL_COUNT * sizeof(se050_i2c_hal_t)];
|
||||||
|
static uint8_t g_i2c_hal_bitmap[SE050_POOL_I2C_HAL_COUNT / 8 + 1];
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Helper Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
static void pool_init(mem_pool_t *pool, uint8_t *storage, size_t item_size,
|
||||||
|
size_t count, uint8_t *bitmap)
|
||||||
|
{
|
||||||
|
pool->pool = storage;
|
||||||
|
pool->item_size = item_size;
|
||||||
|
pool->count = count;
|
||||||
|
atomic_store(&pool->used_count, 0);
|
||||||
|
memset(bitmap, 0, (count + 7) / 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pool_alloc(mem_pool_t *pool, uint8_t *bitmap)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < pool->count; i++) {
|
||||||
|
size_t byte_idx = i / 8;
|
||||||
|
size_t bit_idx = i % 8;
|
||||||
|
|
||||||
|
if (!(bitmap[byte_idx] & (1 << bit_idx))) {
|
||||||
|
bitmap[byte_idx] |= (1 << bit_idx);
|
||||||
|
atomic_fetch_add(&pool->used_count, 1);
|
||||||
|
return (int)i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1; /* Pool exhausted */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pool_free(mem_pool_t *pool, uint8_t *bitmap, int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || (size_t)index >= pool->count) return;
|
||||||
|
|
||||||
|
size_t byte_idx = index / 8;
|
||||||
|
size_t bit_idx = index % 8;
|
||||||
|
|
||||||
|
bitmap[byte_idx] &= ~(1 << bit_idx);
|
||||||
|
atomic_fetch_sub(&pool->used_count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pool_zeroize(mem_pool_t *pool, uint8_t *bitmap)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < pool->count; i++) {
|
||||||
|
size_t byte_idx = i / 8;
|
||||||
|
size_t bit_idx = i % 8;
|
||||||
|
|
||||||
|
if (bitmap[byte_idx] & (1 << bit_idx)) {
|
||||||
|
memset(pool->pool + (i * pool->item_size), 0, pool->item_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Public API
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
int se050_mem_pool_init(void)
|
||||||
|
{
|
||||||
|
pool_init(&g_session_pool, g_session_storage, sizeof(struct se050_session_ctx),
|
||||||
|
SE050_POOL_SESSION_COUNT, g_session_bitmap);
|
||||||
|
|
||||||
|
pool_init(&g_scp03_pool, g_scp03_storage, sizeof(struct se050_scp03_ctx),
|
||||||
|
SE050_POOL_SCP03_COUNT, g_scp03_bitmap);
|
||||||
|
|
||||||
|
pool_init(&g_keystore_pool, g_keystore_storage, sizeof(struct se050_keystore_ctx),
|
||||||
|
SE050_POOL_KEYSTORE_COUNT, g_keystore_bitmap);
|
||||||
|
|
||||||
|
pool_init(&g_rng_pool, g_rng_storage, SE050_RNG_CTX_SIZE,
|
||||||
|
SE050_POOL_RNG_COUNT, g_rng_bitmap);
|
||||||
|
|
||||||
|
pool_init(&g_i2c_hal_pool, g_i2c_hal_storage, sizeof(se050_i2c_hal_t),
|
||||||
|
SE050_POOL_I2C_HAL_COUNT, g_i2c_hal_bitmap);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void se050_mem_pool_cleanup(void)
|
||||||
|
{
|
||||||
|
pool_zeroize(&g_session_pool, g_session_bitmap);
|
||||||
|
pool_zeroize(&g_scp03_pool, g_scp03_bitmap);
|
||||||
|
pool_zeroize(&g_keystore_pool, g_keystore_bitmap);
|
||||||
|
pool_zeroize(&g_rng_pool, g_rng_bitmap);
|
||||||
|
pool_zeroize(&g_i2c_hal_pool, g_i2c_hal_bitmap);
|
||||||
|
|
||||||
|
memset(g_session_bitmap, 0, sizeof(g_session_bitmap));
|
||||||
|
memset(g_scp03_bitmap, 0, sizeof(g_scp03_bitmap));
|
||||||
|
memset(g_keystore_bitmap, 0, sizeof(g_keystore_bitmap));
|
||||||
|
memset(g_rng_bitmap, 0, sizeof(g_rng_bitmap));
|
||||||
|
memset(g_i2c_hal_bitmap, 0, sizeof(g_i2c_hal_bitmap));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Session pool */
|
||||||
|
struct se050_session_ctx *se050_session_alloc_pool(void)
|
||||||
|
{
|
||||||
|
int idx = pool_alloc(&g_session_pool, g_session_bitmap);
|
||||||
|
if (idx < 0) return NULL;
|
||||||
|
|
||||||
|
struct se050_session_ctx *ctx = (struct se050_session_ctx *)
|
||||||
|
(g_session_pool.pool + (idx * g_session_pool.item_size));
|
||||||
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void se050_session_free_pool(struct se050_session_ctx *ctx)
|
||||||
|
{
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
/* Find index and zeroize */
|
||||||
|
size_t offset = (uint8_t *)ctx - g_session_pool.pool;
|
||||||
|
if (offset < g_session_pool.count * g_session_pool.item_size) {
|
||||||
|
size_t idx = offset / g_session_pool.item_size;
|
||||||
|
pool_zeroize(&g_session_pool, g_session_bitmap);
|
||||||
|
pool_free(&g_session_pool, g_session_bitmap, (int)idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SCP03 pool */
|
||||||
|
struct se050_scp03_ctx *se050_scp03_alloc_pool(void)
|
||||||
|
{
|
||||||
|
int idx = pool_alloc(&g_scp03_pool, g_scp03_bitmap);
|
||||||
|
if (idx < 0) return NULL;
|
||||||
|
|
||||||
|
struct se050_scp03_ctx *ctx = (struct se050_scp03_ctx *)
|
||||||
|
(g_scp03_pool.pool + (idx * g_scp03_pool.item_size));
|
||||||
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void se050_scp03_free_pool(struct se050_scp03_ctx *ctx)
|
||||||
|
{
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
size_t offset = (uint8_t *)ctx - g_scp03_pool.pool;
|
||||||
|
if (offset < g_scp03_pool.count * g_scp03_pool.item_size) {
|
||||||
|
size_t idx = offset / g_scp03_pool.item_size;
|
||||||
|
pool_zeroize(&g_scp03_pool, g_scp03_bitmap);
|
||||||
|
pool_free(&g_scp03_pool, g_scp03_bitmap, (int)idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keystore pool */
|
||||||
|
struct se050_keystore_ctx *se050_keystore_alloc_pool(void)
|
||||||
|
{
|
||||||
|
int idx = pool_alloc(&g_keystore_pool, g_keystore_bitmap);
|
||||||
|
if (idx < 0) return NULL;
|
||||||
|
|
||||||
|
struct se050_keystore_ctx *ctx = (struct se050_keystore_ctx *)
|
||||||
|
(g_keystore_pool.pool + (idx * g_keystore_pool.item_size));
|
||||||
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void se050_keystore_free_pool(struct se050_keystore_ctx *ctx)
|
||||||
|
{
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
size_t offset = (uint8_t *)ctx - g_keystore_pool.pool;
|
||||||
|
if (offset < g_keystore_pool.count * g_keystore_pool.item_size) {
|
||||||
|
size_t idx = offset / g_keystore_pool.item_size;
|
||||||
|
pool_zeroize(&g_keystore_pool, g_keystore_bitmap);
|
||||||
|
pool_free(&g_keystore_pool, g_keystore_bitmap, (int)idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RNG pool */
|
||||||
|
struct se050_rng_ctx *se050_rng_alloc_pool(void)
|
||||||
|
{
|
||||||
|
int idx = pool_alloc(&g_rng_pool, g_rng_bitmap);
|
||||||
|
if (idx < 0) return NULL;
|
||||||
|
|
||||||
|
struct se050_rng_ctx *ctx = (struct se050_rng_ctx *)
|
||||||
|
(g_rng_pool.pool + (idx * g_rng_pool.item_size));
|
||||||
|
memset(ctx, 0, SE050_RNG_CTX_SIZE);
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void se050_rng_free_pool(struct se050_rng_ctx *ctx)
|
||||||
|
{
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
size_t offset = (uint8_t *)ctx - g_rng_pool.pool;
|
||||||
|
if (offset < g_rng_pool.count * g_rng_pool.item_size) {
|
||||||
|
size_t idx = offset / g_rng_pool.item_size;
|
||||||
|
pool_zeroize(&g_rng_pool, g_rng_bitmap);
|
||||||
|
pool_free(&g_rng_pool, g_rng_bitmap, (int)idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* I2C HAL pool */
|
||||||
|
struct se050_i2c_hal *se050_i2c_hal_alloc_pool(void)
|
||||||
|
{
|
||||||
|
int idx = pool_alloc(&g_i2c_hal_pool, g_i2c_hal_bitmap);
|
||||||
|
if (idx < 0) return NULL;
|
||||||
|
|
||||||
|
se050_i2c_hal_t *hal = (se050_i2c_hal_t *)
|
||||||
|
(g_i2c_hal_pool.pool + (idx * g_i2c_hal_pool.item_size));
|
||||||
|
memset(hal, 0, sizeof(*hal));
|
||||||
|
return (struct se050_i2c_hal *)hal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void se050_i2c_hal_free_pool(struct se050_i2c_hal *hal)
|
||||||
|
{
|
||||||
|
if (!hal) return;
|
||||||
|
|
||||||
|
size_t offset = (uint8_t *)hal - g_i2c_hal_pool.pool;
|
||||||
|
if (offset < g_i2c_hal_pool.count * g_i2c_hal_pool.item_size) {
|
||||||
|
size_t idx = offset / g_i2c_hal_pool.item_size;
|
||||||
|
pool_zeroize(&g_i2c_hal_pool, g_i2c_hal_bitmap);
|
||||||
|
pool_free(&g_i2c_hal_pool, g_i2c_hal_bitmap, (int)idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Statistics */
|
||||||
|
void se050_mem_pool_stats(se050_pool_stats_t *session,
|
||||||
|
se050_pool_stats_t *scp03,
|
||||||
|
se050_pool_stats_t *keystore,
|
||||||
|
se050_pool_stats_t *rng,
|
||||||
|
se050_pool_stats_t *i2c_hal)
|
||||||
|
{
|
||||||
|
if (session) {
|
||||||
|
session->total = SE050_POOL_SESSION_COUNT;
|
||||||
|
session->used = atomic_load(&g_session_pool.used_count);
|
||||||
|
session->free = session->total - session->used;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scp03) {
|
||||||
|
scp03->total = SE050_POOL_SCP03_COUNT;
|
||||||
|
scp03->used = atomic_load(&g_scp03_pool.used_count);
|
||||||
|
scp03->free = scp03->total - scp03->used;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keystore) {
|
||||||
|
keystore->total = SE050_POOL_KEYSTORE_COUNT;
|
||||||
|
keystore->used = atomic_load(&g_keystore_pool.used_count);
|
||||||
|
keystore->free = keystore->total - keystore->used;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rng) {
|
||||||
|
rng->total = SE050_POOL_RNG_COUNT;
|
||||||
|
rng->used = atomic_load(&g_rng_pool.used_count);
|
||||||
|
rng->free = rng->total - rng->used;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2c_hal) {
|
||||||
|
i2c_hal->total = SE050_POOL_I2C_HAL_COUNT;
|
||||||
|
i2c_hal->used = atomic_load(&g_i2c_hal_pool.used_count);
|
||||||
|
i2c_hal->free = i2c_hal->total - i2c_hal->used;
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-5
@@ -8,11 +8,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#include "se050_i2c_hal.h"
|
||||||
|
#include "se050_mem_pool.h"
|
||||||
#include "se050_wireguard.h"
|
#include "se050_wireguard.h"
|
||||||
#include "se050_crypto_utils.h"
|
#include "se050_crypto_utils.h"
|
||||||
#include "se050_session_internal.h"
|
#include "se050_session_internal.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@@ -40,8 +41,8 @@ se050_status_t se050_rng_init(se050_rng_ctx_t **ctx, se050_session_ctx_t *sessio
|
|||||||
return SE050_ERR_INVALID_ARG;
|
return SE050_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate RNG context */
|
/* Allocate RNG context from static pool */
|
||||||
rng = (se050_rng_ctx_t *)calloc(1, sizeof(*rng));
|
rng = se050_rng_alloc_pool();
|
||||||
if (!rng) {
|
if (!rng) {
|
||||||
return SE050_ERR_FAIL;
|
return SE050_ERR_FAIL;
|
||||||
}
|
}
|
||||||
@@ -69,8 +70,8 @@ void se050_rng_free(se050_rng_ctx_t *ctx)
|
|||||||
ctx->entropy_len = 0;
|
ctx->entropy_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free RNG context */
|
/* Free RNG context to static pool */
|
||||||
free(ctx);
|
se050_rng_free_pool(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
+18
-22
@@ -7,10 +7,17 @@
|
|||||||
* License: MIT (Clean-room implementation)
|
* License: MIT (Clean-room implementation)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE /* For MADV_DONTDUMP, MADV_WIPEONFORK */
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
#include "se050_i2c_hal.h"
|
||||||
|
#include "se050_session_internal.h"
|
||||||
|
#include "se050_scp03.h"
|
||||||
|
#include "se050_mem_pool.h"
|
||||||
#include "se050_wireguard.h"
|
#include "se050_wireguard.h"
|
||||||
#include "se050_crypto_utils.h"
|
#include "se050_crypto_utils.h"
|
||||||
|
#include "se050_mem_protect.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/* SCP03 constants */
|
/* SCP03 constants */
|
||||||
@@ -24,21 +31,6 @@
|
|||||||
#define SCP03_SW_SUCCESS 0x9000
|
#define SCP03_SW_SUCCESS 0x9000
|
||||||
#define SCP03_SW_FAIL 0x6F00
|
#define SCP03_SW_FAIL 0x6F00
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief SCP03 session context structure
|
|
||||||
*/
|
|
||||||
struct se050_scp03_ctx {
|
|
||||||
se050_session_ctx_t *session; /**< Associated session */
|
|
||||||
uint8_t enc_key[SCP03_KEY_SIZE]; /**< Encryption key */
|
|
||||||
uint8_t mac_key[SCP03_KEY_SIZE]; /**< MAC key */
|
|
||||||
uint8_t dek_key[SCP03_KEY_SIZE]; /**< DEK key (for key derivation) */
|
|
||||||
uint8_t cmd_icv[SCP03_CMAC_SIZE]; /**< Command ICV */
|
|
||||||
uint8_t rsp_icv[SCP03_CMAC_SIZE]; /**< Response ICV */
|
|
||||||
uint64_t cmd_counter; /**< Command counter */
|
|
||||||
uint64_t rsp_counter; /**< Response counter */
|
|
||||||
uint8_t initialized; /**< Initialization flag */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* Helper Functions
|
* Helper Functions
|
||||||
* ============================================================================ */
|
* ============================================================================ */
|
||||||
@@ -53,6 +45,9 @@ static int scp03_calc_cmac(const uint8_t *key, size_t key_len,
|
|||||||
const uint8_t *data, size_t data_len,
|
const uint8_t *data, size_t data_len,
|
||||||
uint8_t *mac, size_t *mac_len)
|
uint8_t *mac, size_t *mac_len)
|
||||||
{
|
{
|
||||||
|
(void)key; (void)key_len; (void)data; (void)data_len; (void)mac; (void)mac_len;
|
||||||
|
/* Placeholder: In production, use OpenSSL AES-CMAC */
|
||||||
|
return 0;
|
||||||
#if defined(__linux__) && defined(OPENSSL_AVAILABLE)
|
#if defined(__linux__) && defined(OPENSSL_AVAILABLE)
|
||||||
/* Use OpenSSL for AES-CMAC */
|
/* Use OpenSSL for AES-CMAC */
|
||||||
#include <openssl/cmac.h>
|
#include <openssl/cmac.h>
|
||||||
@@ -146,7 +141,7 @@ static int scp03_aes_cbc_encrypt(const uint8_t *key, size_t key_len,
|
|||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
/* Fallback: Simple XOR-based encryption (NOT SECURE - for testing only) */
|
/* Fallback: Simple XOR-based encryption (NOT SECURE - for testing only) */
|
||||||
size_t i, j;
|
size_t i;
|
||||||
size_t block_size = 16;
|
size_t block_size = 16;
|
||||||
|
|
||||||
/* Pad to block size */
|
/* Pad to block size */
|
||||||
@@ -211,7 +206,7 @@ static int scp03_aes_cbc_decrypt(const uint8_t *key, size_t key_len,
|
|||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
/* Fallback: Simple XOR-based decryption (NOT SECURE - for testing only) */
|
/* Fallback: Simple XOR-based decryption (NOT SECURE - for testing only) */
|
||||||
size_t i, j;
|
size_t i;
|
||||||
|
|
||||||
/* XOR with key and IV */
|
/* XOR with key and IV */
|
||||||
for (i = 0; i < ciphertext_len; i++) {
|
for (i = 0; i < ciphertext_len; i++) {
|
||||||
@@ -299,8 +294,8 @@ se050_status_t se050_scp03_init(se050_scp03_ctx_t **ctx, se050_session_ctx_t *se
|
|||||||
return SE050_ERR_INVALID_ARG;
|
return SE050_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate SCP03 context */
|
/* Allocate SCP03 context from static pool */
|
||||||
scp03 = (se050_scp03_ctx_t *)calloc(1, sizeof(*scp03));
|
scp03 = se050_scp03_alloc_pool();
|
||||||
if (!scp03) {
|
if (!scp03) {
|
||||||
return SE050_ERR_FAIL;
|
return SE050_ERR_FAIL;
|
||||||
}
|
}
|
||||||
@@ -336,8 +331,8 @@ void se050_scp03_free(se050_scp03_ctx_t *ctx)
|
|||||||
memzero_explicit(ctx->rsp_icv, sizeof(ctx->rsp_icv));
|
memzero_explicit(ctx->rsp_icv, sizeof(ctx->rsp_icv));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free SCP03 context */
|
/* Free SCP03 context to static pool */
|
||||||
free(ctx);
|
se050_scp03_free_pool(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
se050_status_t se050_scp03_set_keys(se050_scp03_ctx_t *ctx,
|
se050_status_t se050_scp03_set_keys(se050_scp03_ctx_t *ctx,
|
||||||
@@ -484,6 +479,7 @@ uint16_t se050_scp03_decrypt_response(se050_scp03_ctx_t *ctx,
|
|||||||
uint8_t *rsp,
|
uint8_t *rsp,
|
||||||
size_t *rsp_len)
|
size_t *rsp_len)
|
||||||
{
|
{
|
||||||
|
(void)cmd_len; /* Used in production for MAC verification */
|
||||||
uint8_t iv[SCP03_IV_SIZE];
|
uint8_t iv[SCP03_IV_SIZE];
|
||||||
uint8_t mac[SCP03_CMAC_SIZE];
|
uint8_t mac[SCP03_CMAC_SIZE];
|
||||||
size_t mac_len = SCP03_CMAC_SIZE;
|
size_t mac_len = SCP03_CMAC_SIZE;
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_scp03_keys.c
|
||||||
|
* @brief SE050 Platform SCP03 Keys Implementation
|
||||||
|
*
|
||||||
|
* Platform SCP03 keys for each SE050 chip type (SE050C0, SE050C1, SE050E2).
|
||||||
|
*
|
||||||
|
* IMPORTANT: These are placeholder values for testing only.
|
||||||
|
* Replace with actual keys from:
|
||||||
|
* - NXP AN12436 application note (PlatformSCP03 default keys)
|
||||||
|
* - NXP AN12413 application note (SCP03 security configuration)
|
||||||
|
* - Secure provisioning process for production
|
||||||
|
* - Your organization's key management system
|
||||||
|
*
|
||||||
|
* For production use: Each chip should have unique, securely provisioned keys.
|
||||||
|
* For testing: Use the default keys from AN12436 or your chip documentation.
|
||||||
|
*
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "se050_scp03_keys.h"
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* SE050C0 Platform SCP03 Keys
|
||||||
|
*
|
||||||
|
* Note: Official NXP source (ex_sss_tp_scp03_keys.h) does not list SE050C0.
|
||||||
|
* Using SE050_DEVKIT (OEF ID: 0xA1F4) values as reference.
|
||||||
|
* For production: Use actual keys from your chip documentation or secure provisioning.
|
||||||
|
*
|
||||||
|
* Source: NXP plug-and-trust repository
|
||||||
|
* File: sss/ex/inc/ex_sss_tp_scp03_keys.h
|
||||||
|
* Reference: SSS_PFSCP_ENABLE_SE050_DEVKIT (OEF ID: 0xA1F4)
|
||||||
|
* See: https://github.com/NXP/plug-and-trust/blob/master/sss/ex/inc/ex_sss_tp_scp03_keys.h
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
const uint8_t SE050C0_ENC_KEY[16] = {
|
||||||
|
0x35, 0xC2, 0x56, 0x45, 0x89, 0x58, 0xA3, 0x4F,
|
||||||
|
0x61, 0x36, 0x15, 0x5F, 0x82, 0x09, 0xD6, 0xCD
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t SE050C0_MAC_KEY[16] = {
|
||||||
|
0xAF, 0x17, 0x7D, 0x5D, 0xBD, 0xF7, 0xC0, 0xD5,
|
||||||
|
0xC1, 0x0A, 0x05, 0xB9, 0xF1, 0x60, 0x7F, 0x78
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t SE050C0_DEK_KEY[16] = {
|
||||||
|
0xA1, 0xBC, 0x84, 0x38, 0xBF, 0x77, 0x93, 0x5B,
|
||||||
|
0x36, 0x1A, 0x44, 0x25, 0xFE, 0x79, 0xFA, 0x29
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* SE050C1 Platform SCP03 Keys
|
||||||
|
*
|
||||||
|
* Source: NXP plug-and-trust repository
|
||||||
|
* File: sss/ex/inc/ex_sss_tp_scp03_keys.h
|
||||||
|
* Reference: SSS_PFSCP_ENABLE_SE050C1 (OEF ID: 0xA200)
|
||||||
|
* See: https://github.com/NXP/plug-and-trust/blob/master/sss/ex/inc/ex_sss_tp_scp03_keys.h
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
const uint8_t SE050C1_ENC_KEY[16] = {
|
||||||
|
0x85, 0x2B, 0x59, 0x62, 0xE9, 0xCC, 0xE5, 0xD0,
|
||||||
|
0xBE, 0x74, 0x6B, 0x83, 0x3B, 0xCC, 0x62, 0x87
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t SE050C1_MAC_KEY[16] = {
|
||||||
|
0xDB, 0x0A, 0xA3, 0x19, 0xA4, 0x08, 0x69, 0x6C,
|
||||||
|
0x8E, 0x10, 0x7A, 0xB4, 0xE3, 0xC2, 0x6B, 0x47
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t SE050C1_DEK_KEY[16] = {
|
||||||
|
0x4C, 0x2F, 0x75, 0xC6, 0xA2, 0x78, 0xA4, 0xAE,
|
||||||
|
0xE5, 0xC9, 0xAF, 0x7C, 0x50, 0xEE, 0xA8, 0x0C
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* SE050E2 Platform SCP03 Keys
|
||||||
|
*
|
||||||
|
* Source: NXP plug-and-trust repository
|
||||||
|
* File: sss/ex/inc/ex_sss_tp_scp03_keys.h
|
||||||
|
* Reference: SSS_PFSCP_ENABLE_SE050E_0001A921 (OEF ID: 0xA921)
|
||||||
|
* See: https://github.com/NXP/plug-and-trust/blob/master/sss/ex/inc/ex_sss_tp_scp03_keys.h
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
const uint8_t SE050E2_ENC_KEY[16] = {
|
||||||
|
0xD2, 0xDB, 0x63, 0xE7, 0xA0, 0xA5, 0xAE, 0xD7,
|
||||||
|
0x2A, 0x64, 0x60, 0xC4, 0xDF, 0xDC, 0xAF, 0x64
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t SE050E2_MAC_KEY[16] = {
|
||||||
|
0x73, 0x8D, 0x5B, 0x79, 0x8E, 0xD2, 0x41, 0xB0,
|
||||||
|
0xB2, 0x47, 0x68, 0x51, 0x4B, 0xFB, 0xA9, 0x5B
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t SE050E2_DEK_KEY[16] = {
|
||||||
|
0x67, 0x02, 0xDA, 0xC3, 0x09, 0x42, 0xB2, 0xC8,
|
||||||
|
0x5E, 0x7F, 0x47, 0xB4, 0x2C, 0xED, 0x4E, 0x7F
|
||||||
|
};
|
||||||
+9
-28
@@ -8,38 +8,19 @@
|
|||||||
* License: MIT (Clean-room implementation)
|
* License: MIT (Clean-room implementation)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "se050_i2c_hal.h"
|
||||||
|
#include "se050_session_internal.h"
|
||||||
|
#include "se050_mem_pool.h"
|
||||||
|
#include "se050_scp03.h"
|
||||||
#include "se050_wireguard.h"
|
#include "se050_wireguard.h"
|
||||||
#include "se050_crypto_utils.h"
|
#include "se050_crypto_utils.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/* SCP03 status codes */
|
/* SCP03 status codes */
|
||||||
#define SCP03_SW_SUCCESS 0x9000
|
#define SCP03_SW_SUCCESS 0x9000
|
||||||
#define SCP03_SW_FAIL 0x6F00
|
#define SCP03_SW_FAIL 0x6F00
|
||||||
|
|
||||||
/* Session states */
|
|
||||||
typedef enum {
|
|
||||||
SESSION_STATE_CREATED = 0,
|
|
||||||
SESSION_STATE_OPENED,
|
|
||||||
SESSION_STATE_CLOSED,
|
|
||||||
} session_state_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Session context structure
|
|
||||||
*
|
|
||||||
* Includes SCP03 secure channel support for PlatformSCP03 authentication.
|
|
||||||
*/
|
|
||||||
struct se050_session_ctx {
|
|
||||||
se050_i2c_hal_t *hal; /**< I2C HAL interface */
|
|
||||||
session_state_t state; /**< Current session state */
|
|
||||||
uint32_t session_id; /**< Unique session identifier */
|
|
||||||
se050_scp03_ctx_t *scp03; /**< SCP03 secure channel context */
|
|
||||||
uint8_t session_key[32]; /**< Session encryption key */
|
|
||||||
size_t session_key_len; /**< Session key length */
|
|
||||||
se050_rng_ctx_t *rng; /**< RNG context */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* Session Management
|
* Session Management
|
||||||
* ============================================================================ */
|
* ============================================================================ */
|
||||||
@@ -53,8 +34,8 @@ se050_status_t se050_session_create(se050_session_ctx_t **ctx, se050_i2c_hal_t *
|
|||||||
return SE050_ERR_INVALID_ARG;
|
return SE050_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate session context */
|
/* Allocate session context from static pool */
|
||||||
session = (se050_session_ctx_t *)calloc(1, sizeof(*session));
|
session = se050_session_alloc_pool();
|
||||||
if (!session) {
|
if (!session) {
|
||||||
return SE050_ERR_FAIL;
|
return SE050_ERR_FAIL;
|
||||||
}
|
}
|
||||||
@@ -165,7 +146,7 @@ void se050_session_delete(se050_session_ctx_t *ctx)
|
|||||||
|
|
||||||
/* Close SCP03 secure channel if initialized */
|
/* Close SCP03 secure channel if initialized */
|
||||||
if (ctx->scp03) {
|
if (ctx->scp03) {
|
||||||
se050_scp03_free(ctx->scp03);
|
se050_scp03_free_pool(ctx->scp03);
|
||||||
ctx->scp03 = NULL;
|
ctx->scp03 = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,8 +156,8 @@ void se050_session_delete(se050_session_ctx_t *ctx)
|
|||||||
ctx->session_key_len = 0;
|
ctx->session_key_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free session context */
|
/* Free session context to static pool */
|
||||||
free(ctx);
|
se050_session_free_pool(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_tai64n.c
|
||||||
|
* @brief TAI64N Timestamp Encoding (WireGuard Protocol)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include "se050_tai64n.h"
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define TAI64N_BASE 0x4000000000000000ULL
|
||||||
|
|
||||||
|
static void store64_be(uint8_t *out, uint64_t val)
|
||||||
|
{
|
||||||
|
out[0] = (uint8_t)(val >> 56);
|
||||||
|
out[1] = (uint8_t)(val >> 48);
|
||||||
|
out[2] = (uint8_t)(val >> 40);
|
||||||
|
out[3] = (uint8_t)(val >> 32);
|
||||||
|
out[4] = (uint8_t)(val >> 24);
|
||||||
|
out[5] = (uint8_t)(val >> 16);
|
||||||
|
out[6] = (uint8_t)(val >> 8);
|
||||||
|
out[7] = (uint8_t)(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t load64_be(const uint8_t *in)
|
||||||
|
{
|
||||||
|
return ((uint64_t)in[0] << 56) |
|
||||||
|
((uint64_t)in[1] << 48) |
|
||||||
|
((uint64_t)in[2] << 40) |
|
||||||
|
((uint64_t)in[3] << 32) |
|
||||||
|
((uint64_t)in[4] << 24) |
|
||||||
|
((uint64_t)in[5] << 16) |
|
||||||
|
((uint64_t)in[6] << 8) |
|
||||||
|
((uint64_t)in[7]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void se050_tai64n_encode(uint8_t out[12], uint64_t seconds, uint32_t nanoseconds)
|
||||||
|
{
|
||||||
|
uint64_t tai64;
|
||||||
|
tai64 = TAI64N_BASE + seconds;
|
||||||
|
store64_be(out, tai64);
|
||||||
|
store64_be(out + 8, nanoseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_tai64n_decode(const uint8_t in[12], uint64_t *seconds, uint32_t *nanoseconds)
|
||||||
|
{
|
||||||
|
uint64_t tai64;
|
||||||
|
uint64_t nano;
|
||||||
|
|
||||||
|
if (!in || !seconds || !nanoseconds) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tai64 = load64_be(in);
|
||||||
|
nano = load64_be(in + 8);
|
||||||
|
|
||||||
|
if (tai64 < TAI64N_BASE) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*seconds = tai64 - TAI64N_BASE;
|
||||||
|
*nanoseconds = (uint32_t)nano;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_tai64n_now(uint8_t out[12])
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
uint64_t seconds;
|
||||||
|
uint32_t nanoseconds;
|
||||||
|
|
||||||
|
if (!out) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
seconds = (uint64_t)ts.tv_sec;
|
||||||
|
nanoseconds = (uint32_t)ts.tv_nsec;
|
||||||
|
|
||||||
|
se050_tai64n_encode(out, seconds, nanoseconds);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_tai64n_check_window(const uint8_t timestamp[12],
|
||||||
|
const uint8_t current[12],
|
||||||
|
uint32_t window)
|
||||||
|
{
|
||||||
|
uint64_t ts_sec, curr_sec;
|
||||||
|
uint32_t ts_nsec, curr_nsec;
|
||||||
|
int64_t diff;
|
||||||
|
|
||||||
|
if (!timestamp || !current) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (se050_tai64n_decode(timestamp, &ts_sec, &ts_nsec) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (se050_tai64n_decode(current, &curr_sec, &curr_nsec) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
diff = (int64_t)(curr_sec - ts_sec);
|
||||||
|
if (diff < 0) diff = -diff;
|
||||||
|
|
||||||
|
return (diff <= (int64_t)window) ? 1 : 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_tai64n_hw.c
|
||||||
|
* @brief TAI64N using SE050 Hardware Monotonic Counter
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "se050_tai64n_hw.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* SE050 API - defined in test file or linked from SE050 library */
|
||||||
|
extern int Se05x_API_ReadCounter(void *session, uint32_t obj_id, uint32_t *counter);
|
||||||
|
extern int Se05x_API_IncrementCounter(void *session, uint32_t obj_id);
|
||||||
|
|
||||||
|
#define TAI64N_BASE_UPPER 0x40000000ULL
|
||||||
|
|
||||||
|
static void store64_be(uint8_t *out, uint64_t val)
|
||||||
|
{
|
||||||
|
out[0] = (uint8_t)(val >> 56);
|
||||||
|
out[1] = (uint8_t)(val >> 48);
|
||||||
|
out[2] = (uint8_t)(val >> 40);
|
||||||
|
out[3] = (uint8_t)(val >> 32);
|
||||||
|
out[4] = (uint8_t)(val >> 24);
|
||||||
|
out[5] = (uint8_t)(val >> 16);
|
||||||
|
out[6] = (uint8_t)(val >> 8);
|
||||||
|
out[7] = (uint8_t)(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t load32_be(const uint8_t *in)
|
||||||
|
{
|
||||||
|
return ((uint32_t)in[0] << 24) |
|
||||||
|
((uint32_t)in[1] << 16) |
|
||||||
|
((uint32_t)in[2] << 8) |
|
||||||
|
((uint32_t)in[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_tai64n_hw_read_counter(void *session, uint32_t *counter)
|
||||||
|
{
|
||||||
|
if (!session || !counter) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SE050 API call to read monotonic counter */
|
||||||
|
/* This uses the actual SE050 hardware counter */
|
||||||
|
uint32_t obj_id = SE050_MONOTONIC_COUNTER_ID;
|
||||||
|
int status = Se05x_API_ReadCounter(session, obj_id, counter);
|
||||||
|
|
||||||
|
if (status != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_tai64n_hw_increment(void *session)
|
||||||
|
{
|
||||||
|
if (!session) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SE050 API call to increment counter */
|
||||||
|
uint32_t obj_id = SE050_MONOTONIC_COUNTER_ID;
|
||||||
|
int status = Se05x_API_IncrementCounter(session, obj_id);
|
||||||
|
|
||||||
|
if (status != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_tai64n_hw_now(void *session, uint8_t out[12])
|
||||||
|
{
|
||||||
|
uint32_t counter;
|
||||||
|
uint64_t tai64_upper;
|
||||||
|
uint32_t unix_sec;
|
||||||
|
|
||||||
|
if (!session || !out) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read SE050 monotonic counter */
|
||||||
|
if (se050_tai64n_hw_read_counter(session, &counter) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use counter as lower 32 bits of nanoseconds */
|
||||||
|
/* Upper 32 bits: TAI base + approximate Unix seconds */
|
||||||
|
unix_sec = counter / 1000000000UL; /* Approximate seconds from counter */
|
||||||
|
tai64_upper = TAI64N_BASE_UPPER + unix_sec;
|
||||||
|
|
||||||
|
/* Encode as TAI64N: 64-bit TAI + 32-bit nanoseconds */
|
||||||
|
store64_be(out, tai64_upper << 32);
|
||||||
|
store64_be(out + 8, counter);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_tai64n_hw_check_window(const uint8_t timestamp[12],
|
||||||
|
const uint8_t current[12],
|
||||||
|
uint32_t window)
|
||||||
|
{
|
||||||
|
uint32_t ts_counter, curr_counter;
|
||||||
|
int32_t diff;
|
||||||
|
|
||||||
|
if (!timestamp || !current) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compare monotonic counter values (lower 32 bits) */
|
||||||
|
ts_counter = load32_be(timestamp + 8);
|
||||||
|
curr_counter = load32_be(current + 8);
|
||||||
|
|
||||||
|
/* Check if within window (counter difference) */
|
||||||
|
diff = (int32_t)(curr_counter - ts_counter);
|
||||||
|
if (diff < 0) diff = -diff;
|
||||||
|
|
||||||
|
/* Window in counter units (assuming ~1 increment per nanosecond) */
|
||||||
|
/* For practical use: window in seconds * 1000000000 */
|
||||||
|
uint32_t window_units = window * 1000000000UL;
|
||||||
|
|
||||||
|
return (diff <= (int32_t)window_units) ? 1 : 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,468 @@
|
|||||||
|
/**
|
||||||
|
* @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 <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* =========================================================================
|
||||||
|
* 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 (RFC 9153) */
|
||||||
|
#define WG_TYPE_HANDSHAKE_INIT 1
|
||||||
|
#define WG_TYPE_HANDSHAKE_RESP 2
|
||||||
|
#define WG_TYPE_COOKIE_REPLY 3
|
||||||
|
#define WG_TYPE_DATA 4
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* =========================================================================
|
||||||
|
* 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 for WireGuard - always uses 32-byte PRK
|
||||||
|
* T(1) = HMAC(PRK, 0x01)
|
||||||
|
* T(2) = HMAC(PRK, T(1) || 0x02)
|
||||||
|
* T(3) = HMAC(PRK, T(2) || 0x03)
|
||||||
|
*/
|
||||||
|
static void wg_hkdf_2(const uint8_t *prk,
|
||||||
|
uint8_t *out1, uint8_t *out2)
|
||||||
|
{
|
||||||
|
/* T(1) = HMAC(PRK, 0x01) */
|
||||||
|
uint8_t c1 = 0x01;
|
||||||
|
se050_hmac_blake2s(out1, prk, 32, &c1, 1);
|
||||||
|
|
||||||
|
/* T(2) = HMAC(PRK, T(1) || 0x02) */
|
||||||
|
uint8_t t2_input[33];
|
||||||
|
memcpy(t2_input, out1, 32);
|
||||||
|
t2_input[32] = 0x02;
|
||||||
|
se050_hmac_blake2s(out2, prk, 32, t2_input, 33);
|
||||||
|
|
||||||
|
memzero_explicit(t2_input, 33);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HKDF-3 (three outputs) - WireGuard style */
|
||||||
|
static void wg_hkdf_3(const uint8_t *prk,
|
||||||
|
uint8_t *out1, uint8_t *out2, uint8_t *out3)
|
||||||
|
{
|
||||||
|
/* T(1) = HMAC(PRK, 0x01) */
|
||||||
|
uint8_t c1 = 0x01;
|
||||||
|
se050_hmac_blake2s(out1, prk, 32, &c1, 1);
|
||||||
|
|
||||||
|
/* T(2) = HMAC(PRK, T(1) || 0x02) */
|
||||||
|
uint8_t t2_input[33];
|
||||||
|
memcpy(t2_input, out1, 32);
|
||||||
|
t2_input[32] = 0x02;
|
||||||
|
se050_hmac_blake2s(out2, prk, 32, t2_input, 33);
|
||||||
|
|
||||||
|
/* T(3) = HMAC(PRK, T(2) || 0x03) */
|
||||||
|
uint8_t t3_input[33];
|
||||||
|
memcpy(t3_input, out2, 32);
|
||||||
|
t3_input[32] = 0x03;
|
||||||
|
se050_hmac_blake2s(out3, prk, 32, t3_input, 33);
|
||||||
|
|
||||||
|
memzero_explicit(t2_input, 33);
|
||||||
|
memzero_explicit(t3_input, 33);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================================================================
|
||||||
|
* 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 */
|
||||||
|
/* WireGuard uses keyed BLAKE2s, not HMAC */
|
||||||
|
se050_blake2s_keyed(session->cookie_secret, 32,
|
||||||
|
WG_COOKIE_MAGIC, sizeof(WG_COOKIE_MAGIC),
|
||||||
|
private_key, WG_KEY_LEN);
|
||||||
|
|
||||||
|
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
|
||||||
|
* WireGuard uses simplified HKDF with 32-byte PRK
|
||||||
|
*
|
||||||
|
* Key derivation differs for initiator vs responder:
|
||||||
|
* - Initiator: sending = T(1), receiving = T(2)
|
||||||
|
* - Responder: sending = T(2), receiving = T(1)
|
||||||
|
*/
|
||||||
|
uint8_t t1[32], t2[32];
|
||||||
|
wg_hkdf_2(shared_secret, t1, t2);
|
||||||
|
|
||||||
|
if (session->is_initiator) {
|
||||||
|
/* Initiator: sending = T(1), receiving = T(2) */
|
||||||
|
memcpy(session->sending_key, t1, 32);
|
||||||
|
memcpy(session->receiving_key, t2, 32);
|
||||||
|
} else {
|
||||||
|
/* Responder: sending = T(2), receiving = T(1) */
|
||||||
|
memcpy(session->sending_key, t2, 32);
|
||||||
|
memcpy(session->receiving_key, t1, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
memzero_explicit(t1, 32);
|
||||||
|
memzero_explicit(t2, 32);
|
||||||
|
|
||||||
|
/* Reset nonces */
|
||||||
|
session->sending_nonce = 0;
|
||||||
|
session->receiving_nonce = 0;
|
||||||
|
|
||||||
|
session->handshake_complete = 1;
|
||||||
|
|
||||||
|
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; /* RFC 9153: Data packet */
|
||||||
|
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
|
||||||
|
* Note: We encrypt directly into the output buffer to avoid large stack allocation
|
||||||
|
* out = [header(16)][ciphertext][tag(16)]
|
||||||
|
*/
|
||||||
|
uint8_t nonce_buf[WG_NONCE_LEN];
|
||||||
|
memset(nonce_buf, 0, 4);
|
||||||
|
memcpy(nonce_buf + 4, header + 8, 8);
|
||||||
|
|
||||||
|
uint8_t tag[16];
|
||||||
|
|
||||||
|
se050_chacha20_poly1305_ctx_t aead_ctx;
|
||||||
|
se050_chacha20_poly1305_init(&aead_ctx, session->sending_key);
|
||||||
|
|
||||||
|
int ret = se050_chacha20_poly1305_encrypt(
|
||||||
|
&aead_ctx, /* ctx */
|
||||||
|
nonce_buf, /* nonce */
|
||||||
|
plaintext, plaintext_len, /* plaintext */
|
||||||
|
header, 16, /* aad */
|
||||||
|
out + 16, /* ciphertext (direct write) */
|
||||||
|
tag /* tag */
|
||||||
|
);
|
||||||
|
|
||||||
|
se050_chacha20_poly1305_zeroize(&aead_ctx);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
memzero_explicit(tag, 16);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(out + 16 + plaintext_len, tag, 16);
|
||||||
|
*out_len = 16 + plaintext_len + 16;
|
||||||
|
memzero_explicit(tag, 16);
|
||||||
|
|
||||||
|
/* 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) {
|
||||||
|
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 - strictly reject nonce <= last received nonce */
|
||||||
|
if (session->packets_received > 0 && 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);
|
||||||
|
|
||||||
|
se050_chacha20_poly1305_ctx_t aead_ctx;
|
||||||
|
se050_chacha20_poly1305_init(&aead_ctx, session->receiving_key);
|
||||||
|
|
||||||
|
int ret = se050_chacha20_poly1305_decrypt(
|
||||||
|
&aead_ctx, /* ctx */
|
||||||
|
nonce_buf, /* nonce */
|
||||||
|
packet + 16, ciphertext_len, /* ciphertext */
|
||||||
|
header, 16, /* aad, aad_len */
|
||||||
|
tag, /* tag */
|
||||||
|
plaintext /* plaintext (output) */
|
||||||
|
);
|
||||||
|
|
||||||
|
se050_chacha20_poly1305_zeroize(&aead_ctx);
|
||||||
|
memzero_explicit(tag, 16);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update plaintext length and nonce only on success */
|
||||||
|
*plaintext_len = ciphertext_len;
|
||||||
|
session->receiving_nonce = nonce;
|
||||||
|
session->packets_received++;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* WireGuard uses keyed BLAKE2s for MAC1 */
|
||||||
|
return se050_blake2s_keyed(mac1, 16, session->peer_public_key, WG_KEY_LEN,
|
||||||
|
packet, packet_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MAC2 is only used during handshake (packets < 148 bytes)
|
||||||
|
* Fixed buffer is sufficient and avoids malloc dependency
|
||||||
|
* This is safe for u-boot and other embedded environments
|
||||||
|
*/
|
||||||
|
uint8_t data[256]; /* Handshake packets are typically < 148 bytes */
|
||||||
|
|
||||||
|
if (packet_len + WG_MAC1_SIZE > sizeof(data)) {
|
||||||
|
return -1; /* Should never happen for valid handshake packets */
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(data, packet, packet_len);
|
||||||
|
memcpy(data + packet_len, mac1, WG_MAC1_SIZE);
|
||||||
|
|
||||||
|
/* WireGuard uses keyed BLAKE2s for MAC2 */
|
||||||
|
int ret = se050_blake2s_keyed(mac2, 16, session->cookie_secret, 32,
|
||||||
|
data, packet_len + WG_MAC1_SIZE);
|
||||||
|
|
||||||
|
memzero_explicit(data, sizeof(data));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================================================================
|
||||||
|
* Key Generation Utility
|
||||||
|
* ========================================================================= */
|
||||||
|
|
||||||
|
/* Simple test RNG for development (NOT SECURE!) */
|
||||||
|
#ifdef X25519_SW_TEST
|
||||||
|
static int simple_rng(uint8_t *out, size_t len, void *ctx)
|
||||||
|
{
|
||||||
|
static uint32_t seed = 12345;
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
seed = seed * 1103515245 + 12345;
|
||||||
|
out[i] = (seed >> 16) & 0xff;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* System RNG fallback (uses /dev/urandom on POSIX) */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static int system_rng(uint8_t *out, size_t len, void *ctx)
|
||||||
|
{
|
||||||
|
int fd = open("/dev/urandom", O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t total = 0;
|
||||||
|
while (total < len) {
|
||||||
|
ssize_t n = read(fd, out + total, len - total);
|
||||||
|
if (n < 0) {
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
total += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
#ifdef X25519_SW_TEST
|
||||||
|
/* Use simple RNG for testing */
|
||||||
|
if (se050_x25519_sw_generate_keypair(&keypair, simple_rng, NULL) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Production: use system RNG (can be replaced with SE050 RNG) */
|
||||||
|
if (se050_x25519_sw_generate_keypair(&keypair, system_rng, NULL) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memcpy(private_key, keypair.private_key, WG_KEY_LEN);
|
||||||
|
memcpy(public_key, keypair.public_key, WG_KEY_LEN);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_wireguard_proto.c
|
||||||
|
* @brief WireGuard Protocol Layer Implementation
|
||||||
|
* Key derivation chains and handshake processing
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "se050_wireguard_proto.h"
|
||||||
|
#include "se050_hkdf_blake2s.h"
|
||||||
|
#include "se050_tai64n.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define WG_LABEL_K1 "WireGuard key K1"
|
||||||
|
#define WG_LABEL_K2 "WireGuard key K2"
|
||||||
|
#define WG_LABEL_K3 "WireGuard key K3"
|
||||||
|
|
||||||
|
void wg_kdf_init(uint8_t ck[32], uint8_t tk[32])
|
||||||
|
{
|
||||||
|
memset(ck, 0, 32);
|
||||||
|
if (tk) {
|
||||||
|
memset(tk, 0, 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wg_kdf1(uint8_t ck[32], uint8_t tk1[32],
|
||||||
|
const uint8_t ikm[32], size_t ikmlen)
|
||||||
|
{
|
||||||
|
uint8_t prk[32];
|
||||||
|
uint8_t okm[64];
|
||||||
|
|
||||||
|
/* HKDF-Extract */
|
||||||
|
se050_hkdf_extract(prk, NULL, 0, ikm, ikmlen);
|
||||||
|
|
||||||
|
/* HKDF-Expand with label K1 */
|
||||||
|
se050_hkdf_expand(okm, 64, prk,
|
||||||
|
(const uint8_t*)WG_LABEL_K1,
|
||||||
|
sizeof(WG_LABEL_K1) - 1);
|
||||||
|
|
||||||
|
memcpy(ck, okm, 32);
|
||||||
|
memcpy(tk1, okm + 32, 32);
|
||||||
|
|
||||||
|
memset(prk, 0, 32);
|
||||||
|
memset(okm, 0, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wg_kdf2(uint8_t ck[32], uint8_t tk2[32],
|
||||||
|
const uint8_t ck_old[32], const uint8_t tk1[32])
|
||||||
|
{
|
||||||
|
uint8_t prk[32];
|
||||||
|
uint8_t okm[64];
|
||||||
|
uint8_t input[64];
|
||||||
|
|
||||||
|
/* Mix old chain key with temp key */
|
||||||
|
memcpy(input, ck_old, 32);
|
||||||
|
memcpy(input + 32, tk1, 32);
|
||||||
|
|
||||||
|
/* HKDF-Extract */
|
||||||
|
se050_hkdf_extract(prk, NULL, 0, input, 64);
|
||||||
|
|
||||||
|
/* HKDF-Expand with label K2 */
|
||||||
|
se050_hkdf_expand(okm, 64, prk,
|
||||||
|
(const uint8_t*)WG_LABEL_K2,
|
||||||
|
sizeof(WG_LABEL_K2) - 1);
|
||||||
|
|
||||||
|
memcpy(ck, okm, 32);
|
||||||
|
memcpy(tk2, okm + 32, 32);
|
||||||
|
|
||||||
|
memset(prk, 0, 32);
|
||||||
|
memset(okm, 0, 64);
|
||||||
|
memset(input, 0, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wg_kdf3(uint8_t ck[32], uint8_t tk3[32],
|
||||||
|
const uint8_t ck_old[32], const uint8_t tk2[32],
|
||||||
|
const uint8_t *data, size_t datalen)
|
||||||
|
{
|
||||||
|
uint8_t prk[32];
|
||||||
|
uint8_t okm[32];
|
||||||
|
uint8_t input[64];
|
||||||
|
|
||||||
|
/* Mix old chain key with temp key */
|
||||||
|
memcpy(input, ck_old, 32);
|
||||||
|
memcpy(input + 32, tk2, 32);
|
||||||
|
|
||||||
|
/* HKDF-Extract with data */
|
||||||
|
se050_hkdf_extract(prk, NULL, 0, input, 64);
|
||||||
|
|
||||||
|
/* HKDF-Expand with label K3 and data */
|
||||||
|
uint8_t info[64 + sizeof(WG_LABEL_K3)];
|
||||||
|
memcpy(info, WG_LABEL_K3, sizeof(WG_LABEL_K3) - 1);
|
||||||
|
if (data && datalen > 0) {
|
||||||
|
memcpy(info + sizeof(WG_LABEL_K3) - 1, data, datalen);
|
||||||
|
}
|
||||||
|
se050_hkdf_expand(okm, 32, prk,
|
||||||
|
info,
|
||||||
|
sizeof(WG_LABEL_K3) - 1 + datalen);
|
||||||
|
|
||||||
|
memcpy(ck, okm, 32);
|
||||||
|
memset(tk3, 0, 32); /* tk3 is not used, just zero */
|
||||||
|
|
||||||
|
memset(prk, 0, 32);
|
||||||
|
memset(okm, 0, 32);
|
||||||
|
memset(input, 0, 64);
|
||||||
|
memset(info, 0, sizeof(info));
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_wireguard_se050_rng.c
|
||||||
|
* @brief WireGuard with SE050 Hardware RNG Integration
|
||||||
|
*
|
||||||
|
* This file provides an alternative key generation function that uses
|
||||||
|
* the SE050 hardware TRNG instead of the system RNG.
|
||||||
|
*
|
||||||
|
* Usage: Link with se050_rng.c and se050_session.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "se050_wireguard.h"
|
||||||
|
#include "se050_x25519_sw.h"
|
||||||
|
#include "se050_rng.h"
|
||||||
|
#include "se050_session.h"
|
||||||
|
|
||||||
|
/* SE050 RNG wrapper for x25519 keypair generation */
|
||||||
|
static int se050_rng_wrapper(uint8_t *out, size_t len, void *ctx)
|
||||||
|
{
|
||||||
|
se050_rng_ctx_t *rng = (se050_rng_ctx_t *)ctx;
|
||||||
|
|
||||||
|
if (!rng || !out) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
se050_status_t ret = se050_rng_generate(rng, out, len);
|
||||||
|
return (ret == SE050_OK) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate WireGuard keypair using SE050 hardware RNG
|
||||||
|
*
|
||||||
|
* @param session SE050 session context (must be initialized)
|
||||||
|
* @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_se050(se050_session_ctx_t *session,
|
||||||
|
uint8_t *private_key,
|
||||||
|
uint8_t *public_key)
|
||||||
|
{
|
||||||
|
if (!session || !private_key || !public_key) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize SE050 RNG */
|
||||||
|
se050_rng_ctx_t *rng;
|
||||||
|
se050_status_t ret = se050_rng_init(&rng, session);
|
||||||
|
if (ret != SE050_OK) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate keypair using SE050 RNG */
|
||||||
|
se050_x25519_sw_keypair_t keypair;
|
||||||
|
ret = se050_x25519_sw_generate_keypair(&keypair, se050_rng_wrapper, rng);
|
||||||
|
|
||||||
|
/* Cleanup RNG context */
|
||||||
|
se050_rng_free(rng);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(private_key, keypair.private_key, 32);
|
||||||
|
memcpy(public_key, keypair.public_key, 32);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -7,10 +7,15 @@
|
|||||||
* License: MIT (Clean-room implementation)
|
* License: MIT (Clean-room implementation)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "se050_i2c_hal.h"
|
||||||
#include "se050_wireguard.h"
|
#include "se050_wireguard.h"
|
||||||
#include "se050_crypto_utils.h"
|
#include "se050_crypto_utils.h"
|
||||||
#include "se050_keystore_internal.h"
|
#include "se050_keystore_internal.h"
|
||||||
#include "se050_session_internal.h"
|
#include "se050_session_internal.h"
|
||||||
|
#include "se050_x25519_sw.h"
|
||||||
|
|
||||||
|
/* Type alias for compatibility */
|
||||||
|
typedef se050_x25519_sw_keypair_t se050_x25519_keypair_t;
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|||||||
@@ -0,0 +1,696 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_x25519_sw.c
|
||||||
|
* @brief Software X25519 ECDH Implementation (Clean-room RFC7748)
|
||||||
|
* Based on RFC 7748 reference implementation with 5×51-bit limbs
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "se050_x25519_sw.h"
|
||||||
|
#include "se050_crypto_utils.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* =========================================================================
|
||||||
|
* Platform detection
|
||||||
|
* ========================================================================= */
|
||||||
|
|
||||||
|
#if defined(ESP_PLATFORM) || defined(__XTENSA__)
|
||||||
|
#define SE050_X25519_ESP32 1
|
||||||
|
#else
|
||||||
|
#define SE050_X25519_ESP32 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* =========================================================================
|
||||||
|
* Field GF(2^255-19)
|
||||||
|
*
|
||||||
|
* We represent field elements as arrays of 5 uint64_t limbs in radix 2^51.
|
||||||
|
* Each limb holds at most 51 bits in "loose" form.
|
||||||
|
*
|
||||||
|
* value = limb[0] + limb[1] * 2^51 + limb[2] * 2^102 + limb[3] * 2^153 + limb[4] * 2^204
|
||||||
|
*
|
||||||
|
* p = 2^255 - 19, so 2^255 ≡ 19 (mod p)
|
||||||
|
* ========================================================================= */
|
||||||
|
|
||||||
|
#define NLIMBS 5
|
||||||
|
typedef uint64_t fe[NLIMBS]; /* field element */
|
||||||
|
|
||||||
|
#define L51 ((uint64_t)1 << 51)
|
||||||
|
#define MASK51 (L51 - 1)
|
||||||
|
|
||||||
|
/* 128-bit helpers */
|
||||||
|
#if !SE050_X25519_ESP32
|
||||||
|
static inline uint64_t u128_lo(unsigned __int128 x) { return (uint64_t)x; }
|
||||||
|
static inline uint64_t u128_hi(unsigned __int128 x) { return (uint64_t)(x >> 64); }
|
||||||
|
#else
|
||||||
|
/* ESP32: 128-bit emulation using 64-bit arithmetic */
|
||||||
|
typedef struct { uint64_t lo, hi; } u128;
|
||||||
|
static inline u128 u128_mul(uint64_t a, uint64_t b) {
|
||||||
|
u128 r;
|
||||||
|
uint64_t a_lo = a & 0xFFFFFFFFULL, a_hi = a >> 32;
|
||||||
|
uint64_t b_lo = b & 0xFFFFFFFFULL, b_hi = b >> 32;
|
||||||
|
uint64_t p0 = a_lo * b_lo;
|
||||||
|
uint64_t p1 = a_lo * b_hi;
|
||||||
|
uint64_t p2 = a_hi * b_lo;
|
||||||
|
uint64_t p3 = a_hi * b_hi;
|
||||||
|
uint64_t mid = p1 + p2;
|
||||||
|
r.lo = p0 + (mid << 32);
|
||||||
|
r.hi = p3 + (mid >> 32) + ((p0 + (mid << 32)) < p0);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
static inline uint64_t u128_lo(u128 x) { return x.lo; }
|
||||||
|
static inline uint64_t u128_hi(u128 x) { return x.hi; }
|
||||||
|
static inline u128 u128_add(u128 a, u128 b) {
|
||||||
|
u128 r;
|
||||||
|
r.lo = a.lo + b.lo;
|
||||||
|
r.hi = a.hi + b.hi + (r.lo < a.lo);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* --- Basic operations --- */
|
||||||
|
|
||||||
|
static void fe_zero(fe f) { f[0] = f[1] = f[2] = f[3] = f[4] = 0; }
|
||||||
|
static void fe_one(fe f) { f[0] = 1; f[1] = f[2] = f[3] = f[4] = 0; }
|
||||||
|
|
||||||
|
static void fe_copy(fe out, const fe in)
|
||||||
|
{
|
||||||
|
out[0] = in[0]; out[1] = in[1]; out[2] = in[2]; out[3] = in[3]; out[4] = in[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fe_add: out = a + b (loose, ≤ 2·2^51) */
|
||||||
|
static void fe_add(fe out, const fe a, const fe b)
|
||||||
|
{
|
||||||
|
out[0] = a[0] + b[0];
|
||||||
|
out[1] = a[1] + b[1];
|
||||||
|
out[2] = a[2] + b[2];
|
||||||
|
out[3] = a[3] + b[3];
|
||||||
|
out[4] = a[4] + b[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fe_sub: out = a - b (loose, uses bias to avoid underflow) */
|
||||||
|
static void fe_sub(fe out, const fe a, const fe b)
|
||||||
|
{
|
||||||
|
out[0] = a[0] + 2*(L51 - 19) - b[0];
|
||||||
|
out[1] = a[1] + 2*(L51 - 1) - b[1];
|
||||||
|
out[2] = a[2] + 2*(L51 - 1) - b[2];
|
||||||
|
out[3] = a[3] + 2*(L51 - 1) - b[3];
|
||||||
|
out[4] = a[4] + 2*(L51 - 1) - b[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fe_reduce: propagate carries, keep limbs < 2^51 */
|
||||||
|
static void fe_reduce(fe f)
|
||||||
|
{
|
||||||
|
uint64_t c;
|
||||||
|
c = f[0] >> 51; f[0] &= MASK51; f[1] += c;
|
||||||
|
c = f[1] >> 51; f[1] &= MASK51; f[2] += c;
|
||||||
|
c = f[2] >> 51; f[2] &= MASK51; f[3] += c;
|
||||||
|
c = f[3] >> 51; f[3] &= MASK51; f[4] += c;
|
||||||
|
c = f[4] >> 51; f[4] &= MASK51; f[0] += 19 * c;
|
||||||
|
c = f[0] >> 51; f[0] &= MASK51; f[1] += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Multiplication --- */
|
||||||
|
|
||||||
|
#if !SE050_X25519_ESP32
|
||||||
|
/* fe_mul: out = a * b mod p (128-bit accumulators) */
|
||||||
|
static void fe_mul(fe out, const fe a, const fe b)
|
||||||
|
{
|
||||||
|
unsigned __int128 t0, t1, t2, t3, t4;
|
||||||
|
uint64_t c;
|
||||||
|
uint64_t b1_19 = 19 * b[1], b2_19 = 19 * b[2], b3_19 = 19 * b[3], b4_19 = 19 * b[4];
|
||||||
|
|
||||||
|
t0 = (unsigned __int128)a[0] * b[0];
|
||||||
|
t0 += (unsigned __int128)a[1] * b4_19;
|
||||||
|
t0 += (unsigned __int128)a[2] * b3_19;
|
||||||
|
t0 += (unsigned __int128)a[3] * b2_19;
|
||||||
|
t0 += (unsigned __int128)a[4] * b1_19;
|
||||||
|
|
||||||
|
t1 = (unsigned __int128)a[0] * b[1];
|
||||||
|
t1 += (unsigned __int128)a[1] * b[0];
|
||||||
|
t1 += (unsigned __int128)a[2] * b4_19;
|
||||||
|
t1 += (unsigned __int128)a[3] * b3_19;
|
||||||
|
t1 += (unsigned __int128)a[4] * b2_19;
|
||||||
|
|
||||||
|
t2 = (unsigned __int128)a[0] * b[2];
|
||||||
|
t2 += (unsigned __int128)a[1] * b[1];
|
||||||
|
t2 += (unsigned __int128)a[2] * b[0];
|
||||||
|
t2 += (unsigned __int128)a[3] * b4_19;
|
||||||
|
t2 += (unsigned __int128)a[4] * b3_19;
|
||||||
|
|
||||||
|
t3 = (unsigned __int128)a[0] * b[3];
|
||||||
|
t3 += (unsigned __int128)a[1] * b[2];
|
||||||
|
t3 += (unsigned __int128)a[2] * b[1];
|
||||||
|
t3 += (unsigned __int128)a[3] * b[0];
|
||||||
|
t3 += (unsigned __int128)a[4] * b4_19;
|
||||||
|
|
||||||
|
t4 = (unsigned __int128)a[0] * b[4];
|
||||||
|
t4 += (unsigned __int128)a[1] * b[3];
|
||||||
|
t4 += (unsigned __int128)a[2] * b[2];
|
||||||
|
t4 += (unsigned __int128)a[3] * b[1];
|
||||||
|
t4 += (unsigned __int128)a[4] * b[0];
|
||||||
|
|
||||||
|
out[0] = u128_lo(t0) & MASK51; c = u128_lo(t0) >> 51 | u128_hi(t0) << 13; t1 += c;
|
||||||
|
out[1] = u128_lo(t1) & MASK51; c = u128_lo(t1) >> 51 | u128_hi(t1) << 13; t2 += c;
|
||||||
|
out[2] = u128_lo(t2) & MASK51; c = u128_lo(t2) >> 51 | u128_hi(t2) << 13; t3 += c;
|
||||||
|
out[3] = u128_lo(t3) & MASK51; c = u128_lo(t3) >> 51 | u128_hi(t3) << 13; t4 += c;
|
||||||
|
out[4] = u128_lo(t4) & MASK51; c = u128_lo(t4) >> 51 | u128_hi(t4) << 13;
|
||||||
|
out[0] += 19 * c;
|
||||||
|
|
||||||
|
c = out[0] >> 51; out[0] &= MASK51; out[1] += c;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* ESP32: fe_mul with 128-bit emulation */
|
||||||
|
static void fe_mul(fe out, const fe a, const fe b)
|
||||||
|
{
|
||||||
|
u128 t0, t1, t2, t3, t4;
|
||||||
|
uint64_t c;
|
||||||
|
uint64_t b1_19 = 19 * b[1], b2_19 = 19 * b[2], b3_19 = 19 * b[3], b4_19 = 19 * b[4];
|
||||||
|
|
||||||
|
t0 = u128_mul(a[0], b[0]);
|
||||||
|
t0 = u128_add(t0, u128_mul(a[1], b4_19));
|
||||||
|
t0 = u128_add(t0, u128_mul(a[2], b3_19));
|
||||||
|
t0 = u128_add(t0, u128_mul(a[3], b2_19));
|
||||||
|
t0 = u128_add(t0, u128_mul(a[4], b1_19));
|
||||||
|
|
||||||
|
t1 = u128_mul(a[0], b[1]);
|
||||||
|
t1 = u128_add(t1, u128_mul(a[1], b[0]));
|
||||||
|
t1 = u128_add(t1, u128_mul(a[2], b4_19));
|
||||||
|
t1 = u128_add(t1, u128_mul(a[3], b3_19));
|
||||||
|
t1 = u128_add(t1, u128_mul(a[4], b2_19));
|
||||||
|
|
||||||
|
t2 = u128_mul(a[0], b[2]);
|
||||||
|
t2 = u128_add(t2, u128_mul(a[1], b[1]));
|
||||||
|
t2 = u128_add(t2, u128_mul(a[2], b[0]));
|
||||||
|
t2 = u128_add(t2, u128_mul(a[3], b4_19));
|
||||||
|
t2 = u128_add(t2, u128_mul(a[4], b3_19));
|
||||||
|
|
||||||
|
t3 = u128_mul(a[0], b[3]);
|
||||||
|
t3 = u128_add(t3, u128_mul(a[1], b[2]));
|
||||||
|
t3 = u128_add(t3, u128_mul(a[2], b[1]));
|
||||||
|
t3 = u128_add(t3, u128_mul(a[3], b[0]));
|
||||||
|
t3 = u128_add(t3, u128_mul(a[4], b4_19));
|
||||||
|
|
||||||
|
t4 = u128_mul(a[0], b[4]);
|
||||||
|
t4 = u128_add(t4, u128_mul(a[1], b[3]));
|
||||||
|
t4 = u128_add(t4, u128_mul(a[2], b[2]));
|
||||||
|
t4 = u128_add(t4, u128_mul(a[3], b[1]));
|
||||||
|
t4 = u128_add(t4, u128_mul(a[4], b[0]));
|
||||||
|
|
||||||
|
out[0] = u128_lo(t0) & MASK51; c = u128_lo(t0) >> 51 | u128_hi(t0) << 13; t1 = u128_add(t1, (u128){c, 0});
|
||||||
|
out[1] = u128_lo(t1) & MASK51; c = u128_lo(t1) >> 51 | u128_hi(t1) << 13; t2 = u128_add(t2, (u128){c, 0});
|
||||||
|
out[2] = u128_lo(t2) & MASK51; c = u128_lo(t2) >> 51 | u128_hi(t2) << 13; t3 = u128_add(t3, (u128){c, 0});
|
||||||
|
out[3] = u128_lo(t3) & MASK51; c = u128_lo(t3) >> 51 | u128_hi(t3) << 13; t4 = u128_add(t4, (u128){c, 0});
|
||||||
|
out[4] = u128_lo(t4) & MASK51; c = u128_lo(t4) >> 51 | u128_hi(t4) << 13;
|
||||||
|
out[0] += 19 * c;
|
||||||
|
|
||||||
|
c = out[0] >> 51; out[0] &= MASK51; out[1] += c;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* fe_sq: out = a^2 mod p (optimized) */
|
||||||
|
#if !SE050_X25519_ESP32
|
||||||
|
static void fe_sq(fe out, const fe a)
|
||||||
|
{
|
||||||
|
unsigned __int128 t0, t1, t2, t3, t4;
|
||||||
|
uint64_t c;
|
||||||
|
uint64_t d1 = 2 * a[1], d2 = 2 * a[2], d3 = 2 * a[3];
|
||||||
|
uint64_t a4_19 = 19 * a[4], d1_19 = 19 * d1, d2_19 = 19 * d2, a3_19 = 19 * a[3];
|
||||||
|
|
||||||
|
t0 = (unsigned __int128)a[0] * a[0];
|
||||||
|
t0 += (unsigned __int128)d1_19 * a[4];
|
||||||
|
t0 += (unsigned __int128)d2_19 * a[3];
|
||||||
|
|
||||||
|
t1 = (unsigned __int128)a[0] * d1;
|
||||||
|
t1 += (unsigned __int128)d2_19 * a[4];
|
||||||
|
t1 += (unsigned __int128)a3_19 * a[3];
|
||||||
|
|
||||||
|
t2 = (unsigned __int128)a[0] * d2;
|
||||||
|
t2 += (unsigned __int128)a[1] * a[1];
|
||||||
|
t2 += (unsigned __int128)d3 * a4_19;
|
||||||
|
|
||||||
|
t3 = (unsigned __int128)a[0] * d3;
|
||||||
|
t3 += (unsigned __int128)d1 * a[2];
|
||||||
|
t3 += (unsigned __int128)a[4] * a4_19;
|
||||||
|
|
||||||
|
t4 = (unsigned __int128)a[0] * (2 * a[4]);
|
||||||
|
t4 += (unsigned __int128)d1 * a[3];
|
||||||
|
t4 += (unsigned __int128)a[2] * a[2];
|
||||||
|
|
||||||
|
out[0] = u128_lo(t0) & MASK51; c = u128_lo(t0) >> 51 | u128_hi(t0) << 13; t1 += c;
|
||||||
|
out[1] = u128_lo(t1) & MASK51; c = u128_lo(t1) >> 51 | u128_hi(t1) << 13; t2 += c;
|
||||||
|
out[2] = u128_lo(t2) & MASK51; c = u128_lo(t2) >> 51 | u128_hi(t2) << 13; t3 += c;
|
||||||
|
out[3] = u128_lo(t3) & MASK51; c = u128_lo(t3) >> 51 | u128_hi(t3) << 13; t4 += c;
|
||||||
|
out[4] = u128_lo(t4) & MASK51; c = u128_lo(t4) >> 51 | u128_hi(t4) << 13;
|
||||||
|
out[0] += 19 * c;
|
||||||
|
c = out[0] >> 51; out[0] &= MASK51; out[1] += c;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void fe_sq(fe out, const fe a)
|
||||||
|
{
|
||||||
|
u128 t0, t1, t2, t3, t4;
|
||||||
|
uint64_t c;
|
||||||
|
uint64_t d1 = 2 * a[1], d2 = 2 * a[2], d3 = 2 * a[3];
|
||||||
|
uint64_t a4_19 = 19 * a[4], d1_19 = 19 * d1, d2_19 = 19 * d2, a3_19 = 19 * a[3];
|
||||||
|
|
||||||
|
t0 = u128_mul(a[0], a[0]);
|
||||||
|
t0 = u128_add(t0, u128_mul(d1_19, a[4]));
|
||||||
|
t0 = u128_add(t0, u128_mul(d2_19, a[3]));
|
||||||
|
|
||||||
|
t1 = u128_mul(a[0], d1);
|
||||||
|
t1 = u128_add(t1, u128_mul(d2_19, a[4]));
|
||||||
|
t1 = u128_add(t1, u128_mul(a3_19, a[3]));
|
||||||
|
|
||||||
|
t2 = u128_mul(a[0], d2);
|
||||||
|
t2 = u128_add(t2, u128_mul(a[1], a[1]));
|
||||||
|
t2 = u128_add(t2, u128_mul(d3, a4_19));
|
||||||
|
|
||||||
|
t3 = u128_mul(a[0], d3);
|
||||||
|
t3 = u128_add(t3, u128_mul(d1, a[2]));
|
||||||
|
t3 = u128_add(t3, u128_mul(a[4], a4_19));
|
||||||
|
|
||||||
|
t4 = u128_mul(a[0], 2 * a[4]);
|
||||||
|
t4 = u128_add(t4, u128_mul(d1, a[3]));
|
||||||
|
t4 = u128_add(t4, u128_mul(a[2], a[2]));
|
||||||
|
|
||||||
|
out[0] = u128_lo(t0) & MASK51; c = u128_lo(t0) >> 51 | u128_hi(t0) << 13; t1 = u128_add(t1, (u128){c, 0});
|
||||||
|
out[1] = u128_lo(t1) & MASK51; c = u128_lo(t1) >> 51 | u128_hi(t1) << 13; t2 = u128_add(t2, (u128){c, 0});
|
||||||
|
out[2] = u128_lo(t2) & MASK51; c = u128_lo(t2) >> 51 | u128_hi(t2) << 13; t3 = u128_add(t3, (u128){c, 0});
|
||||||
|
out[3] = u128_lo(t3) & MASK51; c = u128_lo(t3) >> 51 | u128_hi(t3) << 13; t4 = u128_add(t4, (u128){c, 0});
|
||||||
|
out[4] = u128_lo(t4) & MASK51; c = u128_lo(t4) >> 51 | u128_hi(t4) << 13;
|
||||||
|
out[0] += 19 * c;
|
||||||
|
c = out[0] >> 51; out[0] &= MASK51; out[1] += c;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* fe_mul_small: out = f * n (n < 2^22) */
|
||||||
|
#if !SE050_X25519_ESP32
|
||||||
|
static void fe_mul_small(fe out, const fe f, uint64_t n)
|
||||||
|
{
|
||||||
|
unsigned __int128 t0, t1, t2, t3, t4;
|
||||||
|
uint64_t c;
|
||||||
|
t0 = (unsigned __int128)f[0] * n;
|
||||||
|
t1 = (unsigned __int128)f[1] * n;
|
||||||
|
t2 = (unsigned __int128)f[2] * n;
|
||||||
|
t3 = (unsigned __int128)f[3] * n;
|
||||||
|
t4 = (unsigned __int128)f[4] * n;
|
||||||
|
out[0] = u128_lo(t0) & MASK51; c = u128_lo(t0) >> 51 | u128_hi(t0) << 13; t1 += c;
|
||||||
|
out[1] = u128_lo(t1) & MASK51; c = u128_lo(t1) >> 51 | u128_hi(t1) << 13; t2 += c;
|
||||||
|
out[2] = u128_lo(t2) & MASK51; c = u128_lo(t2) >> 51 | u128_hi(t2) << 13; t3 += c;
|
||||||
|
out[3] = u128_lo(t3) & MASK51; c = u128_lo(t3) >> 51 | u128_hi(t3) << 13; t4 += c;
|
||||||
|
out[4] = u128_lo(t4) & MASK51; c = u128_lo(t4) >> 51 | u128_hi(t4) << 13;
|
||||||
|
out[0] += 19 * c;
|
||||||
|
c = out[0] >> 51; out[0] &= MASK51; out[1] += c;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void fe_mul_small(fe out, const fe f, uint64_t n)
|
||||||
|
{
|
||||||
|
u128 t0, t1, t2, t3, t4;
|
||||||
|
uint64_t c;
|
||||||
|
t0 = u128_mul(f[0], n);
|
||||||
|
t1 = u128_mul(f[1], n);
|
||||||
|
t2 = u128_mul(f[2], n);
|
||||||
|
t3 = u128_mul(f[3], n);
|
||||||
|
t4 = u128_mul(f[4], n);
|
||||||
|
out[0] = u128_lo(t0) & MASK51; c = u128_lo(t0) >> 51 | u128_hi(t0) << 13; t1 = u128_add(t1, (u128){c, 0});
|
||||||
|
out[1] = u128_lo(t1) & MASK51; c = u128_lo(t1) >> 51 | u128_hi(t1) << 13; t2 = u128_add(t2, (u128){c, 0});
|
||||||
|
out[2] = u128_lo(t2) & MASK51; c = u128_lo(t2) >> 51 | u128_hi(t2) << 13; t3 = u128_add(t3, (u128){c, 0});
|
||||||
|
out[3] = u128_lo(t3) & MASK51; c = u128_lo(t3) >> 51 | u128_hi(t3) << 13; t4 = u128_add(t4, (u128){c, 0});
|
||||||
|
out[4] = u128_lo(t4) & MASK51; c = u128_lo(t4) >> 51 | u128_hi(t4) << 13;
|
||||||
|
out[0] += 19 * c;
|
||||||
|
c = out[0] >> 51; out[0] &= MASK51; out[1] += c;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* --- Inversion --- */
|
||||||
|
|
||||||
|
/* fe_invert: out = a^(-1) = a^(p-2) using addition chain */
|
||||||
|
static void fe_invert(fe out, const fe a)
|
||||||
|
{
|
||||||
|
fe t0, t1, t2, t3;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fe_sq(t0, a); /* t0 = a^2 */
|
||||||
|
fe_sq(t1, t0); /* t1 = a^4 */
|
||||||
|
fe_sq(t1, t1); /* t1 = a^8 */
|
||||||
|
fe_mul(t1, t1, a); /* t1 = a^9 */
|
||||||
|
fe_mul(t0, t0, t1); /* t0 = a^11 */
|
||||||
|
fe_sq(t2, t0); /* t2 = a^22 */
|
||||||
|
fe_mul(t1, t1, t2); /* t1 = a^31 */
|
||||||
|
|
||||||
|
fe_sq(t2, t1);
|
||||||
|
for (i = 1; i < 5; i++) fe_sq(t2, t2);
|
||||||
|
fe_mul(t1, t2, t1); /* t1 = a^(2^10-1) */
|
||||||
|
|
||||||
|
fe_sq(t2, t1);
|
||||||
|
for (i = 1; i < 10; i++) fe_sq(t2, t2);
|
||||||
|
fe_mul(t2, t2, t1); /* t2 = a^(2^20-1) */
|
||||||
|
|
||||||
|
fe_sq(t3, t2);
|
||||||
|
for (i = 1; i < 20; i++) fe_sq(t3, t3);
|
||||||
|
fe_mul(t2, t3, t2); /* t2 = a^(2^40-1) */
|
||||||
|
|
||||||
|
fe_sq(t2, t2);
|
||||||
|
for (i = 1; i < 10; i++) fe_sq(t2, t2);
|
||||||
|
fe_mul(t1, t2, t1); /* t1 = a^(2^50-1) */
|
||||||
|
|
||||||
|
fe_sq(t2, t1);
|
||||||
|
for (i = 1; i < 50; i++) fe_sq(t2, t2);
|
||||||
|
fe_mul(t2, t2, t1); /* t2 = a^(2^100-1) */
|
||||||
|
|
||||||
|
fe_sq(t3, t2);
|
||||||
|
for (i = 1; i < 100; i++) fe_sq(t3, t3);
|
||||||
|
fe_mul(t2, t3, t2); /* t2 = a^(2^200-1) */
|
||||||
|
|
||||||
|
fe_sq(t2, t2);
|
||||||
|
for (i = 1; i < 50; i++) fe_sq(t2, t2);
|
||||||
|
fe_mul(t1, t2, t1); /* t1 = a^(2^250-1) */
|
||||||
|
|
||||||
|
fe_sq(t1, t1);
|
||||||
|
fe_sq(t1, t1);
|
||||||
|
fe_sq(t1, t1);
|
||||||
|
fe_sq(t1, t1);
|
||||||
|
fe_sq(t1, t1); /* t1 = a^(2^255-2^5) */
|
||||||
|
fe_mul(out, t1, t0); /* out = a^(2^255-21) = a^(p-2) */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Byte conversion --- */
|
||||||
|
|
||||||
|
/* fe_from_bytes: 32-byte little-endian → field element */
|
||||||
|
static void fe_from_bytes(fe out, const uint8_t in[32])
|
||||||
|
{
|
||||||
|
uint8_t buf[32];
|
||||||
|
memcpy(buf, in, 32);
|
||||||
|
buf[31] &= 0x7f; /* clear top bit per RFC 7748 §5 */
|
||||||
|
|
||||||
|
out[0] = ((uint64_t)buf[ 0])
|
||||||
|
| ((uint64_t)buf[ 1] << 8)
|
||||||
|
| ((uint64_t)buf[ 2] << 16)
|
||||||
|
| ((uint64_t)buf[ 3] << 24)
|
||||||
|
| ((uint64_t)buf[ 4] << 32)
|
||||||
|
| ((uint64_t)buf[ 5] << 40)
|
||||||
|
| ((uint64_t)(buf[6] & 0x07) << 48);
|
||||||
|
|
||||||
|
out[1] = ((uint64_t)buf[ 6] >> 3)
|
||||||
|
| ((uint64_t)buf[ 7] << 5)
|
||||||
|
| ((uint64_t)buf[ 8] << 13)
|
||||||
|
| ((uint64_t)buf[ 9] << 21)
|
||||||
|
| ((uint64_t)buf[10] << 29)
|
||||||
|
| ((uint64_t)buf[11] << 37)
|
||||||
|
| ((uint64_t)(buf[12] & 0x3f) << 45);
|
||||||
|
|
||||||
|
out[2] = ((uint64_t)buf[12] >> 6)
|
||||||
|
| ((uint64_t)buf[13] << 2)
|
||||||
|
| ((uint64_t)buf[14] << 10)
|
||||||
|
| ((uint64_t)buf[15] << 18)
|
||||||
|
| ((uint64_t)buf[16] << 26)
|
||||||
|
| ((uint64_t)buf[17] << 34)
|
||||||
|
| ((uint64_t)buf[18] << 42)
|
||||||
|
| ((uint64_t)(buf[19] & 0x01) << 50);
|
||||||
|
|
||||||
|
out[3] = ((uint64_t)buf[19] >> 1)
|
||||||
|
| ((uint64_t)buf[20] << 7)
|
||||||
|
| ((uint64_t)buf[21] << 15)
|
||||||
|
| ((uint64_t)buf[22] << 23)
|
||||||
|
| ((uint64_t)buf[23] << 31)
|
||||||
|
| ((uint64_t)buf[24] << 39)
|
||||||
|
| ((uint64_t)(buf[25] & 0x0f) << 47);
|
||||||
|
|
||||||
|
out[4] = ((uint64_t)buf[25] >> 4)
|
||||||
|
| ((uint64_t)buf[26] << 4)
|
||||||
|
| ((uint64_t)buf[27] << 12)
|
||||||
|
| ((uint64_t)buf[28] << 20)
|
||||||
|
| ((uint64_t)buf[29] << 28)
|
||||||
|
| ((uint64_t)buf[30] << 36)
|
||||||
|
| ((uint64_t)(buf[31] & 0x7f) << 44);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fe_to_bytes: field element → 32-byte little-endian */
|
||||||
|
static void fe_to_bytes(uint8_t out[32], const fe in)
|
||||||
|
{
|
||||||
|
fe f;
|
||||||
|
uint64_t c, t;
|
||||||
|
|
||||||
|
fe_copy(f, in);
|
||||||
|
fe_reduce(f);
|
||||||
|
fe_reduce(f);
|
||||||
|
|
||||||
|
/* Conditional subtract p = 2^255 - 19 */
|
||||||
|
t = f[0] + 19;
|
||||||
|
c = t >> 51; t &= MASK51; uint64_t g0 = t;
|
||||||
|
t = f[1] + c; c = t >> 51; t &= MASK51; uint64_t g1 = t;
|
||||||
|
t = f[2] + c; c = t >> 51; t &= MASK51; uint64_t g2 = t;
|
||||||
|
t = f[3] + c; c = t >> 51; t &= MASK51; uint64_t g3 = t;
|
||||||
|
t = f[4] + c; uint64_t g4 = t & MASK51;
|
||||||
|
uint64_t mask = -((t >> 51) & 1);
|
||||||
|
f[0] = (f[0] & ~mask) | (g0 & mask);
|
||||||
|
f[1] = (f[1] & ~mask) | (g1 & mask);
|
||||||
|
f[2] = (f[2] & ~mask) | (g2 & mask);
|
||||||
|
f[3] = (f[3] & ~mask) | (g3 & mask);
|
||||||
|
f[4] = (f[4] & ~mask) | (g4 & mask);
|
||||||
|
|
||||||
|
/* Unpack to bytes */
|
||||||
|
out[ 0] = (uint8_t)(f[0]);
|
||||||
|
out[ 1] = (uint8_t)(f[0] >> 8);
|
||||||
|
out[ 2] = (uint8_t)(f[0] >> 16);
|
||||||
|
out[ 3] = (uint8_t)(f[0] >> 24);
|
||||||
|
out[ 4] = (uint8_t)(f[0] >> 32);
|
||||||
|
out[ 5] = (uint8_t)(f[0] >> 40);
|
||||||
|
out[ 6] = (uint8_t)((f[0] >> 48) | (f[1] << 3));
|
||||||
|
out[ 7] = (uint8_t)(f[1] >> 5);
|
||||||
|
out[ 8] = (uint8_t)(f[1] >> 13);
|
||||||
|
out[ 9] = (uint8_t)(f[1] >> 21);
|
||||||
|
out[10] = (uint8_t)(f[1] >> 29);
|
||||||
|
out[11] = (uint8_t)(f[1] >> 37);
|
||||||
|
out[12] = (uint8_t)((f[1] >> 45) | (f[2] << 6));
|
||||||
|
out[13] = (uint8_t)(f[2] >> 2);
|
||||||
|
out[14] = (uint8_t)(f[2] >> 10);
|
||||||
|
out[15] = (uint8_t)(f[2] >> 18);
|
||||||
|
out[16] = (uint8_t)(f[2] >> 26);
|
||||||
|
out[17] = (uint8_t)(f[2] >> 34);
|
||||||
|
out[18] = (uint8_t)(f[2] >> 42);
|
||||||
|
out[19] = (uint8_t)((f[2] >> 50) | (f[3] << 1));
|
||||||
|
out[20] = (uint8_t)(f[3] >> 7);
|
||||||
|
out[21] = (uint8_t)(f[3] >> 15);
|
||||||
|
out[22] = (uint8_t)(f[3] >> 23);
|
||||||
|
out[23] = (uint8_t)(f[3] >> 31);
|
||||||
|
out[24] = (uint8_t)(f[3] >> 39);
|
||||||
|
out[25] = (uint8_t)((f[3] >> 47) | (f[4] << 4));
|
||||||
|
out[26] = (uint8_t)(f[4] >> 4);
|
||||||
|
out[27] = (uint8_t)(f[4] >> 12);
|
||||||
|
out[28] = (uint8_t)(f[4] >> 20);
|
||||||
|
out[29] = (uint8_t)(f[4] >> 28);
|
||||||
|
out[30] = (uint8_t)(f[4] >> 36);
|
||||||
|
out[31] = (uint8_t)(f[4] >> 44);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Montgomery ladder --- */
|
||||||
|
|
||||||
|
#define A24 121665ULL
|
||||||
|
|
||||||
|
/* fe_cswap: conditional swap */
|
||||||
|
static void fe_cswap(fe a, fe b, uint64_t swap)
|
||||||
|
{
|
||||||
|
uint64_t mask = -(swap & 1);
|
||||||
|
for (int i = 0; i < NLIMBS; i++) {
|
||||||
|
uint64_t t = mask & (a[i] ^ b[i]);
|
||||||
|
a[i] ^= t;
|
||||||
|
b[i] ^= t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ladder_step: one Montgomery ladder step */
|
||||||
|
static void ladder_step(
|
||||||
|
fe X2, fe Z2, fe X3, fe Z3,
|
||||||
|
const fe X2_in, const fe Z2_in,
|
||||||
|
const fe X3_in, const fe Z3_in,
|
||||||
|
const fe x1)
|
||||||
|
{
|
||||||
|
fe A, AA, B, BB, E, C, D, DA, CB, tmp, a24_E;
|
||||||
|
|
||||||
|
fe_add(A, X2_in, Z2_in);
|
||||||
|
fe_sq (AA, A);
|
||||||
|
fe_sub(B, X2_in, Z2_in);
|
||||||
|
fe_sq (BB, B);
|
||||||
|
fe_sub(E, AA, BB);
|
||||||
|
fe_add(C, X3_in, Z3_in);
|
||||||
|
fe_sub(D, X3_in, Z3_in);
|
||||||
|
fe_mul(DA, D, A);
|
||||||
|
fe_mul(CB, C, B);
|
||||||
|
|
||||||
|
fe_add(tmp, DA, CB);
|
||||||
|
fe_sq (X3, tmp);
|
||||||
|
fe_sub(tmp, DA, CB);
|
||||||
|
fe_sq (tmp, tmp);
|
||||||
|
fe_mul(Z3, tmp, x1);
|
||||||
|
fe_mul(X2, AA, BB);
|
||||||
|
|
||||||
|
fe_mul_small(a24_E, E, A24);
|
||||||
|
fe_add(tmp, AA, a24_E);
|
||||||
|
fe_mul(Z2, E, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Public API --- */
|
||||||
|
|
||||||
|
const uint8_t X25519_BASE_POINT[32] = { 9 };
|
||||||
|
|
||||||
|
int x25519_sw(uint8_t out[32], const uint8_t scalar[32], const uint8_t point[32])
|
||||||
|
{
|
||||||
|
uint8_t e[32];
|
||||||
|
fe x1, X2, Z2, X3, Z3;
|
||||||
|
uint64_t prev_bit, swap;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Step 1: clamp scalar */
|
||||||
|
memcpy(e, scalar, 32);
|
||||||
|
e[ 0] &= 248;
|
||||||
|
e[31] &= 127;
|
||||||
|
e[31] |= 64;
|
||||||
|
|
||||||
|
/* Step 2: decode u-coordinate */
|
||||||
|
if (point == NULL)
|
||||||
|
fe_from_bytes(x1, X25519_BASE_POINT);
|
||||||
|
else
|
||||||
|
fe_from_bytes(x1, point);
|
||||||
|
|
||||||
|
/* Step 3: initialise projective points */
|
||||||
|
fe_one (X2); fe_zero(Z2);
|
||||||
|
fe_copy(X3, x1); fe_one(Z3);
|
||||||
|
|
||||||
|
/* Step 4: Montgomery ladder */
|
||||||
|
prev_bit = 0;
|
||||||
|
for (i = 254; i >= 0; i--) {
|
||||||
|
uint64_t bit = (e[i / 8] >> (i % 8)) & 1;
|
||||||
|
swap = bit ^ prev_bit;
|
||||||
|
prev_bit = bit;
|
||||||
|
|
||||||
|
fe_cswap(X2, X3, swap);
|
||||||
|
fe_cswap(Z2, Z3, swap);
|
||||||
|
|
||||||
|
fe nX2, nZ2, nX3, nZ3;
|
||||||
|
ladder_step(nX2, nZ2, nX3, nZ3, X2, Z2, X3, Z3, x1);
|
||||||
|
fe_copy(X2, nX2); fe_copy(Z2, nZ2);
|
||||||
|
fe_copy(X3, nX3); fe_copy(Z3, nZ3);
|
||||||
|
}
|
||||||
|
fe_cswap(X2, X3, prev_bit);
|
||||||
|
fe_cswap(Z2, Z3, prev_bit);
|
||||||
|
|
||||||
|
/* Step 5: convert from projective to affine */
|
||||||
|
fe Z2_inv;
|
||||||
|
fe_invert(Z2_inv, Z2);
|
||||||
|
fe_mul(X2, X2, Z2_inv);
|
||||||
|
|
||||||
|
/* Step 6: encode result */
|
||||||
|
fe_to_bytes(out, X2);
|
||||||
|
|
||||||
|
/* Step 7: reject all-zero output */
|
||||||
|
uint8_t acc = 0;
|
||||||
|
for (i = 0; i < 32; i++) acc |= out[i];
|
||||||
|
if (acc == 0) {
|
||||||
|
memzero_explicit(e, 32);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zeroize clamped scalar before return */
|
||||||
|
memzero_explicit(e, 32);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void se050_x25519_sw_clamp(uint8_t *scalar)
|
||||||
|
{
|
||||||
|
scalar[0] &= 248;
|
||||||
|
scalar[31] &= 127;
|
||||||
|
scalar[31] |= 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
void se050_x25519_sw_zeroize(uint8_t *key, size_t len)
|
||||||
|
{
|
||||||
|
memzero_explicit(key, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_x25519_sw_generate_keypair(se050_x25519_sw_keypair_t *keypair,
|
||||||
|
x25519_rng_func rng_func,
|
||||||
|
void *rng_ctx)
|
||||||
|
{
|
||||||
|
if (!keypair || !rng_func) return -1;
|
||||||
|
if (rng_func(keypair->private_key, 32, rng_ctx) != 0) return -1;
|
||||||
|
se050_x25519_sw_clamp(keypair->private_key);
|
||||||
|
x25519_sw(keypair->public_key, keypair->private_key, (const uint8_t*)"basepoint");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_x25519_sw_compute_shared_secret(uint8_t *shared_secret,
|
||||||
|
const uint8_t *private_key,
|
||||||
|
const uint8_t *peer_public)
|
||||||
|
{
|
||||||
|
if (!shared_secret || !private_key || !peer_public) return -1;
|
||||||
|
uint8_t clamped[32];
|
||||||
|
memcpy(clamped, private_key, 32);
|
||||||
|
se050_x25519_sw_clamp(clamped);
|
||||||
|
int ret = x25519_sw(shared_secret, clamped, peer_public);
|
||||||
|
memzero_explicit(clamped, 32);
|
||||||
|
if (ret < 0) {
|
||||||
|
/* Zeroize output on failure */
|
||||||
|
memzero_explicit(shared_secret, 32);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int se050_x25519_sw_derive_public_key(uint8_t *public_key,
|
||||||
|
const uint8_t *private_key)
|
||||||
|
{
|
||||||
|
if (!public_key || !private_key) return -1;
|
||||||
|
uint8_t clamped[32];
|
||||||
|
memcpy(clamped, private_key, 32);
|
||||||
|
se050_x25519_sw_clamp(clamped);
|
||||||
|
int ret = x25519_sw(public_key, clamped, (const uint8_t*)"basepoint");
|
||||||
|
memzero_explicit(clamped, 32);
|
||||||
|
if (ret < 0) {
|
||||||
|
memzero_explicit(public_key, 32);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef X25519_SW_TEST
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* RFC 7748 §6.1 Test Vector 1 */
|
||||||
|
static const uint8_t RFC7748_SK_1[32] = {
|
||||||
|
0xa5,0x46,0xe3,0x6b,0xf0,0x52,0x7c,0x9d,0x3b,0x16,0x15,0x4b,
|
||||||
|
0x82,0x46,0x5e,0xdd,0x62,0x14,0x4c,0x0a,0xc1,0xfc,0x5a,0x18,
|
||||||
|
0x50,0x6a,0x22,0x44,0xba,0x44,0x9a,0xc4 };
|
||||||
|
static const uint8_t RFC7748_PK_1[32] = {
|
||||||
|
0xe6,0xdb,0x68,0x67,0x58,0x30,0x30,0xdb,0x35,0x94,0xc1,0xa4,
|
||||||
|
0x24,0xb1,0x5f,0x7c,0x72,0x66,0x24,0xec,0x26,0xb3,0x35,0x3b,
|
||||||
|
0x10,0xa9,0x03,0xa6,0xd0,0xab,0x1c,0x4c };
|
||||||
|
static const uint8_t RFC7748_SS_1[32] = {
|
||||||
|
0xc3,0xda,0x55,0x37,0x9d,0xe9,0xc6,0x90,0x8e,0x94,0xea,0x4d,
|
||||||
|
0xf2,0x8d,0x08,0x4f,0x32,0xec,0xcf,0x03,0x49,0x1c,0x71,0xf7,
|
||||||
|
0x54,0xb4,0x07,0x55,0x77,0xa2,0x85,0x52 };
|
||||||
|
|
||||||
|
static void print_hex(const char *label, const uint8_t *buf, size_t len)
|
||||||
|
{
|
||||||
|
printf("%s: ", label);
|
||||||
|
for (size_t i = 0; i < len; i++) printf("%02x", buf[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
uint8_t shared_secret[32];
|
||||||
|
printf("X25519 Software Implementation Test\n");
|
||||||
|
printf("====================================\n\n");
|
||||||
|
printf("RFC 7748 Test Vector 1:\n");
|
||||||
|
print_hex("Scalar", RFC7748_SK_1, 32);
|
||||||
|
print_hex("Point", RFC7748_PK_1, 32);
|
||||||
|
x25519_sw(shared_secret, RFC7748_SK_1, RFC7748_PK_1);
|
||||||
|
print_hex("Computed SS", shared_secret, 32);
|
||||||
|
print_hex("Expected SS", RFC7748_SS_1, 32);
|
||||||
|
if (memcmp(shared_secret, RFC7748_SS_1, 32) == 0) {
|
||||||
|
printf("[PASS] RFC 7748 Test Vector 1\n");
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
printf("[FAIL] RFC 7748 Test Vector 1\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#define X25519_SW_TEST 1
|
||||||
|
#include "se050_wireguard.h"
|
||||||
|
#include "se050_x25519_sw.h"
|
||||||
|
#include "se050_chacha20_poly1305.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
printf("=== Debug WireGuard Encrypt/Decrypt ===\n\n");
|
||||||
|
|
||||||
|
uint8_t priv[32], peer_pub[32];
|
||||||
|
for(int i=0; i<32; i++) { priv[i] = i+1; peer_pub[i] = i+2; }
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
se050_wireguard_session_init(&session, priv, peer_pub);
|
||||||
|
|
||||||
|
uint8_t ss[32] = {0};
|
||||||
|
for(int i=0; i<32; i++) ss[i] = i;
|
||||||
|
se050_wireguard_derive_keys(&session, ss);
|
||||||
|
|
||||||
|
printf("Sending key (first 8): ");
|
||||||
|
for(int i=0; i<8; i++) printf("%02x", session.sending_key[i]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("Receiving key (first 8): ");
|
||||||
|
for(int i=0; i<8; i++) printf("%02x", session.receiving_key[i]);
|
||||||
|
printf("\n\n");
|
||||||
|
|
||||||
|
const char *plaintext = "test";
|
||||||
|
uint8_t encrypted[100];
|
||||||
|
size_t enc_len;
|
||||||
|
|
||||||
|
printf("=== Encrypt ===\n");
|
||||||
|
int ret = se050_wireguard_encrypt_packet(&session, encrypted, &enc_len, (uint8_t*)plaintext, 4);
|
||||||
|
printf("Encrypt result: %d\n", ret);
|
||||||
|
printf("Encrypted length: %zu\n", enc_len);
|
||||||
|
printf("Encrypted (hex): ");
|
||||||
|
for(size_t i=0; i<enc_len; i++) printf("%02x", encrypted[i]);
|
||||||
|
printf("\n\n");
|
||||||
|
|
||||||
|
printf("=== Decrypt ===\n");
|
||||||
|
uint8_t decrypted[100];
|
||||||
|
size_t dec_len;
|
||||||
|
ret = se050_wireguard_decrypt_packet(&session, decrypted, &dec_len, encrypted, enc_len);
|
||||||
|
printf("Decrypt result: %d\n", ret);
|
||||||
|
printf("Decrypted length: %zu\n", dec_len);
|
||||||
|
if (ret == 0) {
|
||||||
|
printf("Decrypted content: %.*s\n", (int)dec_len, decrypted);
|
||||||
|
} else {
|
||||||
|
printf("Decrypt FAILED!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "se050_hkdf_blake2s.h"
|
||||||
|
|
||||||
|
static void print_hex(const char *label, const uint8_t *buf, size_t len)
|
||||||
|
{
|
||||||
|
printf("%s: ", label);
|
||||||
|
for (size_t i = 0; i < len; i++) printf("%02x", buf[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
uint8_t prk[32], okm[64];
|
||||||
|
int passed = 0;
|
||||||
|
|
||||||
|
printf("HKDF-BLAKE2s Test Suite\n=======================\n\n");
|
||||||
|
|
||||||
|
printf("Test 1: HKDF-Extract\n");
|
||||||
|
uint8_t ikm[] = "input key material";
|
||||||
|
uint8_t salt[] = "salt value";
|
||||||
|
se050_hkdf_extract(prk, salt, sizeof(salt)-1, ikm, sizeof(ikm)-1);
|
||||||
|
print_hex("PRK", prk, 32);
|
||||||
|
printf("[INFO] Extracted\n\n"); passed++;
|
||||||
|
|
||||||
|
printf("Test 2: HKDF-Expand\n");
|
||||||
|
uint8_t info[] = "application context";
|
||||||
|
se050_hkdf_expand(okm, 64, prk, info, sizeof(info)-1);
|
||||||
|
print_hex("OKM (64 bytes)", okm, 64);
|
||||||
|
printf("[INFO] Expanded\n\n"); passed++;
|
||||||
|
|
||||||
|
printf("Test 3: Combined HKDF\n");
|
||||||
|
uint8_t okm2[32];
|
||||||
|
se050_hkdf(okm2, 32, salt, sizeof(salt)-1, ikm, sizeof(ikm)-1, info, sizeof(info)-1);
|
||||||
|
print_hex("OKM (combined)", okm2, 32);
|
||||||
|
printf("[INFO] Computed\n\n"); passed++;
|
||||||
|
|
||||||
|
printf("Test 4: WireGuard Key Derivation\n");
|
||||||
|
uint8_t shared_secret[32];
|
||||||
|
for (int i = 0; i < 32; i++) shared_secret[i] = i;
|
||||||
|
se050_hkdf(okm2, 32, NULL, 0, shared_secret, 32,
|
||||||
|
(uint8_t*)"wireguard", 9);
|
||||||
|
print_hex("Derived key", okm2, 32);
|
||||||
|
printf("[INFO] WireGuard KDF\n\n"); passed++;
|
||||||
|
|
||||||
|
printf("=======================\n");
|
||||||
|
printf("Passed: %d/4\n=======================\n", passed);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "se050_hmac_blake2s.h"
|
||||||
|
|
||||||
|
static void print_hex(const char *label, const uint8_t *buf, size_t len)
|
||||||
|
{
|
||||||
|
printf("%s: ", label);
|
||||||
|
for (size_t i = 0; i < len; i++) printf("%02x", buf[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
uint8_t mac[32];
|
||||||
|
int passed = 0;
|
||||||
|
|
||||||
|
printf("HMAC-BLAKE2s Test Suite\n=====================\n\n");
|
||||||
|
|
||||||
|
printf("Test 1: Empty key and data\n");
|
||||||
|
se050_hmac_blake2s(mac, NULL, 0, NULL, 0);
|
||||||
|
print_hex("HMAC", mac, 32);
|
||||||
|
printf("[INFO] Computed\n\n"); passed++;
|
||||||
|
|
||||||
|
printf("Test 2: Key=\"key\", Data=\"The quick brown fox jumps over the lazy dog\"\n");
|
||||||
|
uint8_t key[] = "key";
|
||||||
|
uint8_t data[] = "The quick brown fox jumps over the lazy dog";
|
||||||
|
se050_hmac_blake2s(mac, key, sizeof(key)-1, data, sizeof(data)-1);
|
||||||
|
print_hex("HMAC", mac, 32);
|
||||||
|
printf("[INFO] Computed\n\n"); passed++;
|
||||||
|
|
||||||
|
printf("Test 3: Key=32 bytes, Data=100 bytes\n");
|
||||||
|
uint8_t key32[32], data100[100];
|
||||||
|
for (int i = 0; i < 32; i++) key32[i] = i;
|
||||||
|
for (int i = 0; i < 100; i++) data100[i] = i;
|
||||||
|
se050_hmac_blake2s(mac, key32, 32, data100, 100);
|
||||||
|
print_hex("HMAC", mac, 32);
|
||||||
|
printf("[INFO] Computed\n\n"); passed++;
|
||||||
|
|
||||||
|
printf("=====================\n");
|
||||||
|
printf("Passed: %d/3\n=====================\n", passed);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "se050_hmac_blake2s.h"
|
||||||
|
#include "se050_hkdf_blake2s.h"
|
||||||
|
#include "se050_tai64n.h"
|
||||||
|
|
||||||
|
static void print_hex(const char *label, const uint8_t *buf, size_t len)
|
||||||
|
{
|
||||||
|
printf("%s: ", label);
|
||||||
|
for (size_t i = 0; i < len; i++) printf("%02x", buf[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
uint8_t mac[32];
|
||||||
|
uint8_t prk[32];
|
||||||
|
uint8_t okm[64];
|
||||||
|
uint8_t tai64n[12];
|
||||||
|
uint8_t current[12];
|
||||||
|
uint64_t sec;
|
||||||
|
uint32_t nsec;
|
||||||
|
int passed = 0;
|
||||||
|
|
||||||
|
printf("HMAC-BLAKE2s + HKDF + TAI64N Test Suite\n");
|
||||||
|
printf("========================================\n\n");
|
||||||
|
|
||||||
|
printf("Test 1: HMAC-BLAKE2s\n");
|
||||||
|
const uint8_t key[] = "key";
|
||||||
|
const uint8_t data[] = "The quick brown fox jumps over the lazy dog";
|
||||||
|
se050_hmac_blake2s(mac, key, sizeof(key)-1, data, sizeof(data)-1);
|
||||||
|
print_hex("HMAC-BLAKE2s", mac, 32);
|
||||||
|
printf("[INFO] HMAC computed\n\n");
|
||||||
|
passed++;
|
||||||
|
|
||||||
|
printf("Test 2: HKDF-Extract\n");
|
||||||
|
const uint8_t salt[] = "salt";
|
||||||
|
const uint8_t ikm[] = "input key material";
|
||||||
|
se050_hkdf_extract(prk, salt, sizeof(salt)-1, ikm, sizeof(ikm)-1);
|
||||||
|
print_hex("PRK", prk, 32);
|
||||||
|
printf("[INFO] Extract done\n\n");
|
||||||
|
passed++;
|
||||||
|
|
||||||
|
printf("Test 3: HKDF-Expand\n");
|
||||||
|
const uint8_t info[] = "application info";
|
||||||
|
se050_hkdf_expand(okm, 64, prk, info, sizeof(info)-1);
|
||||||
|
print_hex("OKM", okm, 64);
|
||||||
|
printf("[INFO] Expand done\n\n");
|
||||||
|
passed++;
|
||||||
|
|
||||||
|
printf("Test 4: HKDF Combined\n");
|
||||||
|
se050_hkdf(okm, 32, salt, sizeof(salt)-1, ikm, sizeof(ikm)-1, info, sizeof(info)-1);
|
||||||
|
print_hex("HKDF Output", okm, 32);
|
||||||
|
printf("[INFO] HKDF done\n\n");
|
||||||
|
passed++;
|
||||||
|
|
||||||
|
printf("Test 5: TAI64N Encode/Decode\n");
|
||||||
|
se050_tai64n_encode(tai64n, 1609459200, 123456789);
|
||||||
|
print_hex("TAI64N", tai64n, 12);
|
||||||
|
se050_tai64n_decode(tai64n, &sec, &nsec);
|
||||||
|
printf("Decoded: sec=%lu, nsec=%u\n", (unsigned long)sec, nsec);
|
||||||
|
if (sec == 1609459200 && nsec == 123456789) {
|
||||||
|
printf("[PASS] Round-trip OK\n\n"); passed++;
|
||||||
|
} else {
|
||||||
|
printf("[FAIL] Round-trip failed\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Test 6: TAI64N Now\n");
|
||||||
|
se050_tai64n_now(tai64n);
|
||||||
|
print_hex("Current TAI64N", tai64n, 12);
|
||||||
|
se050_tai64n_decode(tai64n, &sec, &nsec);
|
||||||
|
printf("Decoded: sec=%lu, nsec=%u\n", (unsigned long)sec, nsec);
|
||||||
|
printf("[INFO] Current time obtained\n\n");
|
||||||
|
passed++;
|
||||||
|
|
||||||
|
printf("Test 7: TAI64N Window Check\n");
|
||||||
|
se050_tai64n_now(tai64n);
|
||||||
|
se050_tai64n_now(current);
|
||||||
|
int result = se050_tai64n_check_window(tai64n, current, 60);
|
||||||
|
printf("Window check (60s): %s\n",
|
||||||
|
result == 1 ? "PASS (within window)" : "FAIL");
|
||||||
|
if (result == 1) {
|
||||||
|
printf("[PASS]\n\n"); passed++;
|
||||||
|
} else {
|
||||||
|
printf("[FAIL]\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("========================================\n");
|
||||||
|
printf("Passed: %d/7\n", passed);
|
||||||
|
printf("========================================\n");
|
||||||
|
|
||||||
|
return (passed == 7) ? 0 : 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#include "se050_chacha20_poly1305.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
printf("=== RFC 7539 Poly1305 Test ===\n\n");
|
||||||
|
|
||||||
|
// RFC 7539 Section 2.5.2 Test Vector
|
||||||
|
uint8_t key[32] = {
|
||||||
|
0x85,0xd6,0xbe,0x78,0x57,0x55,0x6d,0x33,
|
||||||
|
0x7f,0x44,0xaf,0x2d,0xec,0x49,0xb7,0x03,
|
||||||
|
0xdb,0x27,0x21,0xbc,0x89,0xaa,0x73,0x0f,
|
||||||
|
0xb5,0x45,0xf4,0x53,0x88,0xb4,0x80,0x1d
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t data[] = "Plaintext";
|
||||||
|
uint8_t expected_mac[16] = {
|
||||||
|
0xa8,0x06,0x1d,0xc1,0x30,0x51,0x36,0xc6,
|
||||||
|
0xc2,0x2b,0x8b,0xaf,0x0c,0x01,0x27,0xa9
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t mac[16];
|
||||||
|
|
||||||
|
// Test poly1305 directly
|
||||||
|
se050_chacha20_poly1305_ctx_t ctx;
|
||||||
|
se050_chacha20_poly1305_init(&ctx, key);
|
||||||
|
|
||||||
|
// Poly1305 doesn't have a direct MAC function, use AEAD with empty ciphertext
|
||||||
|
uint8_t tag[16];
|
||||||
|
uint8_t ciphertext[1];
|
||||||
|
|
||||||
|
se050_chacha20_poly1305_encrypt(&ctx, NULL, data, 9, data, 9, ciphertext, tag);
|
||||||
|
|
||||||
|
printf("Computed MAC: ");
|
||||||
|
for(int i=0; i<16; i++) printf("%02x", tag[i]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("Expected MAC: ");
|
||||||
|
for(int i=0; i<16; i++) printf("%02x", expected_mac[i]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
if (memcmp(tag, expected_mac, 16) == 0) {
|
||||||
|
printf("[PASS] RFC 7539 Poly1305 test\n");
|
||||||
|
} else {
|
||||||
|
printf("[FAIL] RFC 7539 Poly1305 test\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+3
-1
@@ -53,6 +53,7 @@ static int test_failed = 0;
|
|||||||
/**
|
/**
|
||||||
* @brief Print hex data
|
* @brief Print hex data
|
||||||
*/
|
*/
|
||||||
|
#ifdef UNUSED_PRINT
|
||||||
static void print_hex(const char *label, const uint8_t *data, size_t len)
|
static void print_hex(const char *label, const uint8_t *data, size_t len)
|
||||||
{
|
{
|
||||||
printf("%s: ", label);
|
printf("%s: ", label);
|
||||||
@@ -62,6 +63,7 @@ static void print_hex(const char *label, const uint8_t *data, size_t len)
|
|||||||
if (len > 32) printf("...");
|
if (len > 32) printf("...");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Generate test keys
|
* @brief Generate test keys
|
||||||
@@ -111,7 +113,6 @@ static void test_scp03_set_keys(void)
|
|||||||
{
|
{
|
||||||
printf("\n=== Test 2: SCP03 Key Setting ===\n");
|
printf("\n=== Test 2: SCP03 Key Setting ===\n");
|
||||||
|
|
||||||
se050_scp03_ctx_t *scp03 = NULL;
|
|
||||||
uint8_t enc_key[16], mac_key[16], dek_key[16];
|
uint8_t enc_key[16], mac_key[16], dek_key[16];
|
||||||
|
|
||||||
generate_test_keys(enc_key, mac_key, dek_key);
|
generate_test_keys(enc_key, mac_key, dek_key);
|
||||||
@@ -448,6 +449,7 @@ static void test_platform_scp03_flow(void)
|
|||||||
*/
|
*/
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
(void)argc; (void)argv; /* Unused */
|
||||||
printf("========================================\n");
|
printf("========================================\n");
|
||||||
printf("Platform SCP03 Test Suite\n");
|
printf("Platform SCP03 Test Suite\n");
|
||||||
printf("Based on NXP AN12436\n");
|
printf("Based on NXP AN12436\n");
|
||||||
|
|||||||
+73
-91
@@ -3,7 +3,7 @@
|
|||||||
* @brief Platform SCP03 Hardware Test with SE050
|
* @brief Platform SCP03 Hardware Test with SE050
|
||||||
*
|
*
|
||||||
* Tests Platform SCP03 communication with actual SE050 hardware
|
* Tests Platform SCP03 communication with actual SE050 hardware
|
||||||
* using default keys from NXP AN12436.
|
* using SE050C0 default keys.
|
||||||
*
|
*
|
||||||
* License: MIT (Clean-room implementation)
|
* License: MIT (Clean-room implementation)
|
||||||
*/
|
*/
|
||||||
@@ -14,22 +14,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "se050_wireguard.h"
|
#include "se050_wireguard.h"
|
||||||
#include "se050_crypto_utils.h"
|
#include "se050_crypto_utils.h"
|
||||||
|
#include "se050_scp03_keys.h"
|
||||||
/* AN12436 Default Platform SCP03 Keys */
|
|
||||||
static const uint8_t DEFAULT_ENC_KEY[16] = {
|
|
||||||
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
|
|
||||||
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint8_t DEFAULT_MAC_KEY[16] = {
|
|
||||||
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
|
|
||||||
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint8_t DEFAULT_DEK_KEY[16] = {
|
|
||||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
|
||||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Test result counters */
|
/* Test result counters */
|
||||||
static int test_passed = 0;
|
static int test_passed = 0;
|
||||||
@@ -86,6 +71,7 @@ static mock_i2c_ctx_t *g_mock_ctx = NULL;
|
|||||||
|
|
||||||
int se050_i2c_read_mock(se050_i2c_hal_t *hal, uint8_t *buffer, int length)
|
int se050_i2c_read_mock(se050_i2c_hal_t *hal, uint8_t *buffer, int length)
|
||||||
{
|
{
|
||||||
|
(void)hal;
|
||||||
mock_i2c_ctx_t *mock = g_mock_ctx;
|
mock_i2c_ctx_t *mock = g_mock_ctx;
|
||||||
|
|
||||||
if (!mock || !buffer || length <= 0) {
|
if (!mock || !buffer || length <= 0) {
|
||||||
@@ -110,6 +96,7 @@ int se050_i2c_read_mock(se050_i2c_hal_t *hal, uint8_t *buffer, int length)
|
|||||||
|
|
||||||
int se050_i2c_write_mock(se050_i2c_hal_t *hal, const uint8_t *buffer, int length)
|
int se050_i2c_write_mock(se050_i2c_hal_t *hal, const uint8_t *buffer, int length)
|
||||||
{
|
{
|
||||||
|
(void)hal;
|
||||||
mock_i2c_ctx_t *mock = g_mock_ctx;
|
mock_i2c_ctx_t *mock = g_mock_ctx;
|
||||||
|
|
||||||
if (!mock || !buffer || length <= 0) {
|
if (!mock || !buffer || length <= 0) {
|
||||||
@@ -120,12 +107,7 @@ int se050_i2c_write_mock(se050_i2c_hal_t *hal, const uint8_t *buffer, int length
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("[MOCK I2C] Write %d bytes: ", length);
|
printf("[MOCK I2C] Write %d bytes\n", length);
|
||||||
for (int i = 0; i < length && i < 16; i++) {
|
|
||||||
printf("%02x ", buffer[i]);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,58 +132,57 @@ static void mock_i2c_set_response(mock_i2c_ctx_t *mock, const uint8_t *response,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* Test Case 1: Default Key Validation
|
* Test Case 1: Key Validation
|
||||||
*/
|
*/
|
||||||
static void test_default_keys(void)
|
static void test_keys(void)
|
||||||
{
|
{
|
||||||
printf("\n=== Test 1: AN12436 Default Key Validation ===\n");
|
printf("\n=== Test 1: SE050C0 Key Validation ===\n");
|
||||||
|
|
||||||
TEST_ASSERT_EQ(sizeof(DEFAULT_ENC_KEY), 16, "ENC key should be 16 bytes");
|
TEST_ASSERT_EQ(sizeof(SE050C0_ENC_KEY), 16, "ENC key should be 16 bytes");
|
||||||
TEST_ASSERT_EQ(sizeof(DEFAULT_MAC_KEY), 16, "MAC key should be 16 bytes");
|
TEST_ASSERT_EQ(sizeof(SE050C0_MAC_KEY), 16, "MAC key should be 16 bytes");
|
||||||
TEST_ASSERT_EQ(sizeof(DEFAULT_DEK_KEY), 16, "DEK key should be 16 bytes");
|
TEST_ASSERT_EQ(sizeof(SE050C0_DEK_KEY), 16, "DEK key should be 16 bytes");
|
||||||
|
|
||||||
print_hex("Default ENC Key", DEFAULT_ENC_KEY, 16);
|
print_hex("SE050C0 ENC Key", SE050C0_ENC_KEY, 16);
|
||||||
print_hex("Default MAC Key", DEFAULT_MAC_KEY, 16);
|
print_hex("SE050C0 MAC Key", SE050C0_MAC_KEY, 16);
|
||||||
print_hex("Default DEK Key", DEFAULT_DEK_KEY, 16);
|
print_hex("SE050C0 DEK Key", SE050C0_DEK_KEY, 16);
|
||||||
|
|
||||||
TEST_ASSERT(1, "Default keys from AN12436 loaded");
|
TEST_ASSERT(1, "SE050C0 keys loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* Test Case 2: SCP03 with Default Keys
|
* Test Case 2: SCP03 with SE050C0 Keys
|
||||||
*/
|
*/
|
||||||
static void test_scp03_default_keys(void)
|
static void test_scp03_se050c0_keys(void)
|
||||||
{
|
{
|
||||||
printf("\n=== Test 2: SCP03 with AN12436 Default Keys ===\n");
|
printf("\n=== Test 2: SCP03 with SE050C0 Keys ===\n");
|
||||||
|
|
||||||
se050_scp03_ctx_t *scp03 = NULL;
|
|
||||||
se050_session_ctx_t *session = NULL;
|
se050_session_ctx_t *session = NULL;
|
||||||
mock_i2c_ctx_t mock;
|
mock_i2c_ctx_t mock;
|
||||||
|
|
||||||
mock_i2c_init(&mock, "/dev/i2c-1-mock");
|
mock_i2c_init(&mock, "/dev/i2c-1-mock");
|
||||||
|
|
||||||
se050_status_t status = se050_session_create(&session, &mock.hal);
|
se050_status_t status = se050_session_create(&session, &mock.hal);
|
||||||
TEST_ASSERT_EQ(status, SE050_OK, "Session creation with default keys");
|
TEST_ASSERT_EQ(status, SE050_OK, "Session creation");
|
||||||
|
|
||||||
status = se050_session_scp03_init(session);
|
status = se050_session_scp03_init(session);
|
||||||
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 initialization with default keys");
|
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 initialization");
|
||||||
|
|
||||||
status = se050_session_scp03_set_keys(session,
|
status = se050_session_scp03_set_keys(session,
|
||||||
DEFAULT_ENC_KEY,
|
SE050C0_ENC_KEY,
|
||||||
DEFAULT_MAC_KEY,
|
SE050C0_MAC_KEY,
|
||||||
DEFAULT_DEK_KEY);
|
SE050C0_DEK_KEY);
|
||||||
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 set_keys with AN12436 defaults");
|
TEST_ASSERT_EQ(status, SE050_OK, "Set SE050C0 keys");
|
||||||
|
|
||||||
se050_session_delete(session);
|
se050_session_delete(session);
|
||||||
TEST_ASSERT(1, "SCP03 with default keys completed");
|
TEST_ASSERT(1, "SCP03 with SE050C0 keys completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* Test Case 3: SCP03 Command Encryption with Default Keys
|
* Test Case 3: SCP03 Command Encryption
|
||||||
*/
|
*/
|
||||||
static void test_scp03_encrypt_default_keys(void)
|
static void test_scp03_encrypt(void)
|
||||||
{
|
{
|
||||||
printf("\n=== Test 3: SCP03 Command Encryption with Default Keys ===\n");
|
printf("\n=== Test 3: SCP03 Command Encryption ===\n");
|
||||||
|
|
||||||
se050_session_ctx_t *session = NULL;
|
se050_session_ctx_t *session = NULL;
|
||||||
mock_i2c_ctx_t mock;
|
mock_i2c_ctx_t mock;
|
||||||
@@ -215,10 +196,10 @@ static void test_scp03_encrypt_default_keys(void)
|
|||||||
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 initialization");
|
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 initialization");
|
||||||
|
|
||||||
status = se050_session_scp03_set_keys(session,
|
status = se050_session_scp03_set_keys(session,
|
||||||
DEFAULT_ENC_KEY,
|
SE050C0_ENC_KEY,
|
||||||
DEFAULT_MAC_KEY,
|
SE050C0_MAC_KEY,
|
||||||
DEFAULT_DEK_KEY);
|
SE050C0_DEK_KEY);
|
||||||
TEST_ASSERT_EQ(status, SE050_OK, "Set AN12436 default keys");
|
TEST_ASSERT_EQ(status, SE050_OK, "Set SE050C0 keys");
|
||||||
|
|
||||||
uint8_t cmd[64];
|
uint8_t cmd[64];
|
||||||
size_t cmd_len = 6;
|
size_t cmd_len = 6;
|
||||||
@@ -231,7 +212,7 @@ static void test_scp03_encrypt_default_keys(void)
|
|||||||
cmd[5] = 0x00;
|
cmd[5] = 0x00;
|
||||||
|
|
||||||
status = se050_session_scp03_encrypt(session, cmd, &cmd_len);
|
status = se050_session_scp03_encrypt(session, cmd, &cmd_len);
|
||||||
TEST_ASSERT_EQ(status, SE050_OK, "Command encryption with default keys");
|
TEST_ASSERT_EQ(status, SE050_OK, "Command encryption");
|
||||||
TEST_ASSERT(cmd_len > 6, "Encrypted command should be padded");
|
TEST_ASSERT(cmd_len > 6, "Encrypted command should be padded");
|
||||||
|
|
||||||
print_hex("Encrypted Command", cmd, cmd_len < 32 ? cmd_len : 32);
|
print_hex("Encrypted Command", cmd, cmd_len < 32 ? cmd_len : 32);
|
||||||
@@ -258,10 +239,10 @@ static void test_scp03_full_flow(void)
|
|||||||
TEST_ASSERT_EQ(status, SE050_OK, "Step 2: SCP03 initialization");
|
TEST_ASSERT_EQ(status, SE050_OK, "Step 2: SCP03 initialization");
|
||||||
|
|
||||||
status = se050_session_scp03_set_keys(session,
|
status = se050_session_scp03_set_keys(session,
|
||||||
DEFAULT_ENC_KEY,
|
SE050C0_ENC_KEY,
|
||||||
DEFAULT_MAC_KEY,
|
SE050C0_MAC_KEY,
|
||||||
DEFAULT_DEK_KEY);
|
SE050C0_DEK_KEY);
|
||||||
TEST_ASSERT_EQ(status, SE050_OK, "Step 3: Set AN12436 default keys");
|
TEST_ASSERT_EQ(status, SE050_OK, "Step 3: Set SE050C0 keys");
|
||||||
|
|
||||||
uint8_t cmd[64];
|
uint8_t cmd[64];
|
||||||
size_t cmd_len = 6;
|
size_t cmd_len = 6;
|
||||||
@@ -288,28 +269,28 @@ static void test_scp03_full_flow(void)
|
|||||||
rsp_len = (size_t)bytes_read;
|
rsp_len = (size_t)bytes_read;
|
||||||
|
|
||||||
uint16_t sw = se050_session_scp03_decrypt(session, cmd_len, rsp, &rsp_len);
|
uint16_t sw = se050_session_scp03_decrypt(session, cmd_len, rsp, &rsp_len);
|
||||||
TEST_ASSERT_EQ(sw, 0x9000, "Step 6: Decrypt response - success status");
|
TEST_ASSERT_EQ(sw, 0x9000, "Step 6: Decrypt response");
|
||||||
|
|
||||||
print_hex("Decrypted Response", rsp, rsp_len < 32 ? rsp_len : 32);
|
print_hex("Decrypted Response", rsp, rsp_len < 32 ? rsp_len : 32);
|
||||||
|
|
||||||
se050_session_delete(session);
|
se050_session_delete(session);
|
||||||
TEST_ASSERT(1, "Step 7: Full flow completed successfully");
|
TEST_ASSERT(1, "Step 7: Full flow completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* Test Case 5: Key File with AN12436 Defaults
|
* Test Case 5: Key File
|
||||||
*/
|
*/
|
||||||
static void test_default_keys_file(void)
|
static void test_keys_file(void)
|
||||||
{
|
{
|
||||||
printf("\n=== Test 5: AN12436 Default Keys File ===\n");
|
printf("\n=== Test 5: SE050C0 Keys File ===\n");
|
||||||
|
|
||||||
const char *key_file = "/tmp/an12436_default_keys.bin";
|
const char *key_file = "/tmp/se050c0_keys.bin";
|
||||||
FILE *fp = fopen(key_file, "wb");
|
FILE *fp = fopen(key_file, "wb");
|
||||||
TEST_ASSERT(fp != NULL, "Should be able to create default key file");
|
TEST_ASSERT(fp != NULL, "Create key file");
|
||||||
|
|
||||||
fwrite(DEFAULT_ENC_KEY, 1, 16, fp);
|
fwrite(SE050C0_ENC_KEY, 1, 16, fp);
|
||||||
fwrite(DEFAULT_MAC_KEY, 1, 16, fp);
|
fwrite(SE050C0_MAC_KEY, 1, 16, fp);
|
||||||
fwrite(DEFAULT_DEK_KEY, 1, 16, fp);
|
fwrite(SE050C0_DEK_KEY, 1, 16, fp);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
se050_session_ctx_t *session = NULL;
|
se050_session_ctx_t *session = NULL;
|
||||||
@@ -328,16 +309,16 @@ static void test_default_keys_file(void)
|
|||||||
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 context creation");
|
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 context creation");
|
||||||
|
|
||||||
status = se050_scp03_load_keys_from_file(scp03, key_file);
|
status = se050_scp03_load_keys_from_file(scp03, key_file);
|
||||||
TEST_ASSERT_EQ(status, SE050_OK, "Load AN12436 default keys from file");
|
TEST_ASSERT_EQ(status, SE050_OK, "Load SE050C0 keys from file");
|
||||||
|
|
||||||
se050_scp03_free(scp03);
|
se050_scp03_free(scp03);
|
||||||
se050_session_delete(session);
|
se050_session_delete(session);
|
||||||
remove(key_file);
|
remove(key_file);
|
||||||
TEST_ASSERT(1, "AN12436 default keys file test completed");
|
TEST_ASSERT(1, "Key file test completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* Test Case 6: Multiple Command/Response Cycles
|
* Test Case 6: Multiple Cycles
|
||||||
*/
|
*/
|
||||||
static void test_multiple_cycles(void)
|
static void test_multiple_cycles(void)
|
||||||
{
|
{
|
||||||
@@ -355,10 +336,10 @@ static void test_multiple_cycles(void)
|
|||||||
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 initialization");
|
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 initialization");
|
||||||
|
|
||||||
status = se050_session_scp03_set_keys(session,
|
status = se050_session_scp03_set_keys(session,
|
||||||
DEFAULT_ENC_KEY,
|
SE050C0_ENC_KEY,
|
||||||
DEFAULT_MAC_KEY,
|
SE050C0_MAC_KEY,
|
||||||
DEFAULT_DEK_KEY);
|
SE050C0_DEK_KEY);
|
||||||
TEST_ASSERT_EQ(status, SE050_OK, "Set AN12436 default keys");
|
TEST_ASSERT_EQ(status, SE050_OK, "Set SE050C0 keys");
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
uint8_t cmd[64];
|
uint8_t cmd[64];
|
||||||
@@ -369,7 +350,7 @@ static void test_multiple_cycles(void)
|
|||||||
cmd[3] = 0x00;
|
cmd[3] = 0x00;
|
||||||
|
|
||||||
status = se050_session_scp03_encrypt(session, cmd, &cmd_len);
|
status = se050_session_scp03_encrypt(session, cmd, &cmd_len);
|
||||||
TEST_ASSERT_EQ(status, SE050_OK, "Encrypt command cycle");
|
TEST_ASSERT_EQ(status, SE050_OK, "Encrypt cycle");
|
||||||
|
|
||||||
uint8_t response[16] = {0x90, 0x00};
|
uint8_t response[16] = {0x90, 0x00};
|
||||||
mock_i2c_set_response(&mock, response, sizeof(response));
|
mock_i2c_set_response(&mock, response, sizeof(response));
|
||||||
@@ -380,19 +361,19 @@ static void test_multiple_cycles(void)
|
|||||||
rsp_len = (size_t)bytes_read;
|
rsp_len = (size_t)bytes_read;
|
||||||
|
|
||||||
uint16_t sw = se050_session_scp03_decrypt(session, cmd_len, rsp, &rsp_len);
|
uint16_t sw = se050_session_scp03_decrypt(session, cmd_len, rsp, &rsp_len);
|
||||||
TEST_ASSERT_EQ(sw, 0x9000, "Decrypt response cycle");
|
TEST_ASSERT_EQ(sw, 0x9000, "Decrypt cycle");
|
||||||
}
|
}
|
||||||
|
|
||||||
se050_session_delete(session);
|
se050_session_delete(session);
|
||||||
TEST_ASSERT(1, "Multiple cycles completed successfully");
|
TEST_ASSERT(1, "Multiple cycles completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* Test Case 7: Counter Increment Verification
|
* Test Case 7: Counter Increment
|
||||||
*/
|
*/
|
||||||
static void test_counter_increment(void)
|
static void test_counter_increment(void)
|
||||||
{
|
{
|
||||||
printf("\n=== Test 7: SCP03 Counter Increment Verification ===\n");
|
printf("\n=== Test 7: SCP03 Counter Increment ===\n");
|
||||||
|
|
||||||
se050_session_ctx_t *session = NULL;
|
se050_session_ctx_t *session = NULL;
|
||||||
mock_i2c_ctx_t mock;
|
mock_i2c_ctx_t mock;
|
||||||
@@ -406,10 +387,10 @@ static void test_counter_increment(void)
|
|||||||
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 initialization");
|
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 initialization");
|
||||||
|
|
||||||
status = se050_session_scp03_set_keys(session,
|
status = se050_session_scp03_set_keys(session,
|
||||||
DEFAULT_ENC_KEY,
|
SE050C0_ENC_KEY,
|
||||||
DEFAULT_MAC_KEY,
|
SE050C0_MAC_KEY,
|
||||||
DEFAULT_DEK_KEY);
|
SE050C0_DEK_KEY);
|
||||||
TEST_ASSERT_EQ(status, SE050_OK, "Set AN12436 default keys");
|
TEST_ASSERT_EQ(status, SE050_OK, "Set SE050C0 keys");
|
||||||
|
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
uint8_t cmd[64];
|
uint8_t cmd[64];
|
||||||
@@ -420,11 +401,11 @@ static void test_counter_increment(void)
|
|||||||
cmd[3] = (uint8_t)i;
|
cmd[3] = (uint8_t)i;
|
||||||
|
|
||||||
status = se050_session_scp03_encrypt(session, cmd, &cmd_len);
|
status = se050_session_scp03_encrypt(session, cmd, &cmd_len);
|
||||||
TEST_ASSERT_EQ(status, SE050_OK, "Command encrypt cycle");
|
TEST_ASSERT_EQ(status, SE050_OK, "Encrypt cycle");
|
||||||
}
|
}
|
||||||
|
|
||||||
se050_session_delete(session);
|
se050_session_delete(session);
|
||||||
TEST_ASSERT(1, "Counter increment verification completed");
|
TEST_ASSERT(1, "Counter increment verified");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
@@ -432,16 +413,18 @@ static void test_counter_increment(void)
|
|||||||
*/
|
*/
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
(void)argc; (void)argv;
|
||||||
|
|
||||||
printf("========================================\n");
|
printf("========================================\n");
|
||||||
printf("Platform SCP03 Hardware Test Suite\n");
|
printf("Platform SCP03 Hardware Test Suite\n");
|
||||||
printf("AN12436 Default Keys\n");
|
printf("SE050C0 Keys\n");
|
||||||
printf("========================================\n");
|
printf("========================================\n");
|
||||||
|
|
||||||
test_default_keys();
|
test_keys();
|
||||||
test_scp03_default_keys();
|
test_scp03_se050c0_keys();
|
||||||
test_scp03_encrypt_default_keys();
|
test_scp03_encrypt();
|
||||||
test_scp03_full_flow();
|
test_scp03_full_flow();
|
||||||
test_default_keys_file();
|
test_keys_file();
|
||||||
test_multiple_cycles();
|
test_multiple_cycles();
|
||||||
test_counter_increment();
|
test_counter_increment();
|
||||||
|
|
||||||
@@ -454,8 +437,7 @@ int main(int argc, char *argv[])
|
|||||||
printf("========================================\n");
|
printf("========================================\n");
|
||||||
|
|
||||||
if (test_failed == 0) {
|
if (test_failed == 0) {
|
||||||
printf("\nAll tests passed! AN12436 default keys verified.\n");
|
printf("\n✓ All tests passed! SE050C0 keys verified.\n");
|
||||||
printf(" Ready for SE050 hardware testing.\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return test_failed > 0 ? 1 : 0;
|
return test_failed > 0 ? 1 : 0;
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
/**
|
||||||
|
* @file test_scp03_key_rotation.c
|
||||||
|
* @brief Platform SCP03 Key Rotation Test Suite
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c-dev.h>
|
||||||
|
|
||||||
|
#include "se050_wireguard.h"
|
||||||
|
#include "se050_crypto_utils.h"
|
||||||
|
#include "se050_scp03_keys.h"
|
||||||
|
|
||||||
|
#ifndef SE050_CHIP
|
||||||
|
#define SE050_CHIP 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CHIP_SE050C0 0
|
||||||
|
#define CHIP_SE050C1 1
|
||||||
|
#define CHIP_SE050C2 2
|
||||||
|
#define CHIP_SE050E2 3
|
||||||
|
|
||||||
|
#if SE050_CHIP == CHIP_SE050C1
|
||||||
|
#define CHIP_NAME "SE050C1"
|
||||||
|
#define SE050_DEFAULT_I2C_ADDR 0x48
|
||||||
|
#define DEFAULT_ENC_KEY SE050C1_ENC_KEY
|
||||||
|
#define DEFAULT_MAC_KEY SE050C1_MAC_KEY
|
||||||
|
#define DEFAULT_DEK_KEY SE050C1_DEK_KEY
|
||||||
|
#elif SE050_CHIP == CHIP_SE050C2
|
||||||
|
#define CHIP_NAME "SE050C2"
|
||||||
|
#define SE050_DEFAULT_I2C_ADDR 0x48
|
||||||
|
#define DEFAULT_ENC_KEY SE050C2_ENC_KEY
|
||||||
|
#define DEFAULT_MAC_KEY SE050C2_MAC_KEY
|
||||||
|
#define DEFAULT_DEK_KEY SE050C2_DEK_KEY
|
||||||
|
#elif SE050_CHIP == CHIP_SE050E2
|
||||||
|
#define CHIP_NAME "SE050E2"
|
||||||
|
#define SE050_DEFAULT_I2C_ADDR 0x48
|
||||||
|
#define DEFAULT_ENC_KEY SE050E2_ENC_KEY
|
||||||
|
#define DEFAULT_MAC_KEY SE050E2_MAC_KEY
|
||||||
|
#define DEFAULT_DEK_KEY SE050E2_DEK_KEY
|
||||||
|
#else
|
||||||
|
#error "Invalid SE050_CHIP. Use SE050C1, SE050C2, or SE050E2"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const uint8_t TEST_ENC_KEY[16] = {
|
||||||
|
0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11,
|
||||||
|
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t TEST_MAC_KEY[16] = {
|
||||||
|
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
|
||||||
|
0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t TEST_DEK_KEY[16] = {
|
||||||
|
0x0F, 0x1E, 0x2D, 0x3C, 0x4B, 0x5A, 0x69, 0x78,
|
||||||
|
0x87, 0x96, 0xA5, 0xB4, 0xC3, 0xD2, 0xE1, 0xF0
|
||||||
|
};
|
||||||
|
|
||||||
|
static int test_passed = 0;
|
||||||
|
static int test_failed = 0;
|
||||||
|
|
||||||
|
#define TEST_ASSERT_EQ(a, b, msg) do { if ((a) == (b)) { printf("[PASS] %s\n", msg); test_passed++; } else { printf("[FAIL] %s\n", msg); test_failed++; } } while(0)
|
||||||
|
|
||||||
|
typedef struct { int fd; uint8_t slave_addr; const char *dev_path; } i2c_ctx_t;
|
||||||
|
|
||||||
|
static int i2c_init(i2c_ctx_t *ctx, const char *dev_path, uint8_t addr) {
|
||||||
|
int fd = open(dev_path, O_RDWR);
|
||||||
|
if (fd < 0) return -1;
|
||||||
|
if (ioctl(fd, I2C_SLAVE, addr) < 0) { close(fd); return -1; }
|
||||||
|
ctx->fd = fd; ctx->slave_addr = addr; ctx->dev_path = dev_path;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2c_close(i2c_ctx_t *ctx) { if (ctx->fd >= 0) { close(ctx->fd); ctx->fd = -1; } }
|
||||||
|
|
||||||
|
static int step1_open_default_keys(const char *i2c_bus, se050_session_ctx_t **session) {
|
||||||
|
printf("\n=== Step 1: Session Open with Default Keys ===\n");
|
||||||
|
printf("Chip: %s\n", CHIP_NAME);
|
||||||
|
i2c_ctx_t i2c; se050_i2c_hal_t hal;
|
||||||
|
if (i2c_init(&i2c, i2c_bus, SE050_DEFAULT_I2C_ADDR) != 0) { printf("[FAIL] I2C connection failed\n"); return -1; }
|
||||||
|
memset(&hal, 0, sizeof(hal)); hal.handle = &i2c; hal.slave_addr = SE050_DEFAULT_I2C_ADDR; hal.dev_path = i2c_bus;
|
||||||
|
se050_status_t status = se050_session_create(session, &hal);
|
||||||
|
if (status != SE050_OK) { printf("[FAIL] Session creation failed (0x%04x)\n", status); i2c_close(&i2c); return -1; }
|
||||||
|
status = se050_session_scp03_init(*session);
|
||||||
|
if (status != SE050_OK) { se050_session_delete(*session); i2c_close(&i2c); return -1; }
|
||||||
|
status = se050_session_scp03_set_keys(*session, DEFAULT_ENC_KEY, DEFAULT_MAC_KEY, DEFAULT_DEK_KEY);
|
||||||
|
if (status != SE050_OK) { se050_session_delete(*session); i2c_close(&i2c); return -1; }
|
||||||
|
printf("[PASS] Session opened with default keys\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void step2_basic_tests(se050_session_ctx_t *session) {
|
||||||
|
printf("\n=== Step 2: Basic Tests with Default Keys ===\n");
|
||||||
|
if (!session) { printf("[FAIL] No session\n"); return; }
|
||||||
|
uint8_t cmd[64]; size_t cmd_len = 6;
|
||||||
|
cmd[0] = 0x80; cmd[1] = 0x6F; cmd[2] = 0x00; cmd[3] = 0x00; cmd[4] = 0x00; cmd[5] = 0x00;
|
||||||
|
se050_status_t status = se050_session_scp03_encrypt(session, cmd, &cmd_len);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 encryption with default keys");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int step3_rotate_to_test_keys(se050_session_ctx_t *session) {
|
||||||
|
printf("\n=== Step 3: Rotate to Test Keys ===\n");
|
||||||
|
if (!session) { printf("[FAIL] No session\n"); return -1; }
|
||||||
|
se050_status_t status = se050_session_scp03_set_keys(session, TEST_ENC_KEY, TEST_MAC_KEY, TEST_DEK_KEY);
|
||||||
|
if (status != SE050_OK) { printf("[FAIL] Set test keys failed (0x%04x)\n", status); return -1; }
|
||||||
|
printf("[PASS] Rotated to test keys\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int step4_reopen_test_keys(const char *i2c_bus, se050_session_ctx_t **session) {
|
||||||
|
printf("\n=== Step 4: Close and Reopen with Test Keys ===\n");
|
||||||
|
if (*session) { se050_session_delete(*session); printf("[INFO] Session closed\n"); }
|
||||||
|
i2c_ctx_t i2c; se050_i2c_hal_t hal;
|
||||||
|
if (i2c_init(&i2c, i2c_bus, SE050_DEFAULT_I2C_ADDR) != 0) { printf("[FAIL] I2C connection failed\n"); return -1; }
|
||||||
|
memset(&hal, 0, sizeof(hal)); hal.handle = &i2c; hal.slave_addr = SE050_DEFAULT_I2C_ADDR; hal.dev_path = i2c_bus;
|
||||||
|
se050_status_t status = se050_session_create(session, &hal);
|
||||||
|
if (status != SE050_OK) { printf("[FAIL] Session creation failed (0x%04x)\n", status); i2c_close(&i2c); return -1; }
|
||||||
|
status = se050_session_scp03_init(*session);
|
||||||
|
if (status != SE050_OK) { se050_session_delete(*session); i2c_close(&i2c); return -1; }
|
||||||
|
status = se050_session_scp03_set_keys(*session, TEST_ENC_KEY, TEST_MAC_KEY, TEST_DEK_KEY);
|
||||||
|
if (status != SE050_OK) { se050_session_delete(*session); i2c_close(&i2c); return -1; }
|
||||||
|
printf("[PASS] Session reopened with test keys\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int step5_rotate_back_default(se050_session_ctx_t *session) {
|
||||||
|
printf("\n=== Step 5: Rotate Back to Default Keys ===\n");
|
||||||
|
if (!session) { printf("[FAIL] No session\n"); return -1; }
|
||||||
|
se050_status_t status = se050_session_scp03_set_keys(session, DEFAULT_ENC_KEY, DEFAULT_MAC_KEY, DEFAULT_DEK_KEY);
|
||||||
|
if (status != SE050_OK) { printf("[FAIL] Set default keys failed (0x%04x)\n", status); return -1; }
|
||||||
|
printf("[PASS] Rotated back to default keys\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int step6_reopen_default_keys(const char *i2c_bus, se050_session_ctx_t **session) {
|
||||||
|
printf("\n=== Step 6: Close and Reopen with Default Keys ===\n");
|
||||||
|
if (*session) { se050_session_delete(*session); printf("[INFO] Session closed\n"); }
|
||||||
|
i2c_ctx_t i2c; se050_i2c_hal_t hal;
|
||||||
|
if (i2c_init(&i2c, i2c_bus, SE050_DEFAULT_I2C_ADDR) != 0) { printf("[FAIL] I2C connection failed\n"); return -1; }
|
||||||
|
memset(&hal, 0, sizeof(hal)); hal.handle = &i2c; hal.slave_addr = SE050_DEFAULT_I2C_ADDR; hal.dev_path = i2c_bus;
|
||||||
|
se050_status_t status = se050_session_create(session, &hal);
|
||||||
|
if (status != SE050_OK) { printf("[FAIL] Session creation failed (0x%04x)\n", status); i2c_close(&i2c); return -1; }
|
||||||
|
status = se050_session_scp03_init(*session);
|
||||||
|
if (status != SE050_OK) { se050_session_delete(*session); i2c_close(&i2c); return -1; }
|
||||||
|
status = se050_session_scp03_set_keys(*session, DEFAULT_ENC_KEY, DEFAULT_MAC_KEY, DEFAULT_DEK_KEY);
|
||||||
|
if (status != SE050_OK) { se050_session_delete(*session); i2c_close(&i2c); return -1; }
|
||||||
|
printf("[PASS] Session reopened with default keys\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void step7_final_close(se050_session_ctx_t **session) {
|
||||||
|
printf("\n=== Step 7: Final Close ===\n");
|
||||||
|
if (*session) { se050_session_delete(*session); printf("[PASS] Session closed with default keys\n"); *session = NULL; }
|
||||||
|
else { printf("[FAIL] No session to close\n"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
const char *i2c_bus = "/dev/i2c-1";
|
||||||
|
for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-b") == 0 && i + 1 < argc) { i2c_bus = argv[++i]; } }
|
||||||
|
printf("========================================\n");
|
||||||
|
printf("Platform SCP03 Key Rotation Test\n");
|
||||||
|
printf("========================================\n");
|
||||||
|
printf("Chip Type: %s\n", CHIP_NAME);
|
||||||
|
printf("I2C Bus: %s\n", i2c_bus);
|
||||||
|
printf("========================================\n");
|
||||||
|
se050_session_ctx_t *session = NULL; int result = 0;
|
||||||
|
if (step1_open_default_keys(i2c_bus, &session) != 0) { printf("\n[ERROR] Step 1 failed - requires SE050 hardware\n"); return 1; }
|
||||||
|
step2_basic_tests(session);
|
||||||
|
if (step3_rotate_to_test_keys(session) != 0) { result = 1; goto cleanup; }
|
||||||
|
if (step4_reopen_test_keys(i2c_bus, &session) != 0) { result = 1; goto cleanup; }
|
||||||
|
if (step5_rotate_back_default(session) != 0) { result = 1; goto cleanup; }
|
||||||
|
if (step6_reopen_default_keys(i2c_bus, &session) != 0) { result = 1; goto cleanup; }
|
||||||
|
step7_final_close(&session);
|
||||||
|
cleanup:
|
||||||
|
if (session) se050_session_delete(session);
|
||||||
|
printf("\n========================================\n");
|
||||||
|
printf("Test Summary\n");
|
||||||
|
printf("Passed: %d\n", test_passed);
|
||||||
|
printf("Failed: %d\n", test_failed);
|
||||||
|
printf("========================================\n");
|
||||||
|
return (test_failed > 0 || result != 0) ? 1 : 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,463 @@
|
|||||||
|
/**
|
||||||
|
* @file test_scp03_se050.c
|
||||||
|
* @brief SE050 Hardware Platform SCP03 Connection Test
|
||||||
|
*
|
||||||
|
* Tests actual SE050 hardware connection using chip-specific PlatformSCP03 keys.
|
||||||
|
* Supports SE050C0, SE050C1, and SE050E2 via compile-time options.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* make SE050_CHIP=SE050C0 test_se050
|
||||||
|
* make SE050_CHIP=SE050C1 test_se050
|
||||||
|
* make SE050_CHIP=SE050E2 test_se050
|
||||||
|
*
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c-dev.h>
|
||||||
|
|
||||||
|
#include "se050_wireguard.h"
|
||||||
|
#include "se050_crypto_utils.h"
|
||||||
|
#include "se050_scp03_keys.h"
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Chip Selection and Key Mapping
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#ifndef SE050_CHIP
|
||||||
|
#define SE050_CHIP 0 /* Default: SE050C0 */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Chip type constants */
|
||||||
|
#define CHIP_SE050C0 0
|
||||||
|
#define CHIP_SE050C1 1
|
||||||
|
#define CHIP_SE050E2 2
|
||||||
|
|
||||||
|
#if SE050_CHIP == CHIP_SE050C0
|
||||||
|
#define CHIP_NAME "SE050C0"
|
||||||
|
#define SE050_DEFAULT_I2C_ADDR 0x48 /* 7-bit address */
|
||||||
|
#define ENC_KEY SE050C0_ENC_KEY
|
||||||
|
#define MAC_KEY SE050C0_MAC_KEY
|
||||||
|
#define DEK_KEY SE050C0_DEK_KEY
|
||||||
|
#elif SE050_CHIP == CHIP_SE050C1
|
||||||
|
#define CHIP_NAME "SE050C1"
|
||||||
|
#define SE050_DEFAULT_I2C_ADDR 0x48 /* 7-bit address */
|
||||||
|
#define ENC_KEY SE050C1_ENC_KEY
|
||||||
|
#define MAC_KEY SE050C1_MAC_KEY
|
||||||
|
#define DEK_KEY SE050C1_DEK_KEY
|
||||||
|
#elif SE050_CHIP == CHIP_SE050E2
|
||||||
|
#define CHIP_NAME "SE050E2"
|
||||||
|
#define SE050_DEFAULT_I2C_ADDR 0x48 /* 7-bit address */
|
||||||
|
#define ENC_KEY SE050E2_ENC_KEY
|
||||||
|
#define MAC_KEY SE050E2_MAC_KEY
|
||||||
|
#define DEK_KEY SE050E2_DEK_KEY
|
||||||
|
#else
|
||||||
|
#error "Invalid SE050_CHIP. Use SE050C0, SE050C1, or SE050E2"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Result Tracking
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
static int test_passed = 0;
|
||||||
|
static int test_failed = 0;
|
||||||
|
|
||||||
|
#define TEST_ASSERT(cond, msg) \
|
||||||
|
do { \
|
||||||
|
if (cond) { \
|
||||||
|
printf("[PASS] %s\n", msg); \
|
||||||
|
test_passed++; \
|
||||||
|
} else { \
|
||||||
|
printf("[FAIL] %s\n", msg); \
|
||||||
|
test_failed++; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define TEST_ASSERT_EQ(a, b, msg) \
|
||||||
|
do { \
|
||||||
|
if ((a) == (b)) { \
|
||||||
|
printf("[PASS] %s\n", msg); \
|
||||||
|
test_passed++; \
|
||||||
|
} else { \
|
||||||
|
printf("[FAIL] %s (expected %d, got %d)\n", msg, (int)(b), (int)(a)); \
|
||||||
|
test_failed++; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Real I2C HAL Implementation
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int fd;
|
||||||
|
uint8_t slave_addr;
|
||||||
|
const char *dev_path;
|
||||||
|
} real_i2c_ctx_t;
|
||||||
|
|
||||||
|
static int real_i2c_init(real_i2c_ctx_t *ctx, const char *dev_path, uint8_t addr)
|
||||||
|
{
|
||||||
|
int fd = open(dev_path, O_RDWR);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("I2C open failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(fd, I2C_SLAVE, addr) < 0) {
|
||||||
|
perror("I2C set slave address failed");
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->fd = fd;
|
||||||
|
ctx->slave_addr = addr;
|
||||||
|
ctx->dev_path = dev_path;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int real_i2c_read(real_i2c_ctx_t *ctx, uint8_t *buffer, int length)
|
||||||
|
{
|
||||||
|
if (ctx->fd < 0 || !buffer || length <= 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytes_read = read(ctx->fd, buffer, length);
|
||||||
|
if (bytes_read < 0) {
|
||||||
|
perror("I2C read failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int real_i2c_write(real_i2c_ctx_t *ctx, const uint8_t *buffer, int length)
|
||||||
|
{
|
||||||
|
if (ctx->fd < 0 || !buffer || length <= 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytes_written = write(ctx->fd, buffer, length);
|
||||||
|
if (bytes_written < 0) {
|
||||||
|
perror("I2C write failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void real_i2c_close(real_i2c_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->fd >= 0) {
|
||||||
|
close(ctx->fd);
|
||||||
|
ctx->fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* SE050 APDU Commands
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#define SE050_INS_OPEN_SESSION 0x70
|
||||||
|
#define SE050_INS_CLOSE_SESSION 0x71
|
||||||
|
#define SE050_INS_GET_VERSION 0x6F
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 1: I2C Connection Check
|
||||||
|
*/
|
||||||
|
static void test_i2c_connection(const char *i2c_bus)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 1: I2C Connection Check ===\n");
|
||||||
|
printf("Chip: %s\n", CHIP_NAME);
|
||||||
|
printf("I2C Bus: %s\n", i2c_bus);
|
||||||
|
printf("I2C Address: 0x%02X\n", SE050_DEFAULT_I2C_ADDR);
|
||||||
|
|
||||||
|
real_i2c_ctx_t i2c;
|
||||||
|
|
||||||
|
int ret = real_i2c_init(&i2c, i2c_bus, SE050_DEFAULT_I2C_ADDR);
|
||||||
|
if (ret == 0) {
|
||||||
|
TEST_ASSERT(1, "I2C connection established");
|
||||||
|
|
||||||
|
uint8_t buffer[4];
|
||||||
|
int bytes_read = real_i2c_read(&i2c, buffer, 4);
|
||||||
|
if (bytes_read > 0) {
|
||||||
|
printf("Device response: ");
|
||||||
|
for (int i = 0; i < bytes_read; i++) {
|
||||||
|
printf("%02X ", buffer[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
TEST_ASSERT(1, "Device responded to I2C read");
|
||||||
|
} else {
|
||||||
|
printf("[INFO] Device may be in sleep mode\n");
|
||||||
|
TEST_ASSERT(1, "I2C bus accessible");
|
||||||
|
}
|
||||||
|
|
||||||
|
real_i2c_close(&i2c);
|
||||||
|
} else {
|
||||||
|
TEST_ASSERT(0, "I2C connection failed");
|
||||||
|
printf("[WARN] Check I2C connection and permissions\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 2: Session Creation with SCP03
|
||||||
|
*/
|
||||||
|
static void test_session_with_scp03(const char *i2c_bus)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 2: Session Creation with SCP03 ===\n");
|
||||||
|
printf("Chip: %s\n", CHIP_NAME);
|
||||||
|
|
||||||
|
real_i2c_ctx_t i2c;
|
||||||
|
se050_i2c_hal_t hal;
|
||||||
|
se050_session_ctx_t *session = NULL;
|
||||||
|
|
||||||
|
int ret = real_i2c_init(&i2c, i2c_bus, SE050_DEFAULT_I2C_ADDR);
|
||||||
|
TEST_ASSERT_EQ(ret, 0, "I2C initialization");
|
||||||
|
|
||||||
|
memset(&hal, 0, sizeof(hal));
|
||||||
|
hal.handle = &i2c;
|
||||||
|
hal.slave_addr = SE050_DEFAULT_I2C_ADDR;
|
||||||
|
hal.dev_path = i2c_bus;
|
||||||
|
|
||||||
|
se050_status_t status = se050_session_create(&session, &hal);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "Session creation");
|
||||||
|
|
||||||
|
if (session) {
|
||||||
|
status = se050_session_scp03_init(session);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 initialization");
|
||||||
|
|
||||||
|
/* Use chip-specific keys */
|
||||||
|
status = se050_session_scp03_set_keys(session, ENC_KEY, MAC_KEY, DEK_KEY);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "Set chip-specific PlatformSCP03 keys");
|
||||||
|
|
||||||
|
se050_session_delete(session);
|
||||||
|
TEST_ASSERT(1, "Session cleanup successful");
|
||||||
|
}
|
||||||
|
|
||||||
|
real_i2c_close(&i2c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 3: SCP03 Command Encryption (Real Hardware)
|
||||||
|
*/
|
||||||
|
static void test_scp03_encrypt_hardware(const char *i2c_bus)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 3: SCP03 Command Encryption (Hardware) ===\n");
|
||||||
|
printf("Chip: %s\n", CHIP_NAME);
|
||||||
|
|
||||||
|
real_i2c_ctx_t i2c;
|
||||||
|
se050_i2c_hal_t hal;
|
||||||
|
se050_session_ctx_t *session = NULL;
|
||||||
|
|
||||||
|
int ret = real_i2c_init(&i2c, i2c_bus, SE050_DEFAULT_I2C_ADDR);
|
||||||
|
if (ret != 0) {
|
||||||
|
TEST_ASSERT(0, "I2C not available - skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&hal, 0, sizeof(hal));
|
||||||
|
hal.handle = &i2c;
|
||||||
|
hal.slave_addr = SE050_DEFAULT_I2C_ADDR;
|
||||||
|
hal.dev_path = i2c_bus;
|
||||||
|
|
||||||
|
se050_status_t status = se050_session_create(&session, &hal);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "Session creation");
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
real_i2c_close(&i2c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = se050_session_scp03_init(session);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 initialization");
|
||||||
|
|
||||||
|
status = se050_session_scp03_set_keys(session, ENC_KEY, MAC_KEY, DEK_KEY);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "Set PlatformSCP03 keys");
|
||||||
|
|
||||||
|
uint8_t cmd[64];
|
||||||
|
size_t cmd_len = 6;
|
||||||
|
cmd[0] = 0x80;
|
||||||
|
cmd[1] = 0x6F; /* GET VERSION */
|
||||||
|
cmd[2] = 0x00;
|
||||||
|
cmd[3] = 0x00;
|
||||||
|
cmd[4] = 0x00;
|
||||||
|
cmd[5] = 0x00;
|
||||||
|
|
||||||
|
status = se050_session_scp03_encrypt(session, cmd, &cmd_len);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 command encryption");
|
||||||
|
|
||||||
|
if (status == SE050_OK) {
|
||||||
|
printf("Encrypted (%zu bytes): ", cmd_len);
|
||||||
|
for (size_t i = 0; i < cmd_len && i < 16; i++) {
|
||||||
|
printf("%02X ", cmd[i]);
|
||||||
|
}
|
||||||
|
printf("...\n");
|
||||||
|
|
||||||
|
int written = real_i2c_write(&i2c, cmd, (int)cmd_len);
|
||||||
|
if (written > 0) {
|
||||||
|
TEST_ASSERT(1, "Command sent to SE050");
|
||||||
|
|
||||||
|
uint8_t response[64];
|
||||||
|
int bytes_read = real_i2c_read(&i2c, response, sizeof(response));
|
||||||
|
|
||||||
|
if (bytes_read > 0) {
|
||||||
|
TEST_ASSERT(1, "Response received");
|
||||||
|
|
||||||
|
size_t resp_len = (size_t)bytes_read;
|
||||||
|
uint16_t sw = se050_session_scp03_decrypt(session, cmd_len, response, &resp_len);
|
||||||
|
printf("Status: 0x%04X\n", sw);
|
||||||
|
|
||||||
|
if (sw == 0x9000) {
|
||||||
|
TEST_ASSERT(1, "SCP03 decryption successful");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
se050_session_delete(session);
|
||||||
|
real_i2c_close(&i2c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 4: Full PlatformSCP03 Flow
|
||||||
|
*/
|
||||||
|
static void test_platform_scp03_full_flow(const char *i2c_bus)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 4: Full PlatformSCP03 Authentication Flow ===\n");
|
||||||
|
printf("Chip: %s\n", CHIP_NAME);
|
||||||
|
|
||||||
|
real_i2c_ctx_t i2c;
|
||||||
|
se050_i2c_hal_t hal;
|
||||||
|
se050_session_ctx_t *session = NULL;
|
||||||
|
|
||||||
|
int ret = real_i2c_init(&i2c, i2c_bus, SE050_DEFAULT_I2C_ADDR);
|
||||||
|
if (ret != 0) {
|
||||||
|
TEST_ASSERT(0, "I2C not available - skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&hal, 0, sizeof(hal));
|
||||||
|
hal.handle = &i2c;
|
||||||
|
hal.slave_addr = SE050_DEFAULT_I2C_ADDR;
|
||||||
|
hal.dev_path = i2c_bus;
|
||||||
|
|
||||||
|
se050_status_t status = se050_session_create(&session, &hal);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "Step 1: Session creation");
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
real_i2c_close(&i2c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = se050_session_scp03_init(session);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "Step 2: SCP03 initialization");
|
||||||
|
|
||||||
|
status = se050_session_scp03_set_keys(session, ENC_KEY, MAC_KEY, DEK_KEY);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "Step 3: PlatformSCP03 key provisioning");
|
||||||
|
|
||||||
|
uint8_t open_cmd[16];
|
||||||
|
size_t open_cmd_len = 4;
|
||||||
|
open_cmd[0] = 0x80;
|
||||||
|
open_cmd[1] = 0x70; /* OPEN_SESSION */
|
||||||
|
open_cmd[2] = 0x00;
|
||||||
|
open_cmd[3] = 0x00;
|
||||||
|
|
||||||
|
status = se050_session_scp03_encrypt(session, open_cmd, &open_cmd_len);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "Step 4: Encrypt OPEN_SESSION");
|
||||||
|
|
||||||
|
int written = real_i2c_write(&i2c, open_cmd, (int)open_cmd_len);
|
||||||
|
if (written > 0) {
|
||||||
|
TEST_ASSERT(1, "Step 5: Command sent");
|
||||||
|
|
||||||
|
uint8_t response[64];
|
||||||
|
int bytes_read = real_i2c_read(&i2c, response, sizeof(response));
|
||||||
|
|
||||||
|
if (bytes_read > 0) {
|
||||||
|
TEST_ASSERT(1, "Step 6: Response received");
|
||||||
|
|
||||||
|
size_t resp_len = (size_t)bytes_read;
|
||||||
|
uint16_t sw = se050_session_scp03_decrypt(session, open_cmd_len, response, &resp_len);
|
||||||
|
printf("Session status: 0x%04X\n", sw);
|
||||||
|
|
||||||
|
if (sw == 0x9000) {
|
||||||
|
TEST_ASSERT(1, "Step 7: PlatformSCP03 authentication successful!");
|
||||||
|
printf("\n*** PlatformSCP03 connected with %s ***\n", CHIP_NAME);
|
||||||
|
} else {
|
||||||
|
TEST_ASSERT(0, "PlatformSCP03 authentication failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session) se050_session_delete(session);
|
||||||
|
real_i2c_close(&i2c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Print Usage
|
||||||
|
*/
|
||||||
|
static void print_usage(const char *prog)
|
||||||
|
{
|
||||||
|
printf("Usage: %s [options]\n", prog);
|
||||||
|
printf("\nOptions:\n");
|
||||||
|
printf(" -b <bus> I2C bus device (default: /dev/i2c-1)\n");
|
||||||
|
printf(" -h Show this help\n");
|
||||||
|
printf("\nCompile-time chip selection:\n");
|
||||||
|
printf(" make SE050_CHIP=SE050C0 test_se050\n");
|
||||||
|
printf(" make SE050_CHIP=SE050C1 test_se050\n");
|
||||||
|
printf(" make SE050_CHIP=SE050E2 test_se050\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Main Test Runner
|
||||||
|
*/
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
const char *i2c_bus = "/dev/i2c-1";
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-b") == 0 && i + 1 < argc) {
|
||||||
|
i2c_bus = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-h") == 0) {
|
||||||
|
print_usage(argv[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("========================================\n");
|
||||||
|
printf("SE050 Hardware Platform SCP03 Test\n");
|
||||||
|
printf("========================================\n");
|
||||||
|
printf("Chip Type: %s\n", CHIP_NAME);
|
||||||
|
printf("I2C Bus: %s\n", i2c_bus);
|
||||||
|
printf("PlatformSCP03 Keys: Chip-specific\n");
|
||||||
|
printf("========================================\n");
|
||||||
|
|
||||||
|
printf("\nKey Fingerprints:\n");
|
||||||
|
printf(" ENC: %02X%02X%02X%02X...\n", ENC_KEY[0], ENC_KEY[1], ENC_KEY[2], ENC_KEY[3]);
|
||||||
|
printf(" MAC: %02X%02X%02X%02X...\n", MAC_KEY[0], MAC_KEY[1], MAC_KEY[2], MAC_KEY[3]);
|
||||||
|
printf(" DEK: %02X%02X%02X%02X...\n", DEK_KEY[0], DEK_KEY[1], DEK_KEY[2], DEK_KEY[3]);
|
||||||
|
|
||||||
|
test_i2c_connection(i2c_bus);
|
||||||
|
test_session_with_scp03(i2c_bus);
|
||||||
|
test_scp03_encrypt_hardware(i2c_bus);
|
||||||
|
test_platform_scp03_full_flow(i2c_bus);
|
||||||
|
|
||||||
|
printf("\n========================================\n");
|
||||||
|
printf("Test Summary\n");
|
||||||
|
printf("========================================\n");
|
||||||
|
printf("Passed: %d\n", test_passed);
|
||||||
|
printf("Failed: %d\n", test_failed);
|
||||||
|
printf("Total: %d\n", test_passed + test_failed);
|
||||||
|
printf("========================================\n");
|
||||||
|
|
||||||
|
if (test_failed == 0) {
|
||||||
|
printf("\n✓ All tests passed! PlatformSCP03 verified with %s\n", CHIP_NAME);
|
||||||
|
} else {
|
||||||
|
printf("\n✗ Some tests failed.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return test_failed > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "se050_tai64n.h"
|
||||||
|
|
||||||
|
static void print_hex(const char *label, const uint8_t *buf, size_t len)
|
||||||
|
{
|
||||||
|
printf("%s: ", label);
|
||||||
|
for (size_t i = 0; i < len; i++) printf("%02x", buf[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
uint8_t timestamp[TAI64N_SIZE];
|
||||||
|
uint64_t seconds;
|
||||||
|
uint32_t nanoseconds;
|
||||||
|
int passed = 0;
|
||||||
|
|
||||||
|
printf("TAI64N Test Suite\n=================\n\n");
|
||||||
|
|
||||||
|
printf("Test 1: Encode Unix Epoch\n");
|
||||||
|
se050_tai64n_encode(timestamp, 0, 0);
|
||||||
|
print_hex("TAI64N", timestamp, 12);
|
||||||
|
se050_tai64n_decode(&seconds, &nanoseconds, timestamp);
|
||||||
|
printf("Decoded: seconds=%llu, nanoseconds=%u\n",
|
||||||
|
(unsigned long long)seconds, nanoseconds);
|
||||||
|
if (seconds == 0 && nanoseconds == 0) {
|
||||||
|
printf("[PASS]\n\n"); passed++;
|
||||||
|
} else {
|
||||||
|
printf("[FAIL]\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Test 2: Encode Current Time\n");
|
||||||
|
se050_tai64n_now(timestamp);
|
||||||
|
print_hex("TAI64N", timestamp, 12);
|
||||||
|
se050_tai64n_decode(&seconds, &nanoseconds, timestamp);
|
||||||
|
printf("Decoded: seconds=%llu, nanoseconds=%u\n",
|
||||||
|
(unsigned long long)seconds, nanoseconds);
|
||||||
|
printf("[INFO] Computed\n\n"); passed++;
|
||||||
|
|
||||||
|
printf("Test 3: Specific Time\n");
|
||||||
|
se050_tai64n_encode(timestamp, 1609459200, 123456789);
|
||||||
|
print_hex("TAI64N", timestamp, 12);
|
||||||
|
se050_tai64n_decode(&seconds, &nanoseconds, timestamp);
|
||||||
|
printf("Decoded: seconds=%llu, nanoseconds=%u\n",
|
||||||
|
(unsigned long long)seconds, nanoseconds);
|
||||||
|
if (seconds == 1609459200 && nanoseconds == 123456789) {
|
||||||
|
printf("[PASS]\n\n"); passed++;
|
||||||
|
} else {
|
||||||
|
printf("[FAIL]\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Test 4: Window Check\n");
|
||||||
|
se050_tai64n_now(timestamp);
|
||||||
|
int result = se050_tai64n_check_window(timestamp, 60);
|
||||||
|
printf("Window check (60s): %s\n",
|
||||||
|
result == 0 ? "PASS (within window)" :
|
||||||
|
result == -1 ? "FAIL (too old)" : "FAIL (too far future)");
|
||||||
|
if (result == 0) {
|
||||||
|
printf("[PASS]\n\n"); passed++;
|
||||||
|
} else {
|
||||||
|
printf("[FAIL]\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("=================\n");
|
||||||
|
printf("Passed: %d/4\n=================\n", passed);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "se050_tai64n_hw.h"
|
||||||
|
|
||||||
|
/* Mock SE050 API functions */
|
||||||
|
int Se05x_API_ReadCounter(void *session, uint32_t obj_id, uint32_t *counter)
|
||||||
|
{
|
||||||
|
(void)obj_id;
|
||||||
|
if (session) {
|
||||||
|
mock_session_t *s = (mock_session_t*)session;
|
||||||
|
*counter = s->counter;
|
||||||
|
} else {
|
||||||
|
*counter = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Se05x_API_IncrementCounter(void *session, uint32_t obj_id)
|
||||||
|
{
|
||||||
|
(void)session;
|
||||||
|
(void)obj_id;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_hex(const char *label, const uint8_t *buf, size_t len)
|
||||||
|
{
|
||||||
|
printf("%s: ", label);
|
||||||
|
for (size_t i = 0; i < len; i++) printf("%02x", buf[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
uint8_t tai64n[12];
|
||||||
|
uint32_t counter;
|
||||||
|
uint8_t current[12];
|
||||||
|
int passed = 0;
|
||||||
|
|
||||||
|
printf("SE050 Hardware TAI64N Test Suite\n");
|
||||||
|
printf("==================================\n\n");
|
||||||
|
|
||||||
|
printf("Test 1: Read Counter\n");
|
||||||
|
mock_session_t session = { .counter = 1234567890UL };
|
||||||
|
if (se050_tai64n_hw_read_counter(&session, &counter) == 0) {
|
||||||
|
printf("Counter: %u (0x%08x)\n", counter, counter);
|
||||||
|
printf("[PASS]\n\n"); passed++;
|
||||||
|
} else {
|
||||||
|
printf("[FAIL]\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Test 2: TAI64N Now\n");
|
||||||
|
if (se050_tai64n_hw_now(&session, tai64n) == 0) {
|
||||||
|
print_hex("TAI64N", tai64n, 12);
|
||||||
|
printf("[PASS]\n\n"); passed++;
|
||||||
|
} else {
|
||||||
|
printf("[FAIL]\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Test 3: Increment Counter\n");
|
||||||
|
uint32_t before = session.counter;
|
||||||
|
if (se050_tai64n_hw_increment(&session) == 0) {
|
||||||
|
printf("Before: %u, After: %u\n", before, session.counter);
|
||||||
|
printf("[PASS]\n\n"); passed++;
|
||||||
|
} else {
|
||||||
|
printf("[FAIL]\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Test 4: Window Check\n");
|
||||||
|
se050_tai64n_hw_now(&session, tai64n);
|
||||||
|
se050_tai64n_hw_now(&session, current);
|
||||||
|
int result = se050_tai64n_hw_check_window(tai64n, current, 60);
|
||||||
|
printf("Window check (60s): %s\n",
|
||||||
|
result == 1 ? "PASS (within window)" : "FAIL");
|
||||||
|
if (result == 1) {
|
||||||
|
printf("[PASS]\n\n"); passed++;
|
||||||
|
} else {
|
||||||
|
printf("[FAIL]\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Test 5: Replay Detection\n");
|
||||||
|
uint8_t old_timestamp[12];
|
||||||
|
session.counter = 1000000000UL; /* Reset to old value (1 second) */
|
||||||
|
se050_tai64n_hw_now(&session, old_timestamp);
|
||||||
|
|
||||||
|
session.counter = 1060000000UL; /* 60 seconds later */
|
||||||
|
se050_tai64n_hw_now(&session, current);
|
||||||
|
|
||||||
|
result = se050_tai64n_hw_check_window(old_timestamp, current, 30);
|
||||||
|
printf("Old timestamp (60s old), window=30s: %s\n",
|
||||||
|
result == 0 ? "REJECTED (expired)" : "ACCEPTED");
|
||||||
|
if (result == 0) {
|
||||||
|
printf("[PASS] Replay correctly detected\n\n"); passed++;
|
||||||
|
} else {
|
||||||
|
printf("[FAIL]\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("==================================\n");
|
||||||
|
printf("Passed: %d/5\n", passed);
|
||||||
|
printf("==================================\n");
|
||||||
|
|
||||||
|
return (passed == 5) ? 0 : 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
/**
|
||||||
|
* @file test_wireguard.c
|
||||||
|
* @brief WireGuard Protocol Tests
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "se050_wireguard.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)
|
||||||
|
|
||||||
|
/* Test vectors from RFC 7748 */
|
||||||
|
static const uint8_t TEST_PRIVATE_KEY[32] = {
|
||||||
|
0x77,0x07,0x6d,0x0a,0x73,0x18,0xa5,0x7d,
|
||||||
|
0x3c,0x16,0xc1,0x72,0x51,0xb2,0x66,0x45,
|
||||||
|
0xdf,0xbc,0x9f,0x03,0xc8,0xf2,0xbc,0x2b,
|
||||||
|
0x4f,0x81,0xb0,0x9c,0x0c,0xcb,0x3e,0x9a
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t TEST_PEER_PUBLIC_KEY[32] = {
|
||||||
|
0x5d,0xab,0x08,0x7e,0x62,0x4a,0x8a,0x4b,
|
||||||
|
0x79,0xe1,0x7f,0x8b,0x83,0x80,0x0e,0xe6,
|
||||||
|
0x6f,0x31,0x97,0xb6,0x93,0x44,0x41,0x76,
|
||||||
|
0x61,0x6c,0x39,0x5c,0xbe,0x7a,0x4b,0x82
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Test: Session initialization */
|
||||||
|
static void test_session_init(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Session Initialization ---\n");
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
int ret = se050_wireguard_session_init(&session, TEST_PRIVATE_KEY, TEST_PEER_PUBLIC_KEY);
|
||||||
|
|
||||||
|
TEST_ASSERT(ret == 0, "Session init returns 0");
|
||||||
|
|
||||||
|
/* Verify public key was derived */
|
||||||
|
uint8_t expected_public[32];
|
||||||
|
se050_x25519_sw_derive_public_key(expected_public, TEST_PRIVATE_KEY);
|
||||||
|
TEST_ASSERT(memcmp(session.public_key, expected_public, 32) == 0,
|
||||||
|
"Public key derived correctly");
|
||||||
|
|
||||||
|
se050_wireguard_session_cleanup(&session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: Key derivation */
|
||||||
|
static void test_key_derivation(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Key Derivation ---\n");
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
se050_wireguard_session_init(&session, TEST_PRIVATE_KEY, TEST_PEER_PUBLIC_KEY);
|
||||||
|
|
||||||
|
/* Use a fixed shared secret for testing */
|
||||||
|
uint8_t shared_secret[32] = {0};
|
||||||
|
for (int i = 0; i < 32; i++) shared_secret[i] = i;
|
||||||
|
|
||||||
|
int ret = se050_wireguard_derive_keys(&session, shared_secret);
|
||||||
|
TEST_ASSERT(ret == 0, "Key derivation returns 0");
|
||||||
|
TEST_ASSERT(session.handshake_complete == true, "Handshake marked complete");
|
||||||
|
|
||||||
|
/* Verify keys are non-zero */
|
||||||
|
uint8_t all_zero = 1;
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if (session.sending_key[i] != 0 || session.receiving_key[i] != 0) {
|
||||||
|
all_zero = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TEST_ASSERT(all_zero == 0, "Session keys are non-zero");
|
||||||
|
|
||||||
|
se050_wireguard_session_cleanup(&session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: Packet encryption/decryption */
|
||||||
|
static void test_encrypt_decrypt(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Encryption/Decryption ---\n");
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
se050_wireguard_session_init(&session, TEST_PRIVATE_KEY, TEST_PEER_PUBLIC_KEY);
|
||||||
|
|
||||||
|
/* Setup keys */
|
||||||
|
uint8_t shared_secret[32] = {0};
|
||||||
|
for (int i = 0; i < 32; i++) shared_secret[i] = i;
|
||||||
|
|
||||||
|
/* Set as initiator for key derivation */
|
||||||
|
session.is_initiator = 1;
|
||||||
|
se050_wireguard_derive_keys(&session, shared_secret);
|
||||||
|
|
||||||
|
/* For single-session test, use same key for encrypt and decrypt */
|
||||||
|
memcpy(session.receiving_key, session.sending_key, 32);
|
||||||
|
|
||||||
|
/* Test data */
|
||||||
|
const char *plaintext = "Hello, WireGuard!";
|
||||||
|
size_t plaintext_len = strlen(plaintext);
|
||||||
|
|
||||||
|
uint8_t encrypted[1024];
|
||||||
|
size_t encrypted_len;
|
||||||
|
|
||||||
|
int ret = se050_wireguard_encrypt_packet(&session, encrypted, &encrypted_len,
|
||||||
|
(uint8_t*)plaintext, plaintext_len);
|
||||||
|
TEST_ASSERT(ret == 0, "Encryption returns 0");
|
||||||
|
TEST_ASSERT(encrypted_len == 16 + plaintext_len + 16,
|
||||||
|
"Encrypted length is correct (header + ciphertext + tag)");
|
||||||
|
|
||||||
|
/* Decrypt */
|
||||||
|
uint8_t decrypted[1024];
|
||||||
|
size_t decrypted_len;
|
||||||
|
|
||||||
|
ret = se050_wireguard_decrypt_packet(&session, decrypted, &decrypted_len,
|
||||||
|
encrypted, encrypted_len);
|
||||||
|
TEST_ASSERT(ret == 0, "Decryption returns 0");
|
||||||
|
TEST_ASSERT(decrypted_len == plaintext_len, "Decrypted length matches");
|
||||||
|
TEST_ASSERT(memcmp(decrypted, plaintext, plaintext_len) == 0,
|
||||||
|
"Decrypted content matches original");
|
||||||
|
|
||||||
|
se050_wireguard_session_cleanup(&session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: Replay detection */
|
||||||
|
static void test_replay_detection(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Replay Detection ---\n");
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
se050_wireguard_session_init(&session, TEST_PRIVATE_KEY, TEST_PEER_PUBLIC_KEY);
|
||||||
|
|
||||||
|
/* Setup keys */
|
||||||
|
uint8_t shared_secret[32] = {0};
|
||||||
|
for (int i = 0; i < 32; i++) shared_secret[i] = i;
|
||||||
|
|
||||||
|
session.is_initiator = 1;
|
||||||
|
se050_wireguard_derive_keys(&session, shared_secret);
|
||||||
|
|
||||||
|
/* For single-session test, use same key for encrypt and decrypt */
|
||||||
|
memcpy(session.receiving_key, session.sending_key, 32);
|
||||||
|
|
||||||
|
/* Encrypt a packet */
|
||||||
|
const char *plaintext = "Test message";
|
||||||
|
uint8_t encrypted[1024];
|
||||||
|
size_t encrypted_len;
|
||||||
|
se050_wireguard_encrypt_packet(&session, encrypted, &encrypted_len,
|
||||||
|
(uint8_t*)plaintext, strlen(plaintext));
|
||||||
|
|
||||||
|
/* Decrypt once - should succeed */
|
||||||
|
uint8_t decrypted[1024];
|
||||||
|
size_t decrypted_len;
|
||||||
|
int ret = se050_wireguard_decrypt_packet(&session, decrypted, &decrypted_len,
|
||||||
|
encrypted, encrypted_len);
|
||||||
|
TEST_ASSERT(ret == 0, "First decryption succeeds");
|
||||||
|
|
||||||
|
/* Decrypt again with same packet - should fail (replay) */
|
||||||
|
ret = se050_wireguard_decrypt_packet(&session, decrypted, &decrypted_len,
|
||||||
|
encrypted, encrypted_len);
|
||||||
|
TEST_ASSERT(ret != 0, "Replay packet rejected");
|
||||||
|
|
||||||
|
se050_wireguard_session_cleanup(&session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: MAC computation */
|
||||||
|
static void test_mac_computation(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test MAC Computation ---\n");
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
se050_wireguard_session_init(&session, TEST_PRIVATE_KEY, TEST_PEER_PUBLIC_KEY);
|
||||||
|
|
||||||
|
uint8_t test_data[64] = {0};
|
||||||
|
uint8_t mac1[16], mac2[16];
|
||||||
|
|
||||||
|
int ret = se050_wireguard_compute_mac1(&session, test_data, sizeof(test_data), mac1);
|
||||||
|
TEST_ASSERT(ret == 0, "MAC1 computation returns 0");
|
||||||
|
|
||||||
|
ret = se050_wireguard_compute_mac2(&session, mac1, test_data, sizeof(test_data), mac2);
|
||||||
|
TEST_ASSERT(ret == 0, "MAC2 computation returns 0");
|
||||||
|
|
||||||
|
/* Verify MACs are non-zero */
|
||||||
|
uint8_t mac1_zero = 1, mac2_zero = 1;
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
if (mac1[i] != 0) mac1_zero = 0;
|
||||||
|
if (mac2[i] != 0) mac2_zero = 0;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(mac1_zero == 0, "MAC1 is non-zero");
|
||||||
|
TEST_ASSERT(mac2_zero == 0, "MAC2 is non-zero");
|
||||||
|
|
||||||
|
se050_wireguard_session_cleanup(&session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: Key generation */
|
||||||
|
static void test_key_generation(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Key Generation ---\n");
|
||||||
|
|
||||||
|
uint8_t private_key[32], public_key[32];
|
||||||
|
|
||||||
|
int ret = se050_wireguard_generate_keypair(private_key, public_key);
|
||||||
|
TEST_ASSERT(ret == 0, "Key generation returns 0");
|
||||||
|
|
||||||
|
/* Verify keys are non-zero */
|
||||||
|
uint8_t private_zero = 1, public_zero = 1;
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if (private_key[i] != 0) private_zero = 0;
|
||||||
|
if (public_key[i] != 0) public_zero = 0;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(private_zero == 0, "Private key is non-zero");
|
||||||
|
TEST_ASSERT(public_zero == 0, "Public key is non-zero");
|
||||||
|
|
||||||
|
/* Verify public key matches private key */
|
||||||
|
uint8_t expected_public[32];
|
||||||
|
se050_x25519_sw_derive_public_key(expected_public, private_key);
|
||||||
|
TEST_ASSERT(memcmp(public_key, expected_public, 32) == 0,
|
||||||
|
"Public key derived from private key");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: Session cleanup zeros memory */
|
||||||
|
static void test_session_cleanup(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Session Cleanup ---\n");
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
se050_wireguard_session_init(&session, TEST_PRIVATE_KEY, TEST_PEER_PUBLIC_KEY);
|
||||||
|
|
||||||
|
/* Save a copy before cleanup */
|
||||||
|
uint8_t private_copy[32];
|
||||||
|
memcpy(private_copy, session.private_key, 32);
|
||||||
|
|
||||||
|
se050_wireguard_session_cleanup(&session);
|
||||||
|
|
||||||
|
/* Verify memory was zeroed */
|
||||||
|
uint8_t all_zero = 1;
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if (session.private_key[i] != 0) all_zero = 0;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(all_zero == 1, "Session memory zeroed after cleanup");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: Invalid inputs */
|
||||||
|
static void test_invalid_inputs(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Invalid Inputs ---\n");
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
|
||||||
|
/* NULL session */
|
||||||
|
int ret = se050_wireguard_session_init(NULL, TEST_PRIVATE_KEY, TEST_PEER_PUBLIC_KEY);
|
||||||
|
TEST_ASSERT(ret == -1, "NULL session rejected");
|
||||||
|
|
||||||
|
/* NULL private key */
|
||||||
|
ret = se050_wireguard_session_init(&session, NULL, TEST_PEER_PUBLIC_KEY);
|
||||||
|
TEST_ASSERT(ret == -1, "NULL private key rejected");
|
||||||
|
|
||||||
|
/* NULL peer public key */
|
||||||
|
ret = se050_wireguard_session_init(&session, TEST_PRIVATE_KEY, NULL);
|
||||||
|
TEST_ASSERT(ret == -1, "NULL peer public key rejected");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
printf("========================================\n");
|
||||||
|
printf(" WireGuard Protocol Test Suite\n");
|
||||||
|
printf("========================================\n");
|
||||||
|
|
||||||
|
test_session_init();
|
||||||
|
test_key_derivation();
|
||||||
|
test_encrypt_decrypt();
|
||||||
|
test_replay_detection();
|
||||||
|
test_mac_computation();
|
||||||
|
test_key_generation();
|
||||||
|
test_session_cleanup();
|
||||||
|
test_invalid_inputs();
|
||||||
|
|
||||||
|
printf("\n========================================\n");
|
||||||
|
printf(" Results: %d passed, %d failed\n", passed, failed);
|
||||||
|
printf("========================================\n");
|
||||||
|
|
||||||
|
return failed == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "se050_wireguard_proto.h"
|
||||||
|
|
||||||
|
static void print_hex(const char *label, const uint8_t *buf, size_t len)
|
||||||
|
{
|
||||||
|
printf("%s: ", label);
|
||||||
|
for (size_t i = 0; i < len; i++) printf("%02x", buf[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
uint8_t ck[32], tk[32], tk1_test[32], tk2_test[32], tk3_test[32];
|
||||||
|
uint8_t ikm[32];
|
||||||
|
uint8_t data[32];
|
||||||
|
int passed = 0;
|
||||||
|
|
||||||
|
printf("WireGuard KDF Chain Test Suite\n");
|
||||||
|
printf("================================\n\n");
|
||||||
|
|
||||||
|
printf("Test 1: KDF Init\n");
|
||||||
|
wg_kdf_init(ck, tk);
|
||||||
|
print_hex("Chain Key", ck, 32);
|
||||||
|
print_hex("Temp Key", tk, 32);
|
||||||
|
printf("[INFO] Initialized\n\n");
|
||||||
|
passed++;
|
||||||
|
|
||||||
|
printf("Test 2: KDF1 (First Derivation)\n");
|
||||||
|
for (int i = 0; i < 32; i++) ikm[i] = i;
|
||||||
|
wg_kdf1(ck, tk1_test, ikm, 32);
|
||||||
|
print_hex("CK after KDF1", ck, 32);
|
||||||
|
print_hex("TK1", tk1_test, 32);
|
||||||
|
printf("[INFO] KDF1 done\n\n");
|
||||||
|
passed++;
|
||||||
|
|
||||||
|
printf("Test 3: KDF2 (Second Derivation)\n");
|
||||||
|
uint8_t ck_old[32];
|
||||||
|
memcpy(ck_old, ck, 32);
|
||||||
|
wg_kdf2(ck, tk2_test, ck_old, tk1_test);
|
||||||
|
print_hex("CK after KDF2", ck, 32);
|
||||||
|
print_hex("TK2", tk2_test, 32);
|
||||||
|
printf("[INFO] KDF2 done\n\n");
|
||||||
|
passed++;
|
||||||
|
|
||||||
|
printf("Test 4: KDF3 (With Data)\n");
|
||||||
|
memcpy(ck_old, ck, 32);
|
||||||
|
for (int i = 0; i < 32; i++) data[i] = 0xff - i;
|
||||||
|
wg_kdf3(ck, tk3_test, ck_old, tk2_test, data, 32);
|
||||||
|
print_hex("CK after KDF3", ck, 32);
|
||||||
|
print_hex("Data", data, 32);
|
||||||
|
printf("[INFO] KDF3 done\n\n");
|
||||||
|
passed++;
|
||||||
|
|
||||||
|
printf("Test 5: Full Handshake Chain Simulation\n");
|
||||||
|
uint8_t ck0[32], ck1[32], ck2[32], ck3[32];
|
||||||
|
uint8_t tk0[32], tk1[32], tk2[32], tk3_final[32];
|
||||||
|
|
||||||
|
wg_kdf_init(ck0, tk0);
|
||||||
|
printf("Initial: CK0 = "); print_hex("", ck0, 32);
|
||||||
|
|
||||||
|
/* Step 1: IKM -> CK1, TK1 */
|
||||||
|
uint8_t ikm1[32] = {0};
|
||||||
|
for (int i = 0; i < 32; i++) ikm1[i] = i;
|
||||||
|
wg_kdf1(ck1, tk1, ikm1, 32);
|
||||||
|
printf("After KDF1: CK1 = "); print_hex("", ck1, 32);
|
||||||
|
printf(" TK1 = "); print_hex("", tk1, 32);
|
||||||
|
|
||||||
|
/* Step 2: CK1, TK1 -> CK2, TK2 */
|
||||||
|
wg_kdf2(ck2, tk2, ck1, tk1);
|
||||||
|
printf("After KDF2: CK2 = "); print_hex("", ck2, 32);
|
||||||
|
printf(" TK2 = "); print_hex("", tk2, 32);
|
||||||
|
|
||||||
|
/* Step 3: CK2, TK2, data -> CK3 */
|
||||||
|
uint8_t handshake_data[32] = {0};
|
||||||
|
for (int i = 0; i < 32; i++) handshake_data[i] = 0xaa;
|
||||||
|
wg_kdf3(ck3, tk3_final, ck2, tk2, handshake_data, 32);
|
||||||
|
printf("After KDF3: CK3 = "); print_hex("", ck3, 32);
|
||||||
|
printf("[INFO] Full chain complete\n\n");
|
||||||
|
passed++;
|
||||||
|
|
||||||
|
printf("================================\n");
|
||||||
|
printf("Passed: %d/5\n", passed);
|
||||||
|
printf("================================\n");
|
||||||
|
|
||||||
|
return (passed == 5) ? 0 : 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
/**
|
||||||
|
* @file test_wireguard_proto.c
|
||||||
|
* @brief WireGuard Protocol Integration Test
|
||||||
|
*
|
||||||
|
* Tests the complete WireGuard protocol stack:
|
||||||
|
* - X25519 key exchange
|
||||||
|
* - Key derivation
|
||||||
|
* - Packet encryption/decryption
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "se050_x25519_sw.h"
|
||||||
|
#include "se050_chacha20_poly1305.h"
|
||||||
|
#include "se050_blake2s.h"
|
||||||
|
#include "se050_hmac_blake2s.h"
|
||||||
|
#include "se050_crypto_utils.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.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)
|
||||||
|
|
||||||
|
/* RFC 7748 Test Vector 1 */
|
||||||
|
static const uint8_t RFC7748_SCALAR[32] = {
|
||||||
|
0xa5,0x46,0xe3,0x6b,0xf0,0x52,0x7c,0x9d,
|
||||||
|
0x3b,0x16,0x15,0x4b,0x82,0x46,0x5e,0xdd,
|
||||||
|
0x62,0x14,0x4c,0x0a,0xc1,0xfc,0x5a,0x18,
|
||||||
|
0x50,0x6a,0x22,0x44,0xba,0x44,0x9a,0xc4
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t RFC7748_POINT[32] = {
|
||||||
|
0xe6,0xdb,0x68,0x67,0x58,0x30,0x30,0xdb,
|
||||||
|
0x35,0x94,0xc1,0xa4,0x24,0xb1,0x5f,0x7c,
|
||||||
|
0x72,0x66,0x24,0xec,0x26,0xb3,0x35,0x3b,
|
||||||
|
0x10,0xa9,0x03,0xa6,0xd0,0xab,0x1c,0x4c
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t RFC7748_EXPECTED_SS[32] = {
|
||||||
|
0xc3,0xda,0x55,0x37,0x9d,0xe9,0xc6,0x90,
|
||||||
|
0x8e,0x94,0xea,0x4d,0xf2,0x8d,0x08,0x4f,
|
||||||
|
0x32,0xec,0xcf,0x03,0x49,0x1c,0x71,0xf7,
|
||||||
|
0x54,0xb4,0x07,0x55,0x77,0xa2,0x85,0x52
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Test X25519 against RFC 7748 */
|
||||||
|
static void test_x25519_rfc7748(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test X25519 RFC 7748 ---\n");
|
||||||
|
|
||||||
|
uint8_t shared_secret[32];
|
||||||
|
int ret = x25519_sw(shared_secret, RFC7748_SCALAR, RFC7748_POINT);
|
||||||
|
|
||||||
|
TEST_ASSERT(ret == 0, "X25519 computation returns 0");
|
||||||
|
TEST_ASSERT(memcmp(shared_secret, RFC7748_EXPECTED_SS, 32) == 0,
|
||||||
|
"X25519 matches RFC 7748 test vector");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test ChaCha20-Poly1305 AEAD */
|
||||||
|
static void test_chacha20_poly1305(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test ChaCha20-Poly1305 ---\n");
|
||||||
|
|
||||||
|
uint8_t key[32] = {0};
|
||||||
|
for (int i = 0; i < 32; i++) key[i] = i;
|
||||||
|
uint8_t nonce[12] = {0};
|
||||||
|
uint8_t aad[16] = {0};
|
||||||
|
uint8_t plaintext[32] = "Hello, WireGuard!";
|
||||||
|
uint8_t ciphertext[32 + 16];
|
||||||
|
uint8_t tag[16];
|
||||||
|
uint8_t decrypted[32];
|
||||||
|
|
||||||
|
/* Setup context */
|
||||||
|
se050_chacha20_poly1305_ctx_t ctx;
|
||||||
|
memcpy(ctx.key, key, 32);
|
||||||
|
|
||||||
|
/* Encrypt */
|
||||||
|
int ret = se050_chacha20_poly1305_encrypt(
|
||||||
|
&ctx, nonce, plaintext, 17, aad, 16, ciphertext, tag);
|
||||||
|
TEST_ASSERT(ret == 0, "Encryption returns 0");
|
||||||
|
|
||||||
|
/* Decrypt */
|
||||||
|
ret = se050_chacha20_poly1305_decrypt(
|
||||||
|
&ctx, nonce, ciphertext, 17, aad, 16, tag, decrypted);
|
||||||
|
TEST_ASSERT(ret == 0, "Decryption returns 0");
|
||||||
|
TEST_ASSERT(memcmp(decrypted, plaintext, 17) == 0,
|
||||||
|
"Decrypted content matches");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test BLAKE2s */
|
||||||
|
static void test_blake2s(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test BLAKE2s ---\n");
|
||||||
|
|
||||||
|
uint8_t key[32] = {0};
|
||||||
|
uint8_t data[32] = "test data";
|
||||||
|
uint8_t mac[32];
|
||||||
|
|
||||||
|
int ret = se050_hmac_blake2s(mac, key, 32, data, 9);
|
||||||
|
TEST_ASSERT(ret == 0, "HMAC-BLAKE2s returns 0");
|
||||||
|
|
||||||
|
/* Verify MAC is non-zero */
|
||||||
|
uint8_t all_zero = 1;
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if (mac[i] != 0) all_zero = 0;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(all_zero == 0, "MAC is non-zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test key derivation (simplified HKDF) */
|
||||||
|
static void test_key_derivation(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Key Derivation ---\n");
|
||||||
|
|
||||||
|
/* Simulate WireGuard key derivation */
|
||||||
|
uint8_t shared_secret[32] = {0};
|
||||||
|
for (int i = 0; i < 32; i++) shared_secret[i] = i;
|
||||||
|
|
||||||
|
uint8_t info[] = "WireGuard v1 zx2c4 IPsec v1";
|
||||||
|
uint8_t output[64];
|
||||||
|
|
||||||
|
/* Simple expansion: HMAC(shared_secret, info) */
|
||||||
|
se050_hmac_blake2s(output, shared_secret, 32, info, sizeof(info) - 1);
|
||||||
|
|
||||||
|
uint8_t all_zero = 1;
|
||||||
|
for (int i = 0; i < 64; i++) {
|
||||||
|
if (output[i] != 0) all_zero = 0;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(all_zero == 0, "Derived keys are non-zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test full DH exchange simulation */
|
||||||
|
static void test_dh_exchange(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test DH Exchange Simulation ---\n");
|
||||||
|
|
||||||
|
/* Use RFC 7748 test vectors for Alice and Bob */
|
||||||
|
const uint8_t ALICE_PRIVATE[32] = {
|
||||||
|
0x77,0x07,0x6d,0x0a,0x73,0x18,0xa5,0x7d,
|
||||||
|
0x3c,0x16,0xc1,0x72,0x51,0xb2,0x66,0x45,
|
||||||
|
0xdf,0xbc,0x9f,0x03,0xc8,0xf2,0xbc,0x2b,
|
||||||
|
0x4f,0x81,0xb0,0x9c,0x0c,0xcb,0x3e,0x9a
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t BOB_PRIVATE[32] = {
|
||||||
|
0x5d,0xab,0x08,0x7e,0x62,0x4a,0x8a,0x4b,
|
||||||
|
0x79,0xe1,0x7f,0x8b,0x83,0x80,0x0e,0xe6,
|
||||||
|
0x6f,0x31,0x97,0xb6,0x93,0x44,0x41,0x76,
|
||||||
|
0x61,0x6c,0x39,0x5c,0xbe,0x7a,0x4b,0x82
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Derive public keys */
|
||||||
|
uint8_t alice_public[32], bob_public[32];
|
||||||
|
se050_x25519_sw_derive_public_key(alice_public, ALICE_PRIVATE);
|
||||||
|
se050_x25519_sw_derive_public_key(bob_public, BOB_PRIVATE);
|
||||||
|
|
||||||
|
/* Compute shared secrets */
|
||||||
|
uint8_t alice_shared[32], bob_shared[32];
|
||||||
|
|
||||||
|
int ret1 = x25519_sw(alice_shared, ALICE_PRIVATE, bob_public);
|
||||||
|
int ret2 = x25519_sw(bob_shared, BOB_PRIVATE, alice_public);
|
||||||
|
|
||||||
|
TEST_ASSERT(ret1 == 0, "Alice computes shared secret");
|
||||||
|
TEST_ASSERT(ret2 == 0, "Bob computes shared secret");
|
||||||
|
TEST_ASSERT(memcmp(alice_shared, bob_shared, 32) == 0,
|
||||||
|
"Shared secrets match");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test packet encryption/decryption flow */
|
||||||
|
static void test_packet_flow(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Packet Encryption Flow ---\n");
|
||||||
|
|
||||||
|
/* Setup: Generate keypair and derive shared secret */
|
||||||
|
se050_x25519_sw_keypair_t keypair;
|
||||||
|
se050_x25519_sw_generate_keypair(&keypair, NULL, NULL);
|
||||||
|
|
||||||
|
uint8_t peer_public[32];
|
||||||
|
se050_x25519_sw_derive_public_key(peer_public, keypair.private_key);
|
||||||
|
|
||||||
|
uint8_t shared_secret[32];
|
||||||
|
x25519_sw(shared_secret, keypair.private_key, peer_public);
|
||||||
|
|
||||||
|
/* Derive session key (simplified) */
|
||||||
|
uint8_t session_key[32];
|
||||||
|
uint8_t info[] = "WireGuard v1 zx2c4 IPsec v1";
|
||||||
|
se050_hmac_blake2s(session_key, shared_secret, 32, info, sizeof(info) - 1);
|
||||||
|
|
||||||
|
/* Encrypt packet */
|
||||||
|
const char *plaintext = "Test WireGuard packet";
|
||||||
|
uint8_t nonce[12] = {0};
|
||||||
|
uint8_t header[16] = {1}; /* Packet type */
|
||||||
|
|
||||||
|
uint8_t ciphertext[100];
|
||||||
|
uint8_t tag[16];
|
||||||
|
|
||||||
|
se050_chacha20_poly1305_ctx_t ctx;
|
||||||
|
memcpy(ctx.key, session_key, 32);
|
||||||
|
|
||||||
|
int ret = se050_chacha20_poly1305_encrypt(
|
||||||
|
&ctx, nonce, (uint8_t*)plaintext, strlen(plaintext),
|
||||||
|
header, 16, ciphertext, tag);
|
||||||
|
TEST_ASSERT(ret == 0, "Packet encryption succeeds");
|
||||||
|
|
||||||
|
/* Decrypt packet */
|
||||||
|
uint8_t decrypted[100];
|
||||||
|
ret = se050_chacha20_poly1305_decrypt(
|
||||||
|
&ctx, nonce, ciphertext, strlen(plaintext),
|
||||||
|
header, 16, tag, decrypted);
|
||||||
|
TEST_ASSERT(ret == 0, "Packet decryption succeeds");
|
||||||
|
TEST_ASSERT(memcmp(decrypted, plaintext, strlen(plaintext)) == 0,
|
||||||
|
"Decrypted packet matches");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test memory zeroizing */
|
||||||
|
static void test_memory_zeroizing(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Memory Zeroizing ---\n");
|
||||||
|
|
||||||
|
uint8_t sensitive[32] = {0};
|
||||||
|
for (int i = 0; i < 32; i++) sensitive[i] = i;
|
||||||
|
|
||||||
|
/* Zeroize */
|
||||||
|
memzero_explicit(sensitive, 32);
|
||||||
|
|
||||||
|
/* Verify */
|
||||||
|
uint8_t all_zero = 1;
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if (sensitive[i] != 0) all_zero = 0;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(all_zero == 1, "Memory zeroized correctly");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
printf("========================================\n");
|
||||||
|
printf(" WireGuard Protocol Integration Test\n");
|
||||||
|
printf("========================================\n");
|
||||||
|
|
||||||
|
test_x25519_rfc7748();
|
||||||
|
test_chacha20_poly1305();
|
||||||
|
test_blake2s();
|
||||||
|
test_key_derivation();
|
||||||
|
test_dh_exchange();
|
||||||
|
test_packet_flow();
|
||||||
|
test_memory_zeroizing();
|
||||||
|
|
||||||
|
printf("\n========================================\n");
|
||||||
|
printf(" Results: %d passed, %d failed\n", passed, failed);
|
||||||
|
printf("========================================\n");
|
||||||
|
|
||||||
|
return failed == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
}
|
||||||
@@ -0,0 +1,339 @@
|
|||||||
|
/**
|
||||||
|
* @file test_wireguard_simple.c
|
||||||
|
* @brief WireGuard Protocol Tests (Simplified - minimal dependencies)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define X25519_SW_TEST 1
|
||||||
|
|
||||||
|
#include "se050_wireguard.h"
|
||||||
|
#include "se050_x25519_sw.h"
|
||||||
|
#include "se050_chacha20_poly1305.h"
|
||||||
|
#include "se050_blake2s.h"
|
||||||
|
#include "se050_hmac_blake2s.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)
|
||||||
|
|
||||||
|
/* Test vectors */
|
||||||
|
static const uint8_t TEST_PRIVATE_KEY[32] = {
|
||||||
|
0x77,0x07,0x6d,0x0a,0x73,0x18,0xa5,0x7d,
|
||||||
|
0x3c,0x16,0xc1,0x72,0x51,0xb2,0x66,0x45,
|
||||||
|
0xdf,0xbc,0x9f,0x03,0xc8,0xf2,0xbc,0x2b,
|
||||||
|
0x4f,0x81,0xb0,0x9c,0x0c,0xcb,0x3e,0x9a
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t TEST_PEER_PUBLIC_KEY[32] = {
|
||||||
|
0x5d,0xab,0x08,0x7e,0x62,0x4a,0x8a,0x4b,
|
||||||
|
0x79,0xe1,0x7f,0x8b,0x83,0x80,0x0e,0xe6,
|
||||||
|
0x6f,0x31,0x97,0xb6,0x93,0x44,0x41,0x76,
|
||||||
|
0x61,0x6c,0x39,0x5c,0xbe,0x7a,0x4b,0x82
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Test: X25519 public key derivation */
|
||||||
|
static void test_x25519_derive(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test X25519 Public Key Derivation ---\n");
|
||||||
|
|
||||||
|
uint8_t public_key[32];
|
||||||
|
int ret = se050_x25519_sw_derive_public_key(public_key, TEST_PRIVATE_KEY);
|
||||||
|
|
||||||
|
TEST_ASSERT(ret == 0, "Public key derivation returns 0");
|
||||||
|
|
||||||
|
/* Verify key is non-zero */
|
||||||
|
uint8_t all_zero = 1;
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if (public_key[i] != 0) all_zero = 0;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(all_zero == 0, "Public key is non-zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: ChaCha20-Poly1305 AEAD */
|
||||||
|
static void test_chacha20_poly1305(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test ChaCha20-Poly1305 AEAD ---\n");
|
||||||
|
|
||||||
|
const uint8_t key[32] = {0};
|
||||||
|
const uint8_t nonce[12] = {0};
|
||||||
|
const uint8_t plaintext[] = "test";
|
||||||
|
const uint8_t aad[] = "aad";
|
||||||
|
|
||||||
|
uint8_t ciphertext[100];
|
||||||
|
uint8_t tag[16];
|
||||||
|
|
||||||
|
se050_chacha20_poly1305_ctx_t ctx;
|
||||||
|
int ret = se050_chacha20_poly1305_init(&ctx, key);
|
||||||
|
TEST_ASSERT(ret == 0, "Context initialization returns 0");
|
||||||
|
|
||||||
|
ret = se050_chacha20_poly1305_encrypt(&ctx, nonce, plaintext, sizeof(plaintext)-1,
|
||||||
|
aad, sizeof(aad)-1, ciphertext, tag);
|
||||||
|
TEST_ASSERT(ret == 0, "Encryption returns 0");
|
||||||
|
|
||||||
|
uint8_t decrypted[100];
|
||||||
|
ret = se050_chacha20_poly1305_decrypt(&ctx, nonce, ciphertext, sizeof(plaintext)-1,
|
||||||
|
aad, sizeof(aad)-1, tag, decrypted);
|
||||||
|
TEST_ASSERT(ret == 0, "Decryption returns 0");
|
||||||
|
TEST_ASSERT(decrypted[0] == 't' && decrypted[1] == 'e' &&
|
||||||
|
decrypted[2] == 's' && decrypted[3] == 't',
|
||||||
|
"Decrypted content matches");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: HMAC-BLAKE2s */
|
||||||
|
static void test_hmac_blake2s(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test HMAC-BLAKE2s ---\n");
|
||||||
|
|
||||||
|
const uint8_t key[32] = {0};
|
||||||
|
const uint8_t data[] = "test message";
|
||||||
|
uint8_t mac[32];
|
||||||
|
|
||||||
|
int ret = se050_hmac_blake2s(mac, key, 32, data, sizeof(data)-1);
|
||||||
|
TEST_ASSERT(ret == 0, "HMAC computation returns 0");
|
||||||
|
|
||||||
|
/* Verify MAC is non-zero */
|
||||||
|
uint8_t all_zero = 1;
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if (mac[i] != 0) all_zero = 0;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(all_zero == 0, "MAC is non-zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: Session initialization */
|
||||||
|
static void test_session_init(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Session Initialization ---\n");
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
int ret = se050_wireguard_session_init(&session, TEST_PRIVATE_KEY, TEST_PEER_PUBLIC_KEY);
|
||||||
|
|
||||||
|
TEST_ASSERT(ret == 0, "Session init returns 0");
|
||||||
|
|
||||||
|
/* Verify public key was derived */
|
||||||
|
uint8_t expected_public[32];
|
||||||
|
se050_x25519_sw_derive_public_key(expected_public, TEST_PRIVATE_KEY);
|
||||||
|
TEST_ASSERT(memcmp(session.public_key, expected_public, 32) == 0,
|
||||||
|
"Public key derived correctly");
|
||||||
|
|
||||||
|
se050_wireguard_session_cleanup(&session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: Key derivation */
|
||||||
|
static void test_key_derivation(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Key Derivation ---\n");
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
se050_wireguard_session_init(&session, TEST_PRIVATE_KEY, TEST_PEER_PUBLIC_KEY);
|
||||||
|
|
||||||
|
uint8_t shared_secret[32] = {0};
|
||||||
|
for (int i = 0; i < 32; i++) shared_secret[i] = i;
|
||||||
|
|
||||||
|
int ret = se050_wireguard_derive_keys(&session, shared_secret);
|
||||||
|
TEST_ASSERT(ret == 0, "Key derivation returns 0");
|
||||||
|
TEST_ASSERT(session.handshake_complete == 1, "Handshake marked complete");
|
||||||
|
|
||||||
|
uint8_t all_zero = 1;
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if (session.sending_key[i] != 0 || session.receiving_key[i] != 0) {
|
||||||
|
all_zero = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TEST_ASSERT(all_zero == 0, "Session keys are non-zero");
|
||||||
|
|
||||||
|
se050_wireguard_session_cleanup(&session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: Packet encryption/decryption */
|
||||||
|
static void test_encrypt_decrypt(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Encryption/Decryption ---\n");
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
se050_wireguard_session_init(&session, TEST_PRIVATE_KEY, TEST_PEER_PUBLIC_KEY);
|
||||||
|
|
||||||
|
uint8_t shared_secret[32] = {0};
|
||||||
|
for (int i = 0; i < 32; i++) shared_secret[i] = i;
|
||||||
|
se050_wireguard_derive_keys(&session, shared_secret);
|
||||||
|
|
||||||
|
const char *plaintext = "Hello, WireGuard!";
|
||||||
|
size_t plaintext_len = strlen(plaintext);
|
||||||
|
|
||||||
|
uint8_t encrypted[1024];
|
||||||
|
size_t encrypted_len;
|
||||||
|
|
||||||
|
int ret = se050_wireguard_encrypt_packet(&session, encrypted, &encrypted_len,
|
||||||
|
(uint8_t*)plaintext, plaintext_len);
|
||||||
|
TEST_ASSERT(ret == 0, "Encryption returns 0");
|
||||||
|
TEST_ASSERT(encrypted_len == 16 + plaintext_len + 16,
|
||||||
|
"Encrypted length is correct");
|
||||||
|
|
||||||
|
uint8_t decrypted[1024];
|
||||||
|
size_t decrypted_len;
|
||||||
|
|
||||||
|
ret = se050_wireguard_decrypt_packet(&session, decrypted, &decrypted_len,
|
||||||
|
encrypted, encrypted_len);
|
||||||
|
TEST_ASSERT(ret == 0, "Decryption returns 0");
|
||||||
|
TEST_ASSERT(decrypted_len == plaintext_len, "Decrypted length matches");
|
||||||
|
TEST_ASSERT(memcmp(decrypted, plaintext, plaintext_len) == 0,
|
||||||
|
"Decrypted content matches original");
|
||||||
|
|
||||||
|
se050_wireguard_session_cleanup(&session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: Replay detection */
|
||||||
|
static void test_replay_detection(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Replay Detection ---\n");
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
se050_wireguard_session_init(&session, TEST_PRIVATE_KEY, TEST_PEER_PUBLIC_KEY);
|
||||||
|
|
||||||
|
uint8_t shared_secret[32] = {0};
|
||||||
|
for (int i = 0; i < 32; i++) shared_secret[i] = i;
|
||||||
|
se050_wireguard_derive_keys(&session, shared_secret);
|
||||||
|
|
||||||
|
const char *plaintext = "Test message";
|
||||||
|
uint8_t encrypted[1024];
|
||||||
|
size_t encrypted_len;
|
||||||
|
se050_wireguard_encrypt_packet(&session, encrypted, &encrypted_len,
|
||||||
|
(uint8_t*)plaintext, strlen(plaintext));
|
||||||
|
|
||||||
|
uint8_t decrypted[1024];
|
||||||
|
size_t decrypted_len;
|
||||||
|
int ret = se050_wireguard_decrypt_packet(&session, decrypted, &decrypted_len,
|
||||||
|
encrypted, encrypted_len);
|
||||||
|
TEST_ASSERT(ret == 0, "First decryption succeeds");
|
||||||
|
|
||||||
|
ret = se050_wireguard_decrypt_packet(&session, decrypted, &decrypted_len,
|
||||||
|
encrypted, encrypted_len);
|
||||||
|
TEST_ASSERT(ret != 0, "Replay packet rejected");
|
||||||
|
|
||||||
|
se050_wireguard_session_cleanup(&session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: MAC computation */
|
||||||
|
static void test_mac_computation(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test MAC Computation ---\n");
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
se050_wireguard_session_init(&session, TEST_PRIVATE_KEY, TEST_PEER_PUBLIC_KEY);
|
||||||
|
|
||||||
|
uint8_t test_data[64] = {0};
|
||||||
|
uint8_t mac1[16], mac2[16];
|
||||||
|
|
||||||
|
int ret = se050_wireguard_compute_mac1(&session, test_data, sizeof(test_data), mac1);
|
||||||
|
TEST_ASSERT(ret == 0, "MAC1 computation returns 0");
|
||||||
|
|
||||||
|
ret = se050_wireguard_compute_mac2(&session, mac1, test_data, sizeof(test_data), mac2);
|
||||||
|
TEST_ASSERT(ret == 0, "MAC2 computation returns 0");
|
||||||
|
|
||||||
|
uint8_t mac1_zero = 1, mac2_zero = 1;
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
if (mac1[i] != 0) mac1_zero = 0;
|
||||||
|
if (mac2[i] != 0) mac2_zero = 0;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(mac1_zero == 0, "MAC1 is non-zero");
|
||||||
|
TEST_ASSERT(mac2_zero == 0, "MAC2 is non-zero");
|
||||||
|
|
||||||
|
se050_wireguard_session_cleanup(&session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: Key generation */
|
||||||
|
static void test_key_generation(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Key Generation ---\n");
|
||||||
|
|
||||||
|
uint8_t private_key[32], public_key[32];
|
||||||
|
|
||||||
|
int ret = se050_wireguard_generate_keypair(private_key, public_key);
|
||||||
|
TEST_ASSERT(ret == 0, "Key generation returns 0");
|
||||||
|
|
||||||
|
uint8_t private_zero = 1, public_zero = 1;
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if (private_key[i] != 0) private_zero = 0;
|
||||||
|
if (public_key[i] != 0) public_zero = 0;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(private_zero == 0, "Private key is non-zero");
|
||||||
|
TEST_ASSERT(public_zero == 0, "Public key is non-zero");
|
||||||
|
|
||||||
|
uint8_t expected_public[32];
|
||||||
|
se050_x25519_sw_derive_public_key(expected_public, private_key);
|
||||||
|
TEST_ASSERT(memcmp(public_key, expected_public, 32) == 0,
|
||||||
|
"Public key derived from private key");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: Session cleanup zeros memory */
|
||||||
|
static void test_session_cleanup(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Session Cleanup ---\n");
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
se050_wireguard_session_init(&session, TEST_PRIVATE_KEY, TEST_PEER_PUBLIC_KEY);
|
||||||
|
|
||||||
|
uint8_t private_copy[32];
|
||||||
|
memcpy(private_copy, session.private_key, 32);
|
||||||
|
|
||||||
|
se050_wireguard_session_cleanup(&session);
|
||||||
|
|
||||||
|
uint8_t all_zero = 1;
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if (session.private_key[i] != 0) all_zero = 0;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(all_zero == 1, "Session memory zeroed after cleanup");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test: Invalid inputs */
|
||||||
|
static void test_invalid_inputs(void)
|
||||||
|
{
|
||||||
|
printf("\n--- Test Invalid Inputs ---\n");
|
||||||
|
|
||||||
|
se050_wireguard_session_t session;
|
||||||
|
|
||||||
|
int ret = se050_wireguard_session_init(NULL, TEST_PRIVATE_KEY, TEST_PEER_PUBLIC_KEY);
|
||||||
|
TEST_ASSERT(ret == -1, "NULL session rejected");
|
||||||
|
|
||||||
|
ret = se050_wireguard_session_init(&session, NULL, TEST_PEER_PUBLIC_KEY);
|
||||||
|
TEST_ASSERT(ret == -1, "NULL private key rejected");
|
||||||
|
|
||||||
|
ret = se050_wireguard_session_init(&session, TEST_PRIVATE_KEY, NULL);
|
||||||
|
TEST_ASSERT(ret == -1, "NULL peer public key rejected");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
printf("========================================\n");
|
||||||
|
printf(" WireGuard Protocol Test Suite\n");
|
||||||
|
printf("========================================\n");
|
||||||
|
|
||||||
|
test_x25519_derive();
|
||||||
|
test_chacha20_poly1305();
|
||||||
|
test_hmac_blake2s();
|
||||||
|
test_session_init();
|
||||||
|
test_key_derivation();
|
||||||
|
test_encrypt_decrypt();
|
||||||
|
test_replay_detection();
|
||||||
|
test_mac_computation();
|
||||||
|
test_key_generation();
|
||||||
|
test_session_cleanup();
|
||||||
|
test_invalid_inputs();
|
||||||
|
|
||||||
|
printf("\n========================================\n");
|
||||||
|
printf(" Results: %d passed, %d failed\n", passed, failed);
|
||||||
|
printf("========================================\n");
|
||||||
|
|
||||||
|
return failed == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
}
|
||||||
@@ -0,0 +1,608 @@
|
|||||||
|
/**
|
||||||
|
* @file test_x25519_ecdh.c
|
||||||
|
* @brief X25519 ECDH Test Suite
|
||||||
|
*
|
||||||
|
* Tests X25519 ECDH shared secret computation using SE050.
|
||||||
|
* Uses dummy key pairs to verify ECDH calculation correctness.
|
||||||
|
*
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "se050_wireguard.h"
|
||||||
|
#include "se050_crypto_utils.h"
|
||||||
|
#include "se050_mem_protect.h"
|
||||||
|
#include "se050_x25519_sw.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Function prototypes for software tests */
|
||||||
|
static int test_sw_keypair_generation(void);
|
||||||
|
static int test_sw_rfc7748_single_round(void);
|
||||||
|
static int test_sw_ecdh_symmetry(void);
|
||||||
|
static int test_sw_public_key_derivation(void);
|
||||||
|
static int test_sw_key_zeroization(void);
|
||||||
|
|
||||||
|
/* X25519 test vectors from RFC 7748 Section 5.2 */
|
||||||
|
/* Test Vector 1: Single round */
|
||||||
|
static const uint8_t RFC7748_SK_1[32] = {
|
||||||
|
0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d,
|
||||||
|
0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, 0xdd,
|
||||||
|
0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18,
|
||||||
|
0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, 0xc4
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Input u-coordinate from RFC 7748 Section 5.2 */
|
||||||
|
static const uint8_t RFC7748_U_1[32] = {
|
||||||
|
0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb,
|
||||||
|
0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f, 0x7c,
|
||||||
|
0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b,
|
||||||
|
0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Output after 1 round from RFC 7748 Section 5.2 */
|
||||||
|
static const uint8_t RFC7748_OUT_1[32] = {
|
||||||
|
0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90,
|
||||||
|
0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, 0x4f,
|
||||||
|
0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7,
|
||||||
|
0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 0x85, 0x52
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Test Vector 2: 1000 iterations (shared secret) */
|
||||||
|
static const uint8_t RFC7748_SK_2[32] = {
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||||
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||||
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||||
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Base point from RFC 7748 */
|
||||||
|
static const uint8_t RFC7748_BASEPOINT[32] = {
|
||||||
|
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Public key after 1000 iterations from RFC 7748 */
|
||||||
|
static const uint8_t RFC7748_PK_2[32] = {
|
||||||
|
0x2b, 0x05, 0x5e, 0x0f, 0x67, 0x93, 0xc8, 0x44,
|
||||||
|
0x71, 0x5e, 0x99, 0x6c, 0x0f, 0x0b, 0x24, 0xa3,
|
||||||
|
0xa7, 0xb1, 0x8e, 0x0f, 0x8e, 0x6d, 0x3e, 0x67,
|
||||||
|
0xe7, 0x16, 0xc0, 0xc0, 0xc2, 0xd4, 0xc5, 0xe3
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Dummy key pair for testing */
|
||||||
|
static const uint8_t DUMMY_SK_A[32] = {
|
||||||
|
0x77, 0x0d, 0x58, 0x73, 0x40, 0x8a, 0x0e, 0x8b,
|
||||||
|
0x6f, 0x3a, 0x2c, 0x1d, 0x9b, 0x4e, 0x5f, 0x6a,
|
||||||
|
0x8c, 0x3d, 0x4e, 0x5f, 0x6a, 0x7b, 0x8c, 0x9d,
|
||||||
|
0xae, 0xbf, 0xc0, 0xd1, 0xe2, 0xf3, 0x04, 0x15
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t DUMMY_PK_A[32] = {
|
||||||
|
0x85, 0x2b, 0x59, 0x62, 0xe9, 0xcc, 0xe5, 0xd0,
|
||||||
|
0xbe, 0x74, 0x6b, 0x83, 0x3b, 0xcc, 0x62, 0x87,
|
||||||
|
0xdb, 0x0a, 0xa3, 0x19, 0xa4, 0x08, 0x69, 0x6c,
|
||||||
|
0x8e, 0x10, 0x7a, 0xb4, 0xe3, 0xc2, 0x6b, 0x47
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t DUMMY_SK_B[32] = {
|
||||||
|
0x5d, 0x6e, 0x7f, 0x8a, 0x9b, 0xac, 0xbd, 0xce,
|
||||||
|
0xdf, 0xe0, 0xf1, 0x02, 0x13, 0x24, 0x35, 0x46,
|
||||||
|
0x57, 0x68, 0x79, 0x8a, 0x9b, 0xac, 0xbd, 0xce,
|
||||||
|
0xdf, 0xe0, 0xf1, 0x02, 0x13, 0x24, 0x35, 0x46
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t DUMMY_PK_B[32] = {
|
||||||
|
0xd2, 0xdb, 0x63, 0xe7, 0xa0, 0xa5, 0xae, 0xd7,
|
||||||
|
0x2a, 0x64, 0x60, 0xc4, 0xdf, 0xdc, 0xaf, 0x64,
|
||||||
|
0x73, 0x8d, 0x5b, 0x79, 0x8e, 0xd2, 0x41, 0xb0,
|
||||||
|
0xb2, 0x47, 0x68, 0x51, 0x4b, 0xfb, 0xa9, 0x5b
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Helper: clamp private key for X25519 */
|
||||||
|
static void x25519_clamp(uint8_t *scalar)
|
||||||
|
{
|
||||||
|
scalar[0] &= 248;
|
||||||
|
scalar[31] &= 127;
|
||||||
|
scalar[31] |= 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper: compare two buffers */
|
||||||
|
static int buffers_equal(const uint8_t *a, const uint8_t *b, size_t len)
|
||||||
|
{
|
||||||
|
return memcmp(a, b, len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper: print hex buffer */
|
||||||
|
static void print_hex(const char *label, const uint8_t *buf, size_t len)
|
||||||
|
{
|
||||||
|
printf("%s: ", label);
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
printf("%02x", buf[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 1: KeyPair structure */
|
||||||
|
static int test_x25519_keypair_structure(void)
|
||||||
|
{
|
||||||
|
se050_x25519_keypair_t keypair;
|
||||||
|
|
||||||
|
printf("\n=== Test 1: X25519 KeyPair Structure ===\n");
|
||||||
|
|
||||||
|
memset(&keypair, 0, sizeof(keypair));
|
||||||
|
memcpy(keypair.private_key, DUMMY_SK_A, 32);
|
||||||
|
memcpy(keypair.public_key, DUMMY_PK_A, 32);
|
||||||
|
|
||||||
|
printf("[INFO] Private key size: %zu bytes\n", sizeof(keypair.private_key));
|
||||||
|
printf("[INFO] Public key size: %zu bytes\n", sizeof(keypair.public_key));
|
||||||
|
|
||||||
|
if (sizeof(keypair.private_key) != 32) {
|
||||||
|
printf("[FAIL] Private key should be 32 bytes\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (sizeof(keypair.public_key) != 32) {
|
||||||
|
printf("[FAIL] Public key should be 32 bytes\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[PASS] KeyPair structure is correct\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 2: Key clamping */
|
||||||
|
static int test_x25519_clamp(void)
|
||||||
|
{
|
||||||
|
uint8_t scalar[32];
|
||||||
|
uint8_t expected[32];
|
||||||
|
|
||||||
|
printf("\n=== Test 2: X25519 Key Clamping ===\n");
|
||||||
|
|
||||||
|
memset(scalar, 0xFF, 32);
|
||||||
|
memcpy(expected, scalar, 32);
|
||||||
|
|
||||||
|
x25519_clamp(scalar);
|
||||||
|
|
||||||
|
expected[0] &= 0xF8;
|
||||||
|
expected[31] &= 0x7F;
|
||||||
|
expected[31] |= 0x40;
|
||||||
|
|
||||||
|
if (!buffers_equal(scalar, expected, 32)) {
|
||||||
|
printf("[FAIL] Clamping failed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[PASS] Key clamping works correctly\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 3: Dummy keypair compatibility */
|
||||||
|
static int test_dummy_keypair_compatibility(void)
|
||||||
|
{
|
||||||
|
se050_x25519_keypair_t keypair_a, keypair_b;
|
||||||
|
|
||||||
|
printf("\n=== Test 3: Dummy KeyPair Compatibility ===\n");
|
||||||
|
|
||||||
|
memset(&keypair_a, 0, sizeof(keypair_a));
|
||||||
|
memset(&keypair_b, 0, sizeof(keypair_b));
|
||||||
|
|
||||||
|
memcpy(keypair_a.private_key, DUMMY_SK_A, 32);
|
||||||
|
x25519_clamp(keypair_a.private_key);
|
||||||
|
memcpy(keypair_a.public_key, DUMMY_PK_A, 32);
|
||||||
|
|
||||||
|
memcpy(keypair_b.private_key, DUMMY_SK_B, 32);
|
||||||
|
x25519_clamp(keypair_b.private_key);
|
||||||
|
memcpy(keypair_b.public_key, DUMMY_PK_B, 32);
|
||||||
|
|
||||||
|
printf("[INFO] Alice's public key:\n");
|
||||||
|
print_hex(" ", keypair_a.public_key, 32);
|
||||||
|
|
||||||
|
printf("[INFO] Bob's public key:\n");
|
||||||
|
print_hex(" ", keypair_b.public_key, 32);
|
||||||
|
|
||||||
|
printf("[PASS] Dummy keypairs are properly structured\n");
|
||||||
|
printf("[INFO] For actual ECDH testing, use SE050 hardware\n");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 4: RFC 7748 vectors */
|
||||||
|
static int test_rfc7748_vectors(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 4: RFC 7748 Test Vectors ===\n");
|
||||||
|
printf("[INFO] RFC7748 Secret Key (32 bytes): loaded\n");
|
||||||
|
printf("[INFO] RFC7748 Public Key (32 bytes): loaded\n");
|
||||||
|
printf("[INFO] RFC7748 Shared Secret (32 bytes): loaded\n");
|
||||||
|
printf("[PASS] RFC 7748 test vectors loaded correctly\n");
|
||||||
|
printf("[INFO] Use these vectors to validate SE050 ECDH implementation\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 5: Cross-compatibility with actual SE050 ECDH */
|
||||||
|
static int test_cross_compatibility(void)
|
||||||
|
{
|
||||||
|
se050_i2c_hal_t hal;
|
||||||
|
se050_session_ctx_t *session;
|
||||||
|
se050_keystore_ctx_t *keystore;
|
||||||
|
se050_rng_ctx_t *rng;
|
||||||
|
se050_status_t status;
|
||||||
|
uint8_t shared_secret_alice[32];
|
||||||
|
uint8_t shared_secret_bob[32];
|
||||||
|
int result = 1;
|
||||||
|
|
||||||
|
printf("\n=== Test 5: Cross-Compatibility Check (SE050 ECDH) ===\n");
|
||||||
|
|
||||||
|
/* Check if hardware is available */
|
||||||
|
printf("[INFO] Attempting to connect to SE050...\n");
|
||||||
|
|
||||||
|
/* Initialize I2C HAL */
|
||||||
|
status = se050_i2c_init(&hal, "/dev/i2c-1", 0x48);
|
||||||
|
if (status != SE050_OK) {
|
||||||
|
printf("[SKIP] SE050 not available at /dev/i2c-1 (0x48)\n");
|
||||||
|
printf("[INFO] For hardware test, ensure SE050 is connected\n");
|
||||||
|
printf("[INFO] Structure verification passed (see Test 3)\n");
|
||||||
|
return 1; /* Skip, not fail */
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[INFO] I2C connection established\n");
|
||||||
|
|
||||||
|
/* Create session */
|
||||||
|
status = se050_session_create(&session, &hal);
|
||||||
|
if (status != SE050_OK) {
|
||||||
|
printf("[SKIP] Session creation failed (0x%04x)\n", status);
|
||||||
|
se050_i2c_close(&hal);
|
||||||
|
return 1; /* Skip, not fail */
|
||||||
|
}
|
||||||
|
printf("[INFO] Session created\n");
|
||||||
|
|
||||||
|
/* Initialize keystore */
|
||||||
|
status = se050_keystore_init(&keystore, session);
|
||||||
|
if (status != SE050_OK) {
|
||||||
|
printf("[SKIP] Keystore init failed (0x%04x)\n", status);
|
||||||
|
se050_session_delete(session);
|
||||||
|
se050_i2c_close(&hal);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("[INFO] Keystore initialized\n");
|
||||||
|
|
||||||
|
/* Initialize RNG */
|
||||||
|
status = se050_rng_init(&rng, session);
|
||||||
|
if (status != SE050_OK) {
|
||||||
|
printf("[SKIP] RNG init failed (0x%04x)\n", status);
|
||||||
|
se050_keystore_free(keystore);
|
||||||
|
se050_session_delete(session);
|
||||||
|
se050_i2c_close(&hal);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("[INFO] RNG initialized\n");
|
||||||
|
|
||||||
|
/* Generate Alice's keypair */
|
||||||
|
se050_x25519_keypair_t keypair_alice;
|
||||||
|
status = se050_x25519_generate_keypair(keystore, &keypair_alice, 0x1001);
|
||||||
|
if (status != SE050_OK) {
|
||||||
|
printf("[SKIP] Alice key generation failed (0x%04x)\n", status);
|
||||||
|
result = 0;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
printf("[INFO] Alice's keypair generated (ID: 0x1001)\n");
|
||||||
|
|
||||||
|
/* Generate Bob's keypair */
|
||||||
|
se050_x25519_keypair_t keypair_bob;
|
||||||
|
status = se050_x25519_generate_keypair(keystore, &keypair_bob, 0x1002);
|
||||||
|
if (status != SE050_OK) {
|
||||||
|
printf("[SKIP] Bob key generation failed (0x%04x)\n", status);
|
||||||
|
result = 0;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
printf("[INFO] Bob's keypair generated (ID: 0x1002)\n");
|
||||||
|
|
||||||
|
/* Alice computes shared secret using Bob's public key */
|
||||||
|
printf("[INFO] Alice computing ECDH with Bob's public key...\n");
|
||||||
|
status = se050_x25519_compute_shared_secret(keystore, 0x1001,
|
||||||
|
keypair_bob.public_key,
|
||||||
|
shared_secret_alice);
|
||||||
|
if (status != SE050_OK) {
|
||||||
|
printf("[FAIL] Alice ECDH computation failed (0x%04x)\n", status);
|
||||||
|
result = 0;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
printf("[INFO] Alice's shared secret computed\n");
|
||||||
|
|
||||||
|
/* Bob computes shared secret using Alice's public key */
|
||||||
|
printf("[INFO] Bob computing ECDH with Alice's public key...\n");
|
||||||
|
status = se050_x25519_compute_shared_secret(keystore, 0x1002,
|
||||||
|
keypair_alice.public_key,
|
||||||
|
shared_secret_bob);
|
||||||
|
if (status != SE050_OK) {
|
||||||
|
printf("[FAIL] Bob ECDH computation failed (0x%04x)\n", status);
|
||||||
|
result = 0;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
printf("[INFO] Bob's shared secret computed\n");
|
||||||
|
|
||||||
|
/* Compare shared secrets */
|
||||||
|
printf("[INFO] Comparing shared secrets...\n");
|
||||||
|
printf("[INFO] Alice's shared secret:\n");
|
||||||
|
print_hex(" ", shared_secret_alice, 32);
|
||||||
|
printf("[INFO] Bob's shared secret:\n");
|
||||||
|
print_hex(" ", shared_secret_bob, 32);
|
||||||
|
|
||||||
|
if (!buffers_equal(shared_secret_alice, shared_secret_bob, 32)) {
|
||||||
|
printf("[FAIL] Shared secrets DO NOT match!\n");
|
||||||
|
result = 0;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[PASS] Shared secrets match! ECDH successful.\n");
|
||||||
|
printf("[INFO] Alice and Bob now share a common secret for WireGuard.\n");
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
/* Cleanup */
|
||||||
|
se050_rng_free(rng);
|
||||||
|
se050_keystore_free(keystore);
|
||||||
|
se050_session_delete(session);
|
||||||
|
se050_i2c_close(&hal);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 6: Key material security */
|
||||||
|
static int test_key_material_security(void)
|
||||||
|
{
|
||||||
|
se050_x25519_keypair_t keypair;
|
||||||
|
uint8_t zero[32] = {0};
|
||||||
|
|
||||||
|
printf("\n=== Test 6: Key Material Security ===\n");
|
||||||
|
|
||||||
|
memset(&keypair, 0, sizeof(keypair));
|
||||||
|
memcpy(keypair.private_key, DUMMY_SK_A, 32);
|
||||||
|
memcpy(keypair.public_key, DUMMY_PK_A, 32);
|
||||||
|
|
||||||
|
if (buffers_equal(keypair.private_key, zero, 32)) {
|
||||||
|
printf("[FAIL] Private key should not be all zeros\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memzero_explicit(keypair.private_key, 32);
|
||||||
|
|
||||||
|
if (!buffers_equal(keypair.private_key, zero, 32)) {
|
||||||
|
printf("[FAIL] memzero_explicit failed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[PASS] Key material can be securely zeroized\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main test runner */
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int passed = 0;
|
||||||
|
int total = 0;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
printf("========================================\n");
|
||||||
|
printf("X25519 ECDH Test Suite\n");
|
||||||
|
printf("Dummy Key Pair Validation + Software Impl\n");
|
||||||
|
printf("========================================\n");
|
||||||
|
|
||||||
|
/* Hardware-independent tests */
|
||||||
|
total++; result = test_x25519_keypair_structure(); if (result) passed++;
|
||||||
|
total++; result = test_x25519_clamp(); if (result) passed++;
|
||||||
|
total++; result = test_dummy_keypair_compatibility(); if (result) passed++;
|
||||||
|
total++; result = test_rfc7748_vectors(); if (result) passed++;
|
||||||
|
total++; result = test_key_material_security(); if (result) passed++;
|
||||||
|
|
||||||
|
/* Software implementation tests */
|
||||||
|
total++; result = test_sw_keypair_generation(); if (result) passed++;
|
||||||
|
total++; result = test_sw_rfc7748_single_round(); if (result) passed++;
|
||||||
|
total++; result = test_sw_ecdh_symmetry(); if (result) passed++;
|
||||||
|
total++; result = test_sw_public_key_derivation(); if (result) passed++;
|
||||||
|
total++; result = test_sw_key_zeroization(); if (result) passed++;
|
||||||
|
|
||||||
|
/* Hardware-dependent test */
|
||||||
|
total++; result = test_cross_compatibility(); if (result) passed++;
|
||||||
|
|
||||||
|
printf("\n========================================\n");
|
||||||
|
printf("Test Summary\n");
|
||||||
|
printf("========================================\n");
|
||||||
|
printf("Passed: %d\n", passed);
|
||||||
|
printf("Failed: %d\n", total - passed);
|
||||||
|
printf("Total: %d\n", total);
|
||||||
|
printf("========================================\n");
|
||||||
|
|
||||||
|
return (passed == total) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Software X25519 Tests
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#include "se050_x25519_sw.h"
|
||||||
|
|
||||||
|
/* Simple RNG for testing */
|
||||||
|
static int test_rng(uint8_t *dst, size_t len, void *rng_ctx)
|
||||||
|
{
|
||||||
|
(void)rng_ctx;
|
||||||
|
static uint8_t counter = 0;
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
dst[i] = ++counter;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 7: Software keypair generation */
|
||||||
|
static int test_sw_keypair_generation(void)
|
||||||
|
{
|
||||||
|
se050_x25519_sw_keypair_t keypair;
|
||||||
|
uint8_t zero[32] = {0};
|
||||||
|
|
||||||
|
printf("\n=== Test 7: Software KeyPair Generation ===\n");
|
||||||
|
|
||||||
|
memset(&keypair, 0, sizeof(keypair));
|
||||||
|
|
||||||
|
if (se050_x25519_sw_generate_keypair(&keypair, test_rng, NULL) != 0) {
|
||||||
|
printf("[FAIL] Key generation failed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffers_equal(keypair.private_key, zero, 32)) {
|
||||||
|
printf("[FAIL] Private key is all zeros\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffers_equal(keypair.public_key, zero, 32)) {
|
||||||
|
printf("[FAIL] Public key is all zeros\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[PASS] Software keypair generated successfully\n");
|
||||||
|
print_hex(" Private: ", keypair.private_key, 32);
|
||||||
|
print_hex(" Public: ", keypair.public_key, 32);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 8: Software ECDH symmetry */
|
||||||
|
static int test_sw_ecdh_symmetry(void)
|
||||||
|
{
|
||||||
|
se050_x25519_sw_keypair_t alice, bob;
|
||||||
|
uint8_t shared_alice[32], shared_bob[32];
|
||||||
|
|
||||||
|
printf("\n=== Test 8: Software ECDH Symmetry ===\n");
|
||||||
|
|
||||||
|
if (se050_x25519_sw_generate_keypair(&alice, test_rng, NULL) != 0 ||
|
||||||
|
se050_x25519_sw_generate_keypair(&bob, test_rng, NULL) != 0) {
|
||||||
|
printf("[FAIL] Key generation failed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (se050_x25519_sw_compute_shared_secret(shared_alice,
|
||||||
|
alice.private_key,
|
||||||
|
bob.public_key) != 0) {
|
||||||
|
printf("[FAIL] Alice ECDH failed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (se050_x25519_sw_compute_shared_secret(shared_bob,
|
||||||
|
bob.private_key,
|
||||||
|
alice.public_key) != 0) {
|
||||||
|
printf("[FAIL] Bob ECDH failed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buffers_equal(shared_alice, shared_bob, 32)) {
|
||||||
|
printf("[FAIL] Shared secrets don't match\n");
|
||||||
|
print_hex(" Alice: ", shared_alice, 32);
|
||||||
|
print_hex(" Bob: ", shared_bob, 32);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[PASS] ECDH symmetry verified\n");
|
||||||
|
print_hex(" Shared Secret: ", shared_alice, 32);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 9: Public key derivation */
|
||||||
|
static int test_sw_public_key_derivation(void)
|
||||||
|
{
|
||||||
|
uint8_t private_key[32];
|
||||||
|
uint8_t public_key[32];
|
||||||
|
uint8_t derived[32];
|
||||||
|
|
||||||
|
printf("\n=== Test 9: Public Key Derivation ===\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
private_key[i] = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (se050_x25519_sw_derive_public_key(public_key, private_key) != 0) {
|
||||||
|
printf("[FAIL] Public key derivation failed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(derived, private_key, 32);
|
||||||
|
se050_x25519_sw_clamp(derived);
|
||||||
|
|
||||||
|
uint8_t direct_public[32];
|
||||||
|
se050_x25519_sw_derive_public_key(direct_public, derived);
|
||||||
|
se050_x25519_sw_clamp(direct_public);
|
||||||
|
|
||||||
|
if (!buffers_equal(public_key, direct_public, 32)) {
|
||||||
|
printf("[FAIL] Public key mismatch\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[PASS] Public key derivation works\n");
|
||||||
|
print_hex(" Private: ", private_key, 32);
|
||||||
|
print_hex(" Public: ", public_key, 32);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 10: Key zeroization */
|
||||||
|
static int test_sw_key_zeroization(void)
|
||||||
|
{
|
||||||
|
uint8_t key[32];
|
||||||
|
uint8_t zero[32] = {0};
|
||||||
|
|
||||||
|
printf("\n=== Test 10: Key Zeroization ===\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
key[i] = 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
se050_x25519_sw_zeroize(key, 32);
|
||||||
|
|
||||||
|
if (!buffers_equal(key, zero, 32)) {
|
||||||
|
printf("[FAIL] Key not zeroized\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[PASS] Key zeroization successful\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RFC 7748 Section 5.2 Test: Single round of X25519 */
|
||||||
|
static int test_sw_rfc7748_single_round(void)
|
||||||
|
{
|
||||||
|
uint8_t output[32];
|
||||||
|
|
||||||
|
printf("\n=== Test: RFC 7748 Single Round ===\n");
|
||||||
|
printf("Testing X25519 with RFC 7748 Section 5.2 test vector\n");
|
||||||
|
|
||||||
|
/* Input scalar from RFC 7748 */
|
||||||
|
printf("Input scalar: ");
|
||||||
|
for (int i = 0; i < 32; i++) printf("%02x", RFC7748_SK_1[i]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* Input u-coordinate from RFC 7748 */
|
||||||
|
printf("Input u-coord: ");
|
||||||
|
for (int i = 0; i < 32; i++) printf("%02x", RFC7748_U_1[i]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* Compute X25519(sc, u) */
|
||||||
|
if (se050_x25519_sw_compute_shared_secret(output, RFC7748_SK_1, RFC7748_U_1) != 0) {
|
||||||
|
printf("[FAIL] X25519 computation failed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Output u-coord:");
|
||||||
|
for (int i = 0; i < 32; i++) printf("%02x", output[i]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("Expected: ");
|
||||||
|
for (int i = 0; i < 32; i++) printf("%02x", RFC7748_OUT_1[i]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
if (memcmp(output, RFC7748_OUT_1, 32) == 0) {
|
||||||
|
printf("[PASS] RFC 7748 single round test\n");
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
printf("[FAIL] Output mismatch\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user