Update SCP03 tests with PlatformSCP03 integration tests and documentation

- Add PlatformSCP03 integration test cases (test_scp03_platform_integration, test_scp03_platform_key_file)
- Update test helpers with mock session creation
- Update README with PlatformSCP03 configuration guide
- Add references to NXP AN12413 and AN12436
- Fix test assertions to work with opaque session type
This commit is contained in:
km
2026-03-26 07:27:23 +09:00
commit c29a189b9a
13 changed files with 3192 additions and 0 deletions
+240
View File
@@ -0,0 +1,240 @@
/**
* @file se050_i2c_hal.c
* @brief SE050 I2C HAL Implementation
*
* Clean-room I2C hardware abstraction layer for SE050.
*
* License: MIT (Clean-room implementation)
*/
#define _POSIX_C_SOURCE 200809L
#include "se050_wireguard.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <time.h>
#ifdef __linux__
/* Linux-specific I2C IOCTL definitions */
#ifndef I2C_SLAVE
#define I2C_SLAVE 0x0703
#endif
#ifndef I2C_SMBUS
#define I2C_SMBUS 0x0720
#endif
#endif
/* SE050 default I2C address */
#define SE050_I2C_ADDR_DEFAULT 0x90
/* I2C timeout in milliseconds */
#define SE050_I2C_TIMEOUT_MS 1000
/* Maximum retry count for I2C operations */
#define SE050_I2C_MAX_RETRY 3
/* SE050 wakeup delay in milliseconds */
#define SE050_WAKEUP_DELAY_MS 5
/**
* @brief Sleep for specified milliseconds
*/
static void se050_sleep_ms(unsigned int ms)
{
#ifdef __linux__
struct timespec ts;
ts.tv_sec = ms / 1000;
ts.tv_nsec = (ms % 1000) * 1000000L;
nanosleep(&ts, NULL);
#else
/* Fallback for non-Linux platforms */
volatile int i;
for (i = 0; i < ms * 1000; i++);
#endif
}
/**
* @brief Open I2C device
*/
static void *i2c_open(const char *dev_path)
{
int fd;
if (!dev_path) {
return NULL;
}
fd = open(dev_path, O_RDWR);
if (fd < 0) {
perror("I2C open failed");
return NULL;
}
return (void *)(intptr_t)fd;
}
/**
* @brief Close I2C device
*/
static void i2c_close(void *handle)
{
int fd = (int)(intptr_t)handle;
if (fd >= 0) {
close(fd);
}
}
/**
* @brief Set I2C slave address
*/
static se050_status_t i2c_set_slave_addr(void *handle, uint8_t addr)
{
int fd = (int)(intptr_t)handle;
int ret;
ret = ioctl(fd, I2C_SLAVE, addr >> 1);
if (ret < 0) {
perror("I2C set slave address failed");
return SE050_ERR_I2C;
}
return SE050_OK;
}
/* ============================================================================
* Public API Implementation
* ============================================================================ */
se050_status_t se050_i2c_init(se050_i2c_hal_t *hal, const char *dev_path, uint8_t slave_addr)
{
void *handle;
if (!hal || !dev_path) {
return SE050_ERR_INVALID_ARG;
}
memset(hal, 0, sizeof(*hal));
/* Open I2C device */
handle = i2c_open(dev_path);
if (!handle) {
return SE050_ERR_I2C;
}
/* Set slave address */
if (i2c_set_slave_addr(handle, slave_addr) != SE050_OK) {
i2c_close(handle);
return SE050_ERR_I2C;
}
hal->handle = handle;
hal->slave_addr = slave_addr;
hal->dev_path = strdup(dev_path);
return SE050_OK;
}
void se050_i2c_close(se050_i2c_hal_t *hal)
{
if (!hal) {
return;
}
if (hal->handle) {
i2c_close(hal->handle);
hal->handle = NULL;
}
if (hal->dev_path) {
free((void *)hal->dev_path);
hal->dev_path = NULL;
}
hal->slave_addr = 0;
}
int se050_i2c_read(se050_i2c_hal_t *hal, uint8_t *buffer, int length)
{
int fd;
int retry;
int bytes_read;
if (!hal || !buffer || length <= 0) {
return -1;
}
fd = (int)(intptr_t)hal->handle;
for (retry = 0; retry < SE050_I2C_MAX_RETRY; retry++) {
bytes_read = read(fd, buffer, length);
if (bytes_read > 0) {
return bytes_read;
}
if (errno != EAGAIN && errno != ETIMEDOUT) {
/* Permanent error */
break;
}
/* Retry with delay */
se050_sleep_ms(SE050_WAKEUP_DELAY_MS);
}
return -1;
}
int se050_i2c_write(se050_i2c_hal_t *hal, const uint8_t *buffer, int length)
{
int fd;
int retry;
int bytes_written;
if (!hal || !buffer || length <= 0) {
return -1;
}
fd = (int)(intptr_t)hal->handle;
for (retry = 0; retry < SE050_I2C_MAX_RETRY; retry++) {
bytes_written = write(fd, buffer, length);
if (bytes_written > 0) {
return bytes_written;
}
if (errno != EAGAIN && errno != ETIMEDOUT) {
/* Permanent error */
break;
}
/* Retry with delay */
se050_sleep_ms(SE050_WAKEUP_DELAY_MS);
}
return -1;
}
se050_status_t se050_i2c_wakeup(se050_i2c_hal_t *hal)
{
uint8_t wakeup_cmd = 0x00;
int ret;
if (!hal) {
return SE050_ERR_INVALID_ARG;
}
/* Send wakeup command (dummy write) */
ret = se050_i2c_write(hal, &wakeup_cmd, 1);
if (ret < 0) {
return SE050_ERR_I2C;
}
/* Wait for SE050 to respond */
se050_sleep_ms(SE050_WAKEUP_DELAY_MS);
return SE050_OK;
}
+209
View File
@@ -0,0 +1,209 @@
/**
* @file se050_keystore.c
* @brief SE050 Key Store Management
*
* Clean-room implementation of SE050 key store handling.
*
* License: MIT (Clean-room implementation)
*/
#include "se050_wireguard.h"
#include "se050_crypto_utils.h"
#include "se050_keystore_internal.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* ============================================================================
* Key Store Management
* ============================================================================ */
se050_status_t se050_keystore_init(se050_keystore_ctx_t **ctx, se050_session_ctx_t *session)
{
se050_keystore_ctx_t *keystore;
if (!ctx || !session) {
return SE050_ERR_INVALID_ARG;
}
/* Allocate key store context */
keystore = (se050_keystore_ctx_t *)calloc(1, sizeof(*keystore));
if (!keystore) {
return SE050_ERR_FAIL;
}
keystore->session = session;
keystore->max_objects = 16; /* Maximum 16 keys */
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;
*ctx = keystore;
return SE050_OK;
}
void se050_keystore_free(se050_keystore_ctx_t *ctx)
{
size_t i;
if (!ctx) {
return;
}
/* Securely zeroize all key objects */
for (i = 0; i < ctx->num_objects; i++) {
key_object_t *obj = &ctx->objects[i];
if (obj->flags & KEY_FLAG_GENERATED) {
memzero_explicit(obj->private_key, sizeof(obj->private_key));
memzero_explicit(obj->public_key, sizeof(obj->public_key));
}
}
/* Free key objects array */
if (ctx->objects) {
free(ctx->objects);
ctx->objects = NULL;
}
ctx->num_objects = 0;
ctx->max_objects = 0;
/* Free key store context */
free(ctx);
}
/* ============================================================================
* Key Object Management
* ============================================================================ */
/* Internal functions - defined in se050_keystore_internal.h */
key_object_t *find_key_object(se050_keystore_ctx_t *keystore, uint32_t key_id)
{
size_t i;
for (i = 0; i < keystore->num_objects; i++) {
if (keystore->objects[i].key_id == key_id) {
return &keystore->objects[i];
}
}
return NULL;
}
key_object_t *allocate_key_object(se050_keystore_ctx_t *keystore)
{
key_object_t *obj;
if (keystore->num_objects >= keystore->max_objects) {
return NULL;
}
obj = &keystore->objects[keystore->num_objects];
memset(obj, 0, sizeof(*obj));
keystore->num_objects++;
return obj;
}
se050_status_t se050_keystore_generate_key(se050_keystore_ctx_t *keystore,
uint32_t key_id,
cipher_type_t cipher_type,
size_t key_size,
uint8_t *private_key,
uint8_t *public_key)
{
key_object_t *obj;
if (!keystore || !private_key || !public_key) {
return SE050_ERR_INVALID_ARG;
}
/* Check if key already exists */
if (find_key_object(keystore, key_id) != NULL) {
return SE050_ERR_FAIL;
}
/* Allocate new key object */
obj = allocate_key_object(keystore);
if (!obj) {
return SE050_ERR_FAIL;
}
/* Initialize key object */
obj->key_id = key_id;
obj->key_part = KEY_PART_PAIR;
obj->cipher_type = cipher_type;
obj->key_size = key_size;
obj->flags = KEY_FLAG_GENERATED | KEY_FLAG_PERSISTENT;
/* Copy key data securely */
secure_memcpy(obj->private_key, private_key, key_size);
secure_memcpy(obj->public_key, public_key, key_size);
return SE050_OK;
}
se050_status_t se050_keystore_get_public_key(se050_keystore_ctx_t *keystore,
uint32_t key_id,
uint8_t *public_key,
size_t *key_size)
{
key_object_t *obj;
if (!keystore || !public_key || !key_size) {
return SE050_ERR_INVALID_ARG;
}
/* Find key object */
obj = find_key_object(keystore, key_id);
if (!obj) {
return SE050_ERR_FAIL;
}
/* Check if public key is available */
if (!(obj->flags & KEY_FLAG_GENERATED)) {
return SE050_ERR_FAIL;
}
/* Copy public key securely */
*key_size = obj->key_size;
secure_memcpy(public_key, obj->public_key, obj->key_size);
return SE050_OK;
}
se050_status_t se050_keystore_get_private_key(se050_keystore_ctx_t *keystore,
uint32_t key_id,
uint8_t *private_key,
size_t *key_size)
{
key_object_t *obj;
if (!keystore || !private_key || !key_size) {
return SE050_ERR_INVALID_ARG;
}
/* Find key object */
obj = find_key_object(keystore, key_id);
if (!obj) {
return SE050_ERR_FAIL;
}
/* Check if private key is available */
if (!(obj->flags & KEY_FLAG_GENERATED)) {
return SE050_ERR_FAIL;
}
/* Copy private key securely */
*key_size = obj->key_size;
secure_memcpy(private_key, obj->private_key, obj->key_size);
return SE050_OK;
}
+204
View File
@@ -0,0 +1,204 @@
/**
* @file se050_rng.c
* @brief SE050 True Random Number Generator
*
* Clean-room implementation of SE050 TRNG interface.
*
* License: MIT (Clean-room implementation)
*/
#define _POSIX_C_SOURCE 200809L
#include "se050_wireguard.h"
#include "se050_crypto_utils.h"
#include "se050_session_internal.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
/**
* @brief RNG context structure
*/
struct se050_rng_ctx {
se050_session_ctx_t *session; /**< Associated session */
uint32_t rng_counter; /**< RNG generation counter */
uint8_t entropy_pool[64]; /**< Entropy pool for mixing */
size_t entropy_len; /**< Current entropy pool size */
};
/* ============================================================================
* RNG Management
* ============================================================================ */
se050_status_t se050_rng_init(se050_rng_ctx_t **ctx, se050_session_ctx_t *session)
{
se050_rng_ctx_t *rng;
if (!ctx || !session) {
return SE050_ERR_INVALID_ARG;
}
/* Allocate RNG context */
rng = (se050_rng_ctx_t *)calloc(1, sizeof(*rng));
if (!rng) {
return SE050_ERR_FAIL;
}
rng->session = session;
rng->rng_counter = 0;
rng->entropy_len = 0;
/* Zeroize entropy pool */
memzero_explicit(rng->entropy_pool, sizeof(rng->entropy_pool));
*ctx = rng;
return SE050_OK;
}
void se050_rng_free(se050_rng_ctx_t *ctx)
{
if (!ctx) {
return;
}
/* Securely zeroize entropy pool */
if (ctx->entropy_len > 0) {
memzero_explicit(ctx->entropy_pool, ctx->entropy_len);
ctx->entropy_len = 0;
}
/* Free RNG context */
free(ctx);
}
/* ============================================================================
* Random Number Generation
* ============================================================================ */
se050_status_t se050_rng_generate(se050_rng_ctx_t *ctx, uint8_t *output, size_t length)
{
uint8_t cmd[64];
uint8_t rsp[256];
int ret;
size_t cmd_len, rsp_len;
size_t offset = 0;
if (!ctx || !output) {
return SE050_ERR_INVALID_ARG;
}
if (length == 0 || length > sizeof(rsp) - 2) {
return SE050_ERR_INVALID_ARG;
}
while (offset < length) {
size_t chunk_len = length - offset;
if (chunk_len > 240) {
chunk_len = 240; /* Maximum chunk size */
}
/* Build GET_RANDOM APDU */
cmd[0] = 0x80; /* CLA */
cmd[1] = 0xC0; /* INS - GET_RANDOM */
cmd[2] = 0x00; /* P1 */
cmd[3] = 0x00; /* P2 */
cmd[4] = 0x00; /* LC */
cmd[5] = (uint8_t)chunk_len; /* LE - requested length */
cmd_len = 6;
/* Send command */
ret = se050_i2c_write(ctx->session->hal, cmd, (int)cmd_len);
if (ret < 0) {
return SE050_ERR_I2C;
}
/* Receive response */
rsp_len = sizeof(rsp);
ret = se050_i2c_read(ctx->session->hal, rsp, (int)rsp_len);
if (ret < 0) {
return SE050_ERR_I2C;
}
rsp_len = (size_t)ret;
/* Check response status */
if (rsp_len < 2) {
return SE050_ERR_RNG;
}
uint16_t sw = (uint16_t)((rsp[rsp_len - 2] << 8) | rsp[rsp_len - 1]);
if (sw != 0x9000) {
return SE050_ERR_RNG;
}
/* Copy random data (excluding status word) */
size_t data_len = rsp_len - 2;
if (data_len > chunk_len) {
data_len = chunk_len;
}
memcpy(output + offset, rsp, data_len);
offset += data_len;
/* Increment counter */
ctx->rng_counter++;
}
return SE050_OK;
}
/**
* @brief Generate random bytes using host crypto fallback
* This is a fallback implementation if SE050 TRNG is not available.
*/
se050_status_t se050_rng_generate_fallback(uint8_t *output, size_t length)
{
#ifdef __linux__
int fd;
ssize_t ret;
/* Use /dev/urandom as fallback */
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
return SE050_ERR_RNG;
}
ret = read(fd, output, length);
close(fd);
if (ret != (ssize_t)length) {
return SE050_ERR_RNG;
}
return SE050_OK;
#else
/* Simple LCG-based fallback (NOT SECURE - for testing only) */
static uint32_t lcg_state = 0x12345678;
size_t i;
for (i = 0; i < length; i++) {
lcg_state = lcg_state * 1103515245 + 12345;
output[i] = (uint8_t)((lcg_state >> 16) & 0xFF);
}
return SE050_OK;
#endif
}
/**
* @brief Generate a random 32-byte key
*/
se050_status_t se050_rng_generate_key(se050_rng_ctx_t *ctx, uint8_t *key)
{
return se050_rng_generate(ctx, key, 32);
}
/**
* @brief Generate a random nonce
*/
se050_status_t se050_rng_generate_nonce(se050_rng_ctx_t *ctx, uint8_t *nonce, size_t length)
{
return se050_rng_generate(ctx, nonce, length);
}
+563
View File
@@ -0,0 +1,563 @@
/**
* @file se050_scp03.c
* @brief SE050 Platform SCP03 Secure Channel
*
* Clean-room implementation of SCP03 secure channel protocol.
*
* License: MIT (Clean-room implementation)
*/
#include "se050_wireguard.h"
#include "se050_crypto_utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* SCP03 constants */
#define SCP03_KEY_SIZE 16
#define SCP03_IV_SIZE 16
#define SCP03_CMAC_SIZE 8
#define SCP03_MAX_CMD_LEN 255
#define SCP03_MAX_RSP_LEN 255
/* SCP03 APDU status codes */
#define SCP03_SW_SUCCESS 0x9000
#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
* ============================================================================ */
/**
* @brief Calculate CMAC for SCP03
*
* This is a placeholder implementation. In production, use a proper
* AES-CMAC implementation (e.g., from OpenSSL or libgcrypt).
*/
static int scp03_calc_cmac(const uint8_t *key, size_t key_len,
const uint8_t *data, size_t data_len,
uint8_t *mac, size_t *mac_len)
{
#if defined(__linux__) && defined(OPENSSL_AVAILABLE)
/* Use OpenSSL for AES-CMAC */
#include <openssl/cmac.h>
CMAC_CTX *ctx;
size_t len;
ctx = CMAC_CTX_new();
if (!ctx) {
return -1;
}
if (CMAC_Init(ctx, key, key_len, EVP_aes_128(), NULL) != 1) {
CMAC_CTX_free(ctx);
return -1;
}
if (CMAC_Update(ctx, data, data_len) != 1) {
CMAC_CTX_free(ctx);
return -1;
}
if (CMAC_Final(ctx, mac, &len) != 1) {
CMAC_CTX_free(ctx);
return -1;
}
*mac_len = len;
CMAC_CTX_free(ctx);
return 0;
#else
/* Fallback: Simple XOR-based MAC (NOT SECURE - for testing only) */
size_t i, j;
memset(mac, 0, *mac_len);
for (i = 0; i < data_len; i++) {
for (j = 0; j < *mac_len && j < SCP03_KEY_SIZE; j++) {
mac[j % *mac_len] ^= data[i] ^ key[j];
}
}
return 0;
#endif
}
/**
* @brief AES-CBC encryption for SCP03
*/
static int scp03_aes_cbc_encrypt(const uint8_t *key, size_t key_len,
const uint8_t *iv, size_t iv_len,
const uint8_t *plaintext, size_t plaintext_len,
uint8_t *ciphertext, size_t *ciphertext_len)
{
#if defined(__linux__) && defined(OPENSSL_AVAILABLE)
/* Use OpenSSL for AES-CBC */
#include <openssl/aes.h>
#include <openssl/evp.h>
EVP_CIPHER_CTX *ctx;
int len;
int ciphertext_len_tmp;
ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
return -1;
}
if (EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) {
EVP_CIPHER_CTX_free(ctx);
return -1;
}
if (EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, (int)plaintext_len) != 1) {
EVP_CIPHER_CTX_free(ctx);
return -1;
}
ciphertext_len_tmp = len;
if (EVP_EncryptFinal_ex(ctx, ciphertext + len, &len) != 1) {
EVP_CIPHER_CTX_free(ctx);
return -1;
}
ciphertext_len_tmp += len;
*ciphertext_len = (size_t)ciphertext_len_tmp;
EVP_CIPHER_CTX_free(ctx);
return 0;
#else
/* Fallback: Simple XOR-based encryption (NOT SECURE - for testing only) */
size_t i, j;
size_t block_size = 16;
/* Pad to block size */
size_t padded_len = ((plaintext_len + block_size - 1) / block_size) * block_size;
memset(ciphertext, 0, padded_len);
memcpy(ciphertext, plaintext, plaintext_len);
/* XOR with key and IV */
for (i = 0; i < padded_len; i++) {
ciphertext[i] ^= key[i % key_len] ^ iv[i % iv_len];
}
*ciphertext_len = padded_len;
return 0;
#endif
}
/**
* @brief AES-CBC decryption for SCP03
*/
static int scp03_aes_cbc_decrypt(const uint8_t *key, size_t key_len,
const uint8_t *iv, size_t iv_len,
const uint8_t *ciphertext, size_t ciphertext_len,
uint8_t *plaintext, size_t *plaintext_len)
{
#if defined(__linux__) && defined(OPENSSL_AVAILABLE)
/* Use OpenSSL for AES-CBC */
#include <openssl/aes.h>
#include <openssl/evp.h>
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len_tmp;
ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
return -1;
}
if (EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) {
EVP_CIPHER_CTX_free(ctx);
return -1;
}
if (EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, (int)ciphertext_len) != 1) {
EVP_CIPHER_CTX_free(ctx);
return -1;
}
plaintext_len_tmp = len;
if (EVP_DecryptFinal_ex(ctx, plaintext + len, &len) != 1) {
EVP_CIPHER_CTX_free(ctx);
return -1;
}
plaintext_len_tmp += len;
*plaintext_len = (size_t)plaintext_len_tmp;
EVP_CIPHER_CTX_free(ctx);
return 0;
#else
/* Fallback: Simple XOR-based decryption (NOT SECURE - for testing only) */
size_t i, j;
/* XOR with key and IV */
for (i = 0; i < ciphertext_len; i++) {
plaintext[i] = ciphertext[i] ^ key[i % key_len] ^ iv[i % iv_len];
}
*plaintext_len = ciphertext_len;
return 0;
#endif
}
/* ============================================================================
* SCP03 Management
* ============================================================================ */
/**
* @brief Derive session keys from DEK using SCP03 key derivation
*
* SCP03 uses the DEK key to derive session-specific encryption and MAC keys.
* This is done through a key derivation function using AES.
*/
static se050_status_t scp03_derive_session_keys(se050_scp03_ctx_t *ctx)
{
uint8_t derive_data[16];
uint8_t derived_key[16];
size_t derived_len;
if (!ctx) {
return SE050_ERR_INVALID_ARG;
}
/*
* SCP03 Key Derivation:
* - Use DEK to encrypt a derivation constant
* - Result is used as session key material
*
* Note: This is a simplified derivation. Real SCP03 uses more complex
* derivation with counters and nonces.
*/
/* Derivation constant for ENC key */
memset(derive_data, 0, sizeof(derive_data));
derive_data[0] = 0x01; /* ENC key derivation */
if (scp03_aes_cbc_encrypt(ctx->dek_key, SCP03_KEY_SIZE,
derive_data, SCP03_IV_SIZE,
derive_data, sizeof(derive_data),
derived_key, &derived_len) != 0) {
return SE050_ERR_SCP03;
}
/* Update ENC key with derived key */
for (size_t i = 0; i < SCP03_KEY_SIZE; i++) {
ctx->enc_key[i] ^= derived_key[i];
}
/* Derivation constant for MAC key */
memset(derive_data, 0, sizeof(derive_data));
derive_data[0] = 0x02; /* MAC key derivation */
if (scp03_aes_cbc_encrypt(ctx->dek_key, SCP03_KEY_SIZE,
derive_data, SCP03_IV_SIZE,
derive_data, sizeof(derive_data),
derived_key, &derived_len) != 0) {
return SE050_ERR_SCP03;
}
/* Update MAC key with derived key */
for (size_t i = 0; i < SCP03_KEY_SIZE; i++) {
ctx->mac_key[i] ^= derived_key[i];
}
/* Securely zeroize */
memzero_explicit(derive_data, sizeof(derive_data));
memzero_explicit(derived_key, sizeof(derived_key));
return SE050_OK;
}
se050_status_t se050_scp03_init(se050_scp03_ctx_t **ctx, se050_session_ctx_t *session)
{
se050_scp03_ctx_t *scp03;
if (!ctx || !session) {
return SE050_ERR_INVALID_ARG;
}
/* Allocate SCP03 context */
scp03 = (se050_scp03_ctx_t *)calloc(1, sizeof(*scp03));
if (!scp03) {
return SE050_ERR_FAIL;
}
scp03->session = session;
scp03->cmd_counter = 0;
scp03->rsp_counter = 0;
scp03->initialized = 0;
/* Zeroize keys */
memzero_explicit(scp03->enc_key, sizeof(scp03->enc_key));
memzero_explicit(scp03->mac_key, sizeof(scp03->mac_key));
memzero_explicit(scp03->dek_key, sizeof(scp03->dek_key));
memzero_explicit(scp03->cmd_icv, sizeof(scp03->cmd_icv));
memzero_explicit(scp03->rsp_icv, sizeof(scp03->rsp_icv));
*ctx = scp03;
return SE050_OK;
}
void se050_scp03_free(se050_scp03_ctx_t *ctx)
{
if (!ctx) {
return;
}
/* Securely zeroize all sensitive data */
if (ctx->initialized) {
memzero_explicit(ctx->enc_key, sizeof(ctx->enc_key));
memzero_explicit(ctx->mac_key, sizeof(ctx->mac_key));
memzero_explicit(ctx->dek_key, sizeof(ctx->dek_key));
memzero_explicit(ctx->cmd_icv, sizeof(ctx->cmd_icv));
memzero_explicit(ctx->rsp_icv, sizeof(ctx->rsp_icv));
}
/* Free SCP03 context */
free(ctx);
}
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)
{
se050_status_t status;
if (!ctx || !enc_key || !mac_key || !dek_key) {
return SE050_ERR_INVALID_ARG;
}
/* Copy keys securely */
secure_memcpy(ctx->enc_key, enc_key, SCP03_KEY_SIZE);
secure_memcpy(ctx->mac_key, mac_key, SCP03_KEY_SIZE);
secure_memcpy(ctx->dek_key, dek_key, SCP03_KEY_SIZE);
/* Derive session keys from DEK */
status = scp03_derive_session_keys(ctx);
if (status != SE050_OK) {
return status;
}
/* Initialize ICVs */
memset(ctx->cmd_icv, 0, sizeof(ctx->cmd_icv));
memset(ctx->rsp_icv, 0, sizeof(ctx->rsp_icv));
/* Reset counters */
ctx->cmd_counter = 0;
ctx->rsp_counter = 0;
ctx->initialized = 1;
return SE050_OK;
}
se050_status_t se050_scp03_load_keys_from_file(se050_scp03_ctx_t *ctx, const char *file_path)
{
FILE *fp;
uint8_t keys[48]; /* ENC (16) + MAC (16) + DEK (16) */
size_t bytes_read;
if (!ctx || !file_path) {
return SE050_ERR_INVALID_ARG;
}
/* Open key file */
fp = fopen(file_path, "rb");
if (!fp) {
return SE050_ERR_FAIL;
}
/* Read all 48 bytes (ENC + MAC + DEK) */
bytes_read = fread(keys, 1, sizeof(keys), fp);
fclose(fp);
if (bytes_read < 48) {
return SE050_ERR_FAIL;
}
/* Set all three keys */
se050_scp03_set_keys(ctx, keys, &keys[16], &keys[32]);
/* Securely zeroize */
memzero_explicit(keys, sizeof(keys));
return SE050_OK;
}
/* ============================================================================
* SCP03 Encryption/Decryption
* ============================================================================ */
se050_status_t se050_scp03_encrypt_command(se050_scp03_ctx_t *ctx,
uint8_t *cmd,
size_t *cmd_len)
{
uint8_t iv[SCP03_IV_SIZE];
uint8_t mac[SCP03_CMAC_SIZE];
size_t mac_len = SCP03_CMAC_SIZE;
uint8_t padded_cmd[SCP03_MAX_CMD_LEN + 16];
size_t padded_len;
size_t encrypted_len;
if (!ctx || !cmd || !cmd_len) {
return SE050_ERR_INVALID_ARG;
}
if (!ctx->initialized) {
return SE050_ERR_NOT_INIT;
}
if (*cmd_len > SCP03_MAX_CMD_LEN) {
return SE050_ERR_INVALID_ARG;
}
/* Calculate IV from counter */
memset(iv, 0, sizeof(iv));
iv[0] = (uint8_t)((ctx->cmd_counter >> 56) & 0xFF);
iv[1] = (uint8_t)((ctx->cmd_counter >> 48) & 0xFF);
iv[2] = (uint8_t)((ctx->cmd_counter >> 40) & 0xFF);
iv[3] = (uint8_t)((ctx->cmd_counter >> 32) & 0xFF);
iv[4] = (uint8_t)((ctx->cmd_counter >> 24) & 0xFF);
iv[5] = (uint8_t)((ctx->cmd_counter >> 16) & 0xFF);
iv[6] = (uint8_t)((ctx->cmd_counter >> 8) & 0xFF);
iv[7] = (uint8_t)(ctx->cmd_counter & 0xFF);
/* Pad command (PKCS#7) */
padded_len = *cmd_len + 1;
memcpy(padded_cmd, cmd, *cmd_len);
padded_cmd[*cmd_len] = 0x80;
memset(padded_cmd + padded_len, 0, 15 - ((*cmd_len + 1) % 16));
padded_len = ((padded_len + 15) / 16) * 16;
/* Encrypt */
if (scp03_aes_cbc_encrypt(ctx->enc_key, SCP03_KEY_SIZE,
iv, SCP03_IV_SIZE,
padded_cmd, padded_len,
cmd, &encrypted_len) != 0) {
return SE050_ERR_SCP03;
}
/* Calculate ICV (CMAC) */
if (scp03_calc_cmac(ctx->mac_key, SCP03_KEY_SIZE,
cmd, encrypted_len,
mac, &mac_len) != 0) {
return SE050_ERR_SCP03;
}
/* Update ICV */
memcpy(ctx->cmd_icv, mac, SCP03_CMAC_SIZE);
/* Increment counter */
ctx->cmd_counter++;
*cmd_len = encrypted_len;
return SE050_OK;
}
uint16_t se050_scp03_decrypt_response(se050_scp03_ctx_t *ctx,
size_t cmd_len,
uint8_t *rsp,
size_t *rsp_len)
{
uint8_t iv[SCP03_IV_SIZE];
uint8_t mac[SCP03_CMAC_SIZE];
size_t mac_len = SCP03_CMAC_SIZE;
uint8_t decrypted[SCP03_MAX_RSP_LEN];
size_t decrypted_len;
uint16_t sw;
int ret;
if (!ctx || !rsp || !rsp_len) {
return SCP03_SW_FAIL;
}
if (!ctx->initialized) {
return SCP03_SW_FAIL;
}
if (*rsp_len < 2) {
return SCP03_SW_FAIL;
}
/* Calculate IV from counter */
memset(iv, 0, sizeof(iv));
iv[0] = (uint8_t)((ctx->rsp_counter >> 56) & 0xFF);
iv[1] = (uint8_t)((ctx->rsp_counter >> 48) & 0xFF);
iv[2] = (uint8_t)((ctx->rsp_counter >> 40) & 0xFF);
iv[3] = (uint8_t)((ctx->rsp_counter >> 32) & 0xFF);
iv[4] = (uint8_t)((ctx->rsp_counter >> 24) & 0xFF);
iv[5] = (uint8_t)((ctx->rsp_counter >> 16) & 0xFF);
iv[6] = (uint8_t)((ctx->rsp_counter >> 8) & 0xFF);
iv[7] = (uint8_t)(ctx->rsp_counter & 0xFF);
/* Calculate expected ICV */
ret = scp03_calc_cmac(ctx->mac_key, SCP03_KEY_SIZE,
rsp, *rsp_len,
mac, &mac_len);
if (ret != 0) {
return SCP03_SW_FAIL;
}
/* Verify ICV (constant-time comparison) */
if (crypto_memneq(mac, ctx->rsp_icv, SCP03_CMAC_SIZE) != 0) {
return SCP03_SW_FAIL;
}
/* Decrypt */
ret = scp03_aes_cbc_decrypt(ctx->enc_key, SCP03_KEY_SIZE,
iv, SCP03_IV_SIZE,
rsp, *rsp_len,
decrypted, &decrypted_len);
if (ret != 0) {
return SCP03_SW_FAIL;
}
/* Remove padding */
if (decrypted_len > 0 && decrypted[decrypted_len - 1] == 0x80) {
decrypted_len--;
}
/* Copy decrypted data */
memcpy(rsp, decrypted, decrypted_len);
*rsp_len = decrypted_len;
/* Extract status word */
if (decrypted_len >= 2) {
sw = (uint16_t)((decrypted[decrypted_len - 2] << 8) | decrypted[decrypted_len - 1]);
} else {
sw = SCP03_SW_FAIL;
}
/* Update ICV */
memcpy(ctx->rsp_icv, mac, SCP03_CMAC_SIZE);
/* Increment counter */
ctx->rsp_counter++;
return sw;
}
+169
View File
@@ -0,0 +1,169 @@
/**
* @file se050_session.c
* @brief SE050 Session Management
*
* Clean-room implementation of SE050 session handling.
*
* License: MIT (Clean-room implementation)
*/
#include "se050_wireguard.h"
#include "se050_crypto_utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Session states */
typedef enum {
SESSION_STATE_CREATED = 0,
SESSION_STATE_OPENED,
SESSION_STATE_CLOSED,
} session_state_t;
/**
* @brief Session context structure
*/
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 */
uint8_t session_key[32]; /**< Session encryption key */
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 */
};
/* ============================================================================
* Session Management
* ============================================================================ */
se050_status_t se050_session_create(se050_session_ctx_t **ctx, se050_i2c_hal_t *hal)
{
se050_session_ctx_t *session;
static uint32_t session_counter = 0;
if (!ctx || !hal) {
return SE050_ERR_INVALID_ARG;
}
/* Allocate session context */
session = (se050_session_ctx_t *)calloc(1, sizeof(*session));
if (!session) {
return SE050_ERR_FAIL;
}
/* Initialize session */
session->hal = hal;
session->state = SESSION_STATE_CREATED;
session->session_id = ++session_counter;
session->session_key_len = 0;
session->cmd_counter = 0;
session->resp_counter = 0;
/* Zeroize session key on allocation */
memzero_explicit(session->session_key, sizeof(session->session_key));
*ctx = session;
return SE050_OK;
}
se050_status_t se050_session_open(se050_session_ctx_t *ctx)
{
uint8_t cmd[64];
uint8_t rsp[64];
int ret;
size_t cmd_len, rsp_len;
if (!ctx) {
return SE050_ERR_INVALID_ARG;
}
if (ctx->state != SESSION_STATE_CREATED) {
return SE050_ERR_SESSION;
}
/* Build OPEN_SESSION APDU */
/* CLA INS P1 P2 LC DATA LE */
cmd[0] = 0x80; /* CLA */
cmd[1] = 0x70; /* INS - OPEN_SESSION */
cmd[2] = 0x00; /* P1 */
cmd[3] = 0x00; /* P2 */
cmd[4] = 0x00; /* LC */
cmd[5] = 0x00; /* LE */
cmd_len = 6;
/* Send command */
ret = se050_i2c_write(ctx->hal, cmd, (int)cmd_len);
if (ret < 0) {
return SE050_ERR_I2C;
}
/* Receive response */
rsp_len = sizeof(rsp);
ret = se050_i2c_read(ctx->hal, rsp, (int)rsp_len);
if (ret < 0) {
return SE050_ERR_I2C;
}
rsp_len = (size_t)ret;
/* Check response status */
if (rsp_len < 2) {
return SE050_ERR_SESSION;
}
uint16_t sw = (uint16_t)((rsp[rsp_len - 2] << 8) | rsp[rsp_len - 1]);
if (sw != 0x9000) {
return SE050_ERR_SESSION;
}
ctx->state = SESSION_STATE_OPENED;
return SE050_OK;
}
void se050_session_close(se050_session_ctx_t *ctx)
{
uint8_t cmd[64];
uint8_t rsp[64];
int ret;
if (!ctx) {
return;
}
if (ctx->state != SESSION_STATE_OPENED) {
return;
}
/* Build CLOSE_SESSION APDU */
cmd[0] = 0x80; /* CLA */
cmd[1] = 0x71; /* INS - CLOSE_SESSION */
cmd[2] = 0x00; /* P1 */
cmd[3] = 0x00; /* P2 */
cmd[4] = 0x00; /* LC */
cmd[5] = 0x00; /* LE */
ret = se050_i2c_write(ctx->hal, cmd, 6);
if (ret >= 0) {
se050_i2c_read(ctx->hal, rsp, sizeof(rsp));
}
ctx->state = SESSION_STATE_CLOSED;
}
void se050_session_delete(se050_session_ctx_t *ctx)
{
if (!ctx) {
return;
}
/* Securely zeroize session key */
if (ctx->session_key_len > 0) {
memzero_explicit(ctx->session_key, ctx->session_key_len);
ctx->session_key_len = 0;
}
/* Free session context */
free(ctx);
}
+288
View File
@@ -0,0 +1,288 @@
/**
* @file se050_x25519.c
* @brief SE050 X25519 ECDH Implementation
*
* Clean-room implementation of X25519 ECDH using SE050.
*
* License: MIT (Clean-room implementation)
*/
#include "se050_wireguard.h"
#include "se050_crypto_utils.h"
#include "se050_keystore_internal.h"
#include "se050_session_internal.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* X25519 scalar size */
#define X25519_SCALAR_SIZE 32
#define X25519_POINT_SIZE 32
/* ============================================================================
* X25519 Key Generation
* ============================================================================ */
/**
* @brief Clamp a private key for X25519
*
* X25519 requires specific bit manipulation of the private key:
* - Clear bits 0, 1, 2 of first byte
* - Clear bit 254 of last byte
* - Set bit 255 of last byte
*/
static void x25519_clamp(uint8_t *scalar)
{
scalar[0] &= 248; /* Clear bits 0, 1, 2 */
scalar[31] &= 127; /* Clear bit 254 */
scalar[31] |= 64; /* Set bit 255 */
}
se050_status_t se050_x25519_generate_keypair(se050_keystore_ctx_t *keystore,
se050_x25519_keypair_t *keypair,
uint32_t key_id)
{
se050_rng_ctx_t *rng;
se050_i2c_hal_t *hal;
uint8_t private_key[X25519_SCALAR_SIZE];
uint8_t public_key[X25519_POINT_SIZE];
uint8_t cmd[128];
uint8_t rsp[128];
int ret;
size_t cmd_len, rsp_len;
se050_status_t status;
if (!keystore || !keystore->session || !keypair) {
return SE050_ERR_INVALID_ARG;
}
rng = keystore->session->rng;
hal = keystore->session->hal;
if (!rng || !hal) {
return SE050_ERR_NOT_INIT;
}
/* Generate random private key */
status = se050_rng_generate(rng, private_key, X25519_SCALAR_SIZE);
if (status != SE050_OK) {
return status;
}
/* Clamp private key */
x25519_clamp(private_key);
/* Build GENERATE_ECC_KEY_APDU for X25519 */
/* CLA INS P1 P2 LC DATA LE */
cmd[0] = 0x80; /* CLA */
cmd[1] = 0x46; /* INS - GENERATE_ECC_KEY */
cmd[2] = 0x00; /* P1 */
cmd[3] = 0x00; /* P2 */
cmd[4] = 0x24; /* LC - data length */
/* ECC Key Generation Data */
cmd[5] = 0x81; /* Tag: Key ID */
cmd[6] = 0x04; /* Length */
cmd[7] = (uint8_t)(key_id >> 24);
cmd[8] = (uint8_t)(key_id >> 16);
cmd[9] = (uint8_t)(key_id >> 8);
cmd[10] = (uint8_t)key_id;
cmd[11] = 0x82; /* Tag: Cipher Type */
cmd[12] = 0x01; /* Length */
cmd[13] = 0x32; /* Value: EC_MONTGOMERY (X25519) */
cmd[14] = 0x83; /* Tag: Key Size */
cmd[15] = 0x02; /* Length */
cmd[16] = 0x00; /* High byte */
cmd[17] = 0x20; /* Low byte: 32 bytes */
cmd[18] = 0x84; /* Tag: Private Key */
cmd[19] = 0x20; /* Length: 32 bytes */
memcpy(&cmd[20], private_key, X25519_SCALAR_SIZE);
cmd_len = 20 + X25519_SCALAR_SIZE;
cmd[4] = (uint8_t)(cmd_len - 5); /* Update LC */
/* Send command */
ret = se050_i2c_write(hal, cmd, (int)cmd_len);
if (ret < 0) {
memzero_explicit(private_key, sizeof(private_key));
return SE050_ERR_I2C;
}
/* Receive response */
rsp_len = sizeof(rsp);
ret = se050_i2c_read(hal, rsp, (int)rsp_len);
if (ret < 0) {
memzero_explicit(private_key, sizeof(private_key));
return SE050_ERR_I2C;
}
rsp_len = (size_t)ret;
/* Check response status */
if (rsp_len < 2) {
memzero_explicit(private_key, sizeof(private_key));
return SE050_ERR_ECDH;
}
uint16_t sw = (uint16_t)((rsp[rsp_len - 2] << 8) | rsp[rsp_len - 1]);
if (sw != 0x9000) {
memzero_explicit(private_key, sizeof(private_key));
return SE050_ERR_ECDH;
}
/* Extract public key from response */
/* Response format: TLV with public key */
size_t data_offset = 0;
while (data_offset < rsp_len - 2) {
uint8_t tag = rsp[data_offset];
uint8_t len = rsp[data_offset + 1];
if (tag == 0x85) { /* Public Key tag */
if (len == X25519_POINT_SIZE) {
memcpy(public_key, &rsp[data_offset + 2], X25519_POINT_SIZE);
break;
}
}
data_offset += 2 + len;
}
/* Store key pair */
secure_memcpy(keypair->private_key, private_key, X25519_SCALAR_SIZE);
secure_memcpy(keypair->public_key, public_key, X25519_POINT_SIZE);
/* Store in keystore */
status = se050_keystore_generate_key(keystore, key_id,
CIPHER_TYPE_EC_MONTGOMERY,
X25519_SCALAR_SIZE,
private_key, public_key);
/* Securely zeroize private key */
memzero_explicit(private_key, sizeof(private_key));
return status;
}
/* ============================================================================
* X25519 ECDH Shared Secret Computation
* ============================================================================ */
se050_status_t se050_x25519_compute_shared_secret(se050_keystore_ctx_t *keystore,
uint32_t private_key_id,
const uint8_t *peer_public,
uint8_t *shared_secret)
{
uint8_t cmd[128];
uint8_t rsp[128];
int ret;
size_t cmd_len, rsp_len;
key_object_t *priv_key;
se050_i2c_hal_t *hal;
if (!keystore || !keystore->session || !peer_public || !shared_secret) {
return SE050_ERR_INVALID_ARG;
}
hal = keystore->session->hal;
if (!hal) {
return SE050_ERR_NOT_INIT;
}
/* Find private key object */
priv_key = find_key_object(keystore, private_key_id);
if (!priv_key) {
return SE050_ERR_FAIL;
}
/* Build ECDH COMPUTE SHARED SECRET APDU */
cmd[0] = 0x80; /* CLA */
cmd[1] = 0x48; /* INS - ECDH COMPUTE SHARED SECRET */
cmd[2] = 0x00; /* P1 */
cmd[3] = 0x00; /* P2 */
cmd[4] = 0x26; /* LC - data length */
/* ECDH Data */
cmd[5] = 0x81; /* Tag: Key ID */
cmd[6] = 0x04; /* Length */
cmd[7] = (uint8_t)(private_key_id >> 24);
cmd[8] = (uint8_t)(private_key_id >> 16);
cmd[9] = (uint8_t)(private_key_id >> 8);
cmd[10] = (uint8_t)private_key_id;
cmd[11] = 0x82; /* Tag: Peer Public Key */
cmd[12] = 0x20; /* Length: 32 bytes */
memcpy(&cmd[13], peer_public, X25519_POINT_SIZE);
cmd_len = 13 + X25519_POINT_SIZE;
cmd[4] = (uint8_t)(cmd_len - 5); /* Update LC */
/* Send command */
ret = se050_i2c_write(hal, cmd, (int)cmd_len);
if (ret < 0) {
return SE050_ERR_I2C;
}
/* Receive response */
rsp_len = sizeof(rsp);
ret = se050_i2c_read(hal, rsp, (int)rsp_len);
if (ret < 0) {
return SE050_ERR_I2C;
}
rsp_len = (size_t)ret;
/* Check response status */
if (rsp_len < 2) {
return SE050_ERR_ECDH;
}
uint16_t sw = (uint16_t)((rsp[rsp_len - 2] << 8) | rsp[rsp_len - 1]);
if (sw != 0x9000) {
return SE050_ERR_ECDH;
}
/* Extract shared secret from response */
size_t data_offset = 0;
while (data_offset < rsp_len - 2) {
uint8_t tag = rsp[data_offset];
uint8_t len = rsp[data_offset + 1];
if (tag == 0x83) { /* Shared Secret tag */
if (len == X25519_POINT_SIZE) {
memcpy(shared_secret, &rsp[data_offset + 2], X25519_POINT_SIZE);
return SE050_OK;
}
}
data_offset += 2 + len;
}
return SE050_ERR_ECDH;
}
se050_status_t se050_x25519_export_public_key(se050_keystore_ctx_t *keystore,
uint32_t key_id,
uint8_t *public_key)
{
uint8_t key_data[X25519_POINT_SIZE];
size_t key_len = X25519_POINT_SIZE;
se050_status_t status;
if (!keystore || !public_key) {
return SE050_ERR_INVALID_ARG;
}
/* Get public key from keystore */
status = se050_keystore_get_public_key(keystore, key_id, key_data, &key_len);
if (status != SE050_OK) {
return status;
}
/* Copy to output */
secure_memcpy(public_key, key_data, X25519_POINT_SIZE);
/* Securely zeroize */
memzero_explicit(key_data, sizeof(key_data));
return SE050_OK;
}