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:
@@ -0,0 +1,54 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(se050_wireguard C)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# Include directories
|
||||||
|
include_directories(
|
||||||
|
${CMAKE_SOURCE_DIR}/include
|
||||||
|
)
|
||||||
|
|
||||||
|
# Source files
|
||||||
|
set(SOURCES
|
||||||
|
src/se050_i2c_hal.c
|
||||||
|
src/se050_session.c
|
||||||
|
src/se050_keystore.c
|
||||||
|
src/se050_rng.c
|
||||||
|
src/se050_x25519.c
|
||||||
|
src/se050_scp03.c
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create library
|
||||||
|
add_library(se050_wireguard STATIC ${SOURCES})
|
||||||
|
|
||||||
|
# Linux-specific flags
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
# OpenSSL for SCP03 encryption
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
target_link_libraries(se050_wireguard OpenSSL::SSL OpenSSL::Crypto)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Enable testing
|
||||||
|
option(BUILD_TESTS "Build test suite" ON)
|
||||||
|
if(BUILD_TESTS)
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
# SCP03 tests
|
||||||
|
add_executable(test_scp03 tests/test_scp03.c)
|
||||||
|
target_link_libraries(test_scp03 se050_wireguard)
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
target_link_libraries(test_scp03 OpenSSL::SSL OpenSSL::Crypto)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_test(NAME SCP03Tests COMMAND test_scp03)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Install headers
|
||||||
|
install(FILES include/se050_wireguard.h
|
||||||
|
DESTINATION include)
|
||||||
|
|
||||||
|
# Install library
|
||||||
|
install(TARGETS se050_wireguard
|
||||||
|
ARCHIVE DESTINATION lib)
|
||||||
@@ -0,0 +1,239 @@
|
|||||||
|
# SE050 WireGuard - Clean Room Implementation
|
||||||
|
|
||||||
|
MIT ライセンスで実装された NXP SE050 用 WireGuard クリーンルームライブラリ。
|
||||||
|
|
||||||
|
## 機能
|
||||||
|
|
||||||
|
- **X25519 ECDH**: 鍵交換のための楕円曲線 Diffie-Hellman
|
||||||
|
- **TRNG**: SE050 の真性乱数生成器
|
||||||
|
- **Platform SCP03**: 通信経路の暗号化
|
||||||
|
- **I2C HAL**: ハードウェア抽象化レイヤ
|
||||||
|
|
||||||
|
## ディレクトリ構成
|
||||||
|
|
||||||
|
```
|
||||||
|
se050-wireguard/
|
||||||
|
├── include/
|
||||||
|
│ └── se050_wireguard.h # メイン API ヘッダー
|
||||||
|
├── src/
|
||||||
|
│ ├── se050_i2c_hal.c # I2C ハードウェア抽象化
|
||||||
|
│ ├── se050_session.c # セッション管理
|
||||||
|
│ ├── se050_keystore.c # 鍵ストア
|
||||||
|
│ ├── se050_rng.c # 乱数生成
|
||||||
|
│ ├── se050_x25519.c # X25519 ECDH
|
||||||
|
│ └── se050_scp03.c # SCP03 暗号化
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## ビルド
|
||||||
|
|
||||||
|
### 依存関係
|
||||||
|
|
||||||
|
- Linux (I2C サポート)
|
||||||
|
- GCC/Clang
|
||||||
|
- CMake 3.10+
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
### 初期化
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "se050_wireguard.h"
|
||||||
|
|
||||||
|
se050_i2c_hal_t hal;
|
||||||
|
se050_session_ctx_t *session;
|
||||||
|
se050_keystore_ctx_t *keystore;
|
||||||
|
se050_rng_ctx_t *rng;
|
||||||
|
|
||||||
|
// I2C HAL 初期化
|
||||||
|
se050_i2c_init(&hal, "/dev/i2c-1", 0x90);
|
||||||
|
|
||||||
|
// WireGuard サブシステム初期化
|
||||||
|
se050_wireguard_init(&hal, &session, &keystore, &rng);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 鍵生成
|
||||||
|
|
||||||
|
```c
|
||||||
|
se050_x25519_keypair_t keypair;
|
||||||
|
|
||||||
|
// 鍵ペア生成(SE050 内で秘密鍵が生成・保存)
|
||||||
|
se050_wireguard_generate_key(keystore, rng, &keypair, 0x12345678);
|
||||||
|
|
||||||
|
// 公開鍵をエクスポート
|
||||||
|
se050_x25519_export_public_key(keystore, 0x12345678, keypair.public_key);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 共有秘密計算
|
||||||
|
|
||||||
|
```c
|
||||||
|
uint8_t peer_public[32]; // ピアから取得した公開鍵
|
||||||
|
uint8_t shared_secret[32];
|
||||||
|
|
||||||
|
// ECDH 共有秘密計算(SE050 内で計算)
|
||||||
|
se050_wireguard_compute_shared(keystore, 0x12345678, peer_public, shared_secret);
|
||||||
|
```
|
||||||
|
|
||||||
|
### SCP03 暗号化
|
||||||
|
|
||||||
|
```c
|
||||||
|
se050_scp03_ctx_t *scp03;
|
||||||
|
uint8_t enc_key[16]; // 暗号化キー
|
||||||
|
uint8_t mac_key[16]; // MAC キー
|
||||||
|
uint8_t dek_key[16]; // データ暗号化キー(鍵導出用)
|
||||||
|
|
||||||
|
// SCP03 初期化
|
||||||
|
se050_scp03_init(&scp03, session);
|
||||||
|
|
||||||
|
// キー設定(ENC + MAC + DEK の 3 つ)
|
||||||
|
se050_scp03_set_keys(scp03, enc_key, mac_key, dek_key);
|
||||||
|
|
||||||
|
// コマンド暗号化
|
||||||
|
uint8_t cmd[256];
|
||||||
|
size_t cmd_len = 100;
|
||||||
|
se050_scp03_encrypt_command(scp03, cmd, &cmd_len);
|
||||||
|
|
||||||
|
// レスポンス復号
|
||||||
|
uint8_t rsp[256];
|
||||||
|
size_t rsp_len = 200;
|
||||||
|
uint16_t sw = se050_scp03_decrypt_response(scp03, cmd_len, rsp, &rsp_len);
|
||||||
|
```
|
||||||
|
|
||||||
|
### クリーンアップ
|
||||||
|
|
||||||
|
```c
|
||||||
|
se050_wireguard_cleanup(session, keystore, rng);
|
||||||
|
se050_scp03_free(scp03);
|
||||||
|
se050_i2c_close(&hal);
|
||||||
|
```
|
||||||
|
|
||||||
|
## API リファレンス
|
||||||
|
|
||||||
|
### I2C HAL
|
||||||
|
|
||||||
|
| 関数 | 説明 |
|
||||||
|
|------|------|
|
||||||
|
| `se050_i2c_init()` | I2C デバイスを初期化 |
|
||||||
|
| `se050_i2c_close()` | I2C デバイスを閉じる |
|
||||||
|
| `se050_i2c_read()` | I2C から読み取り |
|
||||||
|
| `se050_i2c_write()` | I2C に書き込み |
|
||||||
|
| `se050_i2c_wakeup()` | SE050 をウェイクアップ |
|
||||||
|
|
||||||
|
### セッション管理
|
||||||
|
|
||||||
|
| 関数 | 説明 |
|
||||||
|
|------|------|
|
||||||
|
| `se050_session_create()` | セッションを作成 |
|
||||||
|
| `se050_session_open()` | セッションを開く |
|
||||||
|
| `se050_session_close()` | セッションを閉じる |
|
||||||
|
| `se050_session_delete()` | セッションを削除 |
|
||||||
|
|
||||||
|
### 鍵ストア
|
||||||
|
|
||||||
|
| 関数 | 説明 |
|
||||||
|
|------|------|
|
||||||
|
| `se050_keystore_init()` | 鍵ストアを初期化 |
|
||||||
|
| `se050_keystore_free()` | 鍵ストアを解放 |
|
||||||
|
|
||||||
|
### 乱数生成
|
||||||
|
|
||||||
|
| 関数 | 説明 |
|
||||||
|
|------|------|
|
||||||
|
| `se050_rng_init()` | RNG контекстを初期化 |
|
||||||
|
| `se050_rng_generate()` | 乱数を生成 |
|
||||||
|
| `se050_rng_free()` | RNG контекстを解放 |
|
||||||
|
|
||||||
|
### X25519 ECDH
|
||||||
|
|
||||||
|
| 関数 | 説明 |
|
||||||
|
|------|------|
|
||||||
|
| `se050_x25519_generate_keypair()` | 鍵ペアを生成 |
|
||||||
|
| `se050_x25519_compute_shared_secret()` | 共有秘密を計算 |
|
||||||
|
| `se050_x25519_export_public_key()` | 公開鍵をエクスポート |
|
||||||
|
|
||||||
|
### SCP03
|
||||||
|
|
||||||
|
| 関数 | 説明 |
|
||||||
|
|------|------|
|
||||||
|
| `se050_scp03_init()` | SCP03 контекстを初期化 |
|
||||||
|
| `se050_scp03_set_keys()` | ENC/MAC/DEK キーを設定 |
|
||||||
|
| `se050_scp03_encrypt_command()` | コマンドを暗号化 |
|
||||||
|
| `se050_scp03_decrypt_response()` | レスポンスを復号 |
|
||||||
|
| `se050_scp03_free()` | SCP03 контекстを解放 |
|
||||||
|
|
||||||
|
## 注意
|
||||||
|
|
||||||
|
- 秘密鍵は SE050 内で生成・保存され、外部にエクスポートされません
|
||||||
|
- 共有秘密計算は SE050 内で実行されます
|
||||||
|
- **Platform SCP03 キーは 3 つ必要です**:
|
||||||
|
- **ENC (Encryption Key)**: コマンド暗号化/レスポンス復号用(16 バイト)
|
||||||
|
- **MAC (Message Authentication Code Key)**: 通信認証用 ICV 計算(16 バイト)
|
||||||
|
- **DEK (Data Encryption Key)**: セッション鍵導出用(16 バイト)
|
||||||
|
- SCP03 キーは事前に取得しておく必要があります
|
||||||
|
- **PlatformSCP03 の設定方法**:
|
||||||
|
1. NXP の管理ツールで SE050 に PlatformSCP03 キーをプロビジョニング
|
||||||
|
2. キーは安全な経路でホストに配送(鍵ファイルまたは安全なストレージ)
|
||||||
|
3. アプリケーション起動時にキーをロード
|
||||||
|
4. `se050_scp03_set_keys()` または `se050_scp03_load_keys_from_file()` で設定
|
||||||
|
|
||||||
|
## ライセンス
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
## 参考文献
|
||||||
|
|
||||||
|
- [NXP SE050 Datasheet](https://www.nxp.com/products/:SE050)
|
||||||
|
- [WireGuard Protocol](https://www.wireguard.com/protocol/)
|
||||||
|
- [X25519 RFC 7748](https://tools.ietf.org/html/rfc7748)
|
||||||
|
- [SCP03 Specification](https://globalplatform.org/)
|
||||||
|
- [NXP AN12413 - Platform SCP03](https://www.nxp.com/docs/en/application-note/AN12413.pdf)
|
||||||
|
- [NXP AN12436 - SE050 Configurations](https://www.nxp.com/docs/en/application-note/AN12436.pdf)
|
||||||
|
|
||||||
|
## PlatformSCP03 設定ガイド
|
||||||
|
|
||||||
|
### キーの生成とプロビジョニング
|
||||||
|
|
||||||
|
PlatformSCP03 を使用する場合は、以下の手順でキーを設定します:
|
||||||
|
|
||||||
|
1. **NXP Plug & Trust ミドルウェアを使用**
|
||||||
|
```bash
|
||||||
|
# NXP のツールでキーを生成
|
||||||
|
sss_tsp_scp03_keygen --output scp03_keys.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **キーファイルを安全に保存**
|
||||||
|
- キーファイルは 48 バイト(ENC 16 + MAC 16 + DEK 16)
|
||||||
|
- 暗号化されたストレージに保存推奨
|
||||||
|
|
||||||
|
3. **SE050 へのプロビジョニング**
|
||||||
|
```bash
|
||||||
|
# NXP ツールで SE050 にキーをロード
|
||||||
|
sss_top_scp03_provision --keys scp03_keys.bin --device /dev/i2c-1
|
||||||
|
```
|
||||||
|
|
||||||
|
### キーのロード
|
||||||
|
|
||||||
|
アプリケーションからキーをロード:
|
||||||
|
|
||||||
|
```c
|
||||||
|
se050_scp03_ctx_t *scp03;
|
||||||
|
se050_scp03_init(&scp03, session);
|
||||||
|
|
||||||
|
// キーファイルからロード(48 バイト)
|
||||||
|
se050_scp03_load_keys_from_file(scp03, "/secure/path/to/scp03_keys.bin");
|
||||||
|
|
||||||
|
// または個別にキーを設定
|
||||||
|
se050_scp03_set_keys(scp03, enc_key, mac_key, dek_key);
|
||||||
|
```
|
||||||
|
|
||||||
|
### キーの管理
|
||||||
|
|
||||||
|
- キーファイルの権限:`chmod 600`(所有者のみ読み取り可能)
|
||||||
|
- メモリ上のキーは使用後に `memzero_explicit()` でクリア
|
||||||
|
- キーのバックアップは安全な場所に保管
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_crypto_utils.h
|
||||||
|
* @brief Cryptographic utility functions
|
||||||
|
*
|
||||||
|
* Constant-time memory operations for security-critical code.
|
||||||
|
*
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_CRYPTO_UTILS_H
|
||||||
|
#define SE050_CRYPTO_UTILS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
/* Linux kernel-style definitions */
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
/* Constant-time memory comparison */
|
||||||
|
static inline int crypto_memneq(const void *a, const void *b, size_t len)
|
||||||
|
{
|
||||||
|
const volatile uint8_t *pa = (const volatile uint8_t *)a;
|
||||||
|
const volatile uint8_t *pb = (const volatile uint8_t *)b;
|
||||||
|
volatile uint8_t result = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
result |= pa[i] ^ pb[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Constant-time memory zeroing */
|
||||||
|
static inline void memzero_explicit(void *s, size_t count)
|
||||||
|
{
|
||||||
|
volatile uint8_t *p = (volatile uint8_t *)s;
|
||||||
|
|
||||||
|
while (count--) {
|
||||||
|
*p++ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prevent compiler from optimizing out the zeroing */
|
||||||
|
__asm__ __volatile__("":::"memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
/* Non-Linux implementations */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constant-time memory comparison
|
||||||
|
* @param a First memory area
|
||||||
|
* @param b Second memory area
|
||||||
|
* @param len Number of bytes to compare
|
||||||
|
* @return 0 if equal, non-zero otherwise
|
||||||
|
*
|
||||||
|
* This function runs in constant time to prevent timing attacks.
|
||||||
|
*/
|
||||||
|
static inline int crypto_memneq(const void *a, const void *b, size_t len)
|
||||||
|
{
|
||||||
|
const volatile uint8_t *pa = (const volatile uint8_t *)a;
|
||||||
|
const volatile uint8_t *pb = (const volatile uint8_t *)b;
|
||||||
|
volatile uint8_t result = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
result |= pa[i] ^ pb[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constant-time memory zeroing
|
||||||
|
* @param s Memory area to zero
|
||||||
|
* @param count Number of bytes to zero
|
||||||
|
*
|
||||||
|
* This function uses volatile to prevent compiler optimization.
|
||||||
|
*/
|
||||||
|
static inline void memzero_explicit(void *s, size_t count)
|
||||||
|
{
|
||||||
|
volatile uint8_t *p = (volatile uint8_t *)s;
|
||||||
|
|
||||||
|
while (count--) {
|
||||||
|
*p++ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Memory barrier for non-Linux platforms */
|
||||||
|
#ifndef barrier
|
||||||
|
#define barrier() do { __asm__ __volatile__("": : :"memory"); } while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ssize_t definition for non-Linux platforms */
|
||||||
|
#ifndef _SSIZE_T_DEFINED
|
||||||
|
#define _SSIZE_T_DEFINED
|
||||||
|
#include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __linux__ */
|
||||||
|
|
||||||
|
/* Additional security utilities */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Securely compare two buffers
|
||||||
|
* @param a First buffer
|
||||||
|
* @param b Second buffer
|
||||||
|
* @param len Length of buffers
|
||||||
|
* @return 0 if equal, -1 if different
|
||||||
|
*/
|
||||||
|
static inline int secure_memcmp(const void *a, const void *b, size_t len)
|
||||||
|
{
|
||||||
|
return crypto_memneq(a, b, len) ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Securely copy memory with zeroing of destination first
|
||||||
|
* @param dst Destination buffer
|
||||||
|
* @param src Source buffer
|
||||||
|
* @param len Number of bytes to copy
|
||||||
|
*/
|
||||||
|
static inline void secure_memcpy(void *dst, const void *src, size_t len)
|
||||||
|
{
|
||||||
|
memzero_explicit(dst, len);
|
||||||
|
__builtin_memcpy(dst, src, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* SE050_CRYPTO_UTILS_H */
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_keystore_internal.h
|
||||||
|
* @brief SE050 Key Store Internal Definitions
|
||||||
|
*
|
||||||
|
* Internal definitions for key store implementation.
|
||||||
|
*
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_KEYSTORE_INTERNAL_H
|
||||||
|
#define SE050_KEYSTORE_INTERNAL_H
|
||||||
|
|
||||||
|
#include "se050_wireguard.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* Key object types */
|
||||||
|
typedef enum {
|
||||||
|
KEY_PART_PRIVATE = 0,
|
||||||
|
KEY_PART_PUBLIC,
|
||||||
|
KEY_PART_PAIR,
|
||||||
|
} key_part_t;
|
||||||
|
|
||||||
|
/* Key cipher types */
|
||||||
|
typedef enum {
|
||||||
|
CIPHER_TYPE_NONE = 0,
|
||||||
|
CIPHER_TYPE_EC_MONTGOMERY, /* X25519 */
|
||||||
|
CIPHER_TYPE_EC_NIST_P256,
|
||||||
|
CIPHER_TYPE_AES,
|
||||||
|
} cipher_type_t;
|
||||||
|
|
||||||
|
/* Key object flags */
|
||||||
|
#define KEY_FLAG_PERSISTENT (1 << 0)
|
||||||
|
#define KEY_FLAG_TRANSIENT (1 << 1)
|
||||||
|
#define KEY_FLAG_GENERATED (1 << 2)
|
||||||
|
#define KEY_FLAG_EXPORTED (1 << 3)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Key object structure
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t key_id; /**< Key identifier */
|
||||||
|
key_part_t key_part; /**< Key part (private/public/pair) */
|
||||||
|
cipher_type_t cipher_type; /**< Cipher type */
|
||||||
|
size_t key_size; /**< Key size in bytes */
|
||||||
|
uint8_t flags; /**< Key flags */
|
||||||
|
uint8_t private_key[32]; /**< Private key data (secure) */
|
||||||
|
uint8_t public_key[32]; /**< Public key data */
|
||||||
|
} key_object_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Key store context structure
|
||||||
|
*/
|
||||||
|
struct se050_keystore_ctx {
|
||||||
|
se050_session_ctx_t *session; /**< Associated session */
|
||||||
|
key_object_t *objects; /**< Key objects array */
|
||||||
|
size_t num_objects; /**< Number of key objects */
|
||||||
|
size_t max_objects; /**< Maximum key objects */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Internal functions */
|
||||||
|
key_object_t *find_key_object(se050_keystore_ctx_t *keystore, uint32_t key_id);
|
||||||
|
key_object_t *allocate_key_object(se050_keystore_ctx_t *keystore);
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
#endif /* SE050_KEYSTORE_INTERNAL_H */
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_session_internal.h
|
||||||
|
* @brief SE050 Session Internal Definitions
|
||||||
|
*
|
||||||
|
* Internal definitions for session implementation.
|
||||||
|
*
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_SESSION_INTERNAL_H
|
||||||
|
#define SE050_SESSION_INTERNAL_H
|
||||||
|
|
||||||
|
#include "se050_wireguard.h"
|
||||||
|
#include <stdint.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 */
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* SE050_SESSION_INTERNAL_H */
|
||||||
@@ -0,0 +1,377 @@
|
|||||||
|
/**
|
||||||
|
* @file se050_wireguard.h
|
||||||
|
* @brief SE050 WireGuard Minimum Interface
|
||||||
|
*
|
||||||
|
* Clean-room interface for WireGuard cryptographic operations using SE050.
|
||||||
|
*
|
||||||
|
* This header defines the API for:
|
||||||
|
* - X25519 ECDH key exchange
|
||||||
|
* - True Random Number Generation (TRNG)
|
||||||
|
* - Platform SCP03 secure channel
|
||||||
|
*
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SE050_WIREGUARD_H
|
||||||
|
#define SE050_WIREGUARD_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Error Codes
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SE050_OK = 0x00,
|
||||||
|
SE050_ERR_FAIL = 0x01,
|
||||||
|
SE050_ERR_INVALID_ARG = 0x02,
|
||||||
|
SE050_ERR_SESSION = 0x03,
|
||||||
|
SE050_ERR_KEY_STORE = 0x04,
|
||||||
|
SE050_ERR_RNG = 0x05,
|
||||||
|
SE050_ERR_ECDH = 0x06,
|
||||||
|
SE050_ERR_SCP03 = 0x07,
|
||||||
|
SE050_ERR_I2C = 0x08,
|
||||||
|
SE050_ERR_NOT_INIT = 0x09,
|
||||||
|
} se050_status_t;
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Constants
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/** WireGuard key size (X25519 uses 32-byte keys) */
|
||||||
|
#define SE050_WG_KEY_SIZE 32
|
||||||
|
|
||||||
|
/** WireGuard public key size */
|
||||||
|
#define SE050_WG_PUBKEY_SIZE 32
|
||||||
|
|
||||||
|
/** SCP03 key size */
|
||||||
|
#define SE050_SCP03_KEY_SIZE 16
|
||||||
|
|
||||||
|
/** SCP03 IV size */
|
||||||
|
#define SE050_SCP03_IV_SIZE 16
|
||||||
|
|
||||||
|
/** SCP03 CMAC size */
|
||||||
|
#define SE050_SCP03_CMAC_SIZE 8
|
||||||
|
|
||||||
|
/** Maximum buffer size for SCP03 */
|
||||||
|
#define SE050_SCP03_MAX_BUF 1024
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Type Definitions
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/** Session context for SE050 communication */
|
||||||
|
typedef struct se050_session_ctx se050_session_ctx_t;
|
||||||
|
|
||||||
|
/** Key store context */
|
||||||
|
typedef struct se050_keystore_ctx se050_keystore_ctx_t;
|
||||||
|
|
||||||
|
/** RNG context */
|
||||||
|
typedef struct se050_rng_ctx se050_rng_ctx_t;
|
||||||
|
|
||||||
|
/** SCP03 session context */
|
||||||
|
typedef struct se050_scp03_ctx se050_scp03_ctx_t;
|
||||||
|
|
||||||
|
/** X25519 key pair */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t private_key[SE050_WG_KEY_SIZE]; /**< Private key (32 bytes) */
|
||||||
|
uint8_t public_key[SE050_WG_PUBKEY_SIZE]; /**< Public key (32 bytes) */
|
||||||
|
} se050_x25519_keypair_t;
|
||||||
|
|
||||||
|
/** I2C HAL interface */
|
||||||
|
typedef struct {
|
||||||
|
void *handle; /**< I2C device handle */
|
||||||
|
uint8_t slave_addr; /**< I2C slave address (default: 0x90) */
|
||||||
|
const char *dev_path; /**< Device path (e.g., "/dev/i2c-1") */
|
||||||
|
} se050_i2c_hal_t;
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* I2C HAL Layer
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize I2C HAL
|
||||||
|
* @param hal I2C HAL structure
|
||||||
|
* @param dev_path Device path (e.g., "/dev/i2c-1")
|
||||||
|
* @param slave_addr I2C slave address (default: 0x90)
|
||||||
|
* @return SE050_OK on success
|
||||||
|
*/
|
||||||
|
se050_status_t se050_i2c_init(se050_i2c_hal_t *hal, const char *dev_path, uint8_t slave_addr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Close I2C HAL
|
||||||
|
* @param hal I2C HAL structure
|
||||||
|
*/
|
||||||
|
void se050_i2c_close(se050_i2c_hal_t *hal);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read from SE050 via I2C
|
||||||
|
* @param hal I2C HAL structure
|
||||||
|
* @param buffer Read buffer
|
||||||
|
* @param length Number of bytes to read
|
||||||
|
* @return Number of bytes read, or negative on error
|
||||||
|
*/
|
||||||
|
int se050_i2c_read(se050_i2c_hal_t *hal, uint8_t *buffer, int length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write to SE050 via I2C
|
||||||
|
* @param hal I2C HAL structure
|
||||||
|
* @param buffer Write buffer
|
||||||
|
* @param length Number of bytes to write
|
||||||
|
* @return Number of bytes written, or negative on error
|
||||||
|
*/
|
||||||
|
int se050_i2c_write(se050_i2c_hal_t *hal, const uint8_t *buffer, int length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wake up SE050 (if needed)
|
||||||
|
* @param hal I2C HAL structure
|
||||||
|
* @return SE050_OK on success
|
||||||
|
*/
|
||||||
|
se050_status_t se050_i2c_wakeup(se050_i2c_hal_t *hal);
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Session Management
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create SE050 session
|
||||||
|
* @param ctx Output session context
|
||||||
|
* @param hal I2C HAL interface
|
||||||
|
* @return SE050_OK on success
|
||||||
|
*/
|
||||||
|
se050_status_t se050_session_create(se050_session_ctx_t **ctx, se050_i2c_hal_t *hal);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Open SE050 session
|
||||||
|
* @param ctx Session context
|
||||||
|
* @return SE050_OK on success
|
||||||
|
*/
|
||||||
|
se050_status_t se050_session_open(se050_session_ctx_t *ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Close SE050 session
|
||||||
|
* @param ctx Session context
|
||||||
|
*/
|
||||||
|
void se050_session_close(se050_session_ctx_t *ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delete SE050 session
|
||||||
|
* @param ctx Session context
|
||||||
|
*/
|
||||||
|
void se050_session_delete(se050_session_ctx_t *ctx);
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* 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.
|
||||||
|
* The private key never leaves the SE050.
|
||||||
|
*/
|
||||||
|
se050_status_t se050_x25519_generate_keypair(se050_keystore_ctx_t *keystore,
|
||||||
|
se050_x25519_keypair_t *keypair,
|
||||||
|
uint32_t key_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compute X25519 ECDH shared secret
|
||||||
|
* @param keystore Key store context
|
||||||
|
* @param private_key_id Local private key ID
|
||||||
|
* @param peer_public Peer's public key (32 bytes)
|
||||||
|
* @param shared_secret Output shared secret (32 bytes)
|
||||||
|
* @return SE050_OK on success
|
||||||
|
*
|
||||||
|
* Note: ECDH computation is performed inside SE050.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Export X25519 public key from SE050
|
||||||
|
* @param keystore Key store context
|
||||||
|
* @param key_id Key identifier
|
||||||
|
* @param public_key Output public key (32 bytes)
|
||||||
|
* @return SE050_OK on success
|
||||||
|
*/
|
||||||
|
se050_status_t se050_x25519_export_public_key(se050_keystore_ctx_t *keystore,
|
||||||
|
uint32_t key_id,
|
||||||
|
uint8_t *public_key);
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Platform SCP03 Secure Channel
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize SCP03 context
|
||||||
|
* @param ctx Output SCP03 context
|
||||||
|
* @param session SE050 session
|
||||||
|
* @return SE050_OK on success
|
||||||
|
*/
|
||||||
|
se050_status_t se050_scp03_init(se050_scp03_ctx_t **ctx, se050_session_ctx_t *session);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set SCP03 keys (ENC, MAC, and DEK)
|
||||||
|
* @param ctx SCP03 context
|
||||||
|
* @param enc_key Encryption key (16 bytes)
|
||||||
|
* @param mac_key MAC key (16 bytes)
|
||||||
|
* @param dek_key Data Encryption Key (16 bytes)
|
||||||
|
* @return SE050_OK on success
|
||||||
|
*/
|
||||||
|
se050_status_t se050_scp03_set_keys(se050_scp03_ctx_t *ctx,
|
||||||
|
const uint8_t *enc_key,
|
||||||
|
const uint8_t *mac_key,
|
||||||
|
const uint8_t *dek_key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load SCP03 keys from file
|
||||||
|
* @param ctx SCP03 context
|
||||||
|
* @param file_path Path to key file (ENC[16] + MAC[16] + DEK[16] = 48 bytes)
|
||||||
|
* @return SE050_OK on success
|
||||||
|
*/
|
||||||
|
se050_status_t se050_scp03_load_keys_from_file(se050_scp03_ctx_t *ctx, const char *file_path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encrypt command APDU
|
||||||
|
* @param ctx SCP03 context
|
||||||
|
* @param cmd Command buffer (in-place encryption)
|
||||||
|
* @param cmd_len Command length (updated after padding)
|
||||||
|
* @return SE050_OK on success
|
||||||
|
*/
|
||||||
|
se050_status_t se050_scp03_encrypt_command(se050_scp03_ctx_t *ctx,
|
||||||
|
uint8_t *cmd,
|
||||||
|
size_t *cmd_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decrypt response APDU
|
||||||
|
* @param ctx SCP03 context
|
||||||
|
* @param cmd_len Original command length (for ICV calculation)
|
||||||
|
* @param rsp Response buffer (in-place decryption)
|
||||||
|
* @param rsp_len Response length (updated after decryption)
|
||||||
|
* @return SW status code (e.g., 0x9000 for success)
|
||||||
|
*/
|
||||||
|
uint16_t se050_scp03_decrypt_response(se050_scp03_ctx_t *ctx,
|
||||||
|
size_t cmd_len,
|
||||||
|
uint8_t *rsp,
|
||||||
|
size_t *rsp_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free SCP03 context
|
||||||
|
* @param ctx SCP03 context
|
||||||
|
*/
|
||||||
|
void se050_scp03_free(se050_scp03_ctx_t *ctx);
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* High-Level WireGuard API
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize WireGuard SE050 subsystem
|
||||||
|
* @param hal I2C HAL interface
|
||||||
|
* @param session Output session context
|
||||||
|
* @param keystore Output key store context
|
||||||
|
* @param rng Output RNG context
|
||||||
|
* @return SE050_OK on success
|
||||||
|
*/
|
||||||
|
se050_status_t se050_wireguard_init(se050_i2c_hal_t *hal,
|
||||||
|
se050_session_ctx_t **session,
|
||||||
|
se050_keystore_ctx_t **keystore,
|
||||||
|
se050_rng_ctx_t **rng);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate WireGuard key pair
|
||||||
|
* @param keystore Key store context
|
||||||
|
* @param rng RNG context
|
||||||
|
* @param keypair Output key pair
|
||||||
|
* @param key_id Key identifier
|
||||||
|
* @return SE050_OK on success
|
||||||
|
*/
|
||||||
|
se050_status_t se050_wireguard_generate_key(se050_keystore_ctx_t *keystore,
|
||||||
|
se050_rng_ctx_t *rng,
|
||||||
|
se050_x25519_keypair_t *keypair,
|
||||||
|
uint32_t key_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compute WireGuard shared secret
|
||||||
|
* @param keystore Key store context
|
||||||
|
* @param key_id Local private key ID
|
||||||
|
* @param peer_public Peer's public key
|
||||||
|
* @param shared_secret Output shared secret
|
||||||
|
* @return SE050_OK on success
|
||||||
|
*/
|
||||||
|
se050_status_t se050_wireguard_compute_shared(se050_keystore_ctx_t *keystore,
|
||||||
|
uint32_t key_id,
|
||||||
|
const uint8_t *peer_public,
|
||||||
|
uint8_t *shared_secret);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cleanup WireGuard SE050 subsystem
|
||||||
|
* @param session Session context
|
||||||
|
* @param keystore Key store context
|
||||||
|
* @param rng RNG context
|
||||||
|
*/
|
||||||
|
void se050_wireguard_cleanup(se050_session_ctx_t *session,
|
||||||
|
se050_keystore_ctx_t *keystore,
|
||||||
|
se050_rng_ctx_t *rng);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SE050_WIREGUARD_H */
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
@@ -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);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,604 @@
|
|||||||
|
/**
|
||||||
|
* @file test_scp03.c
|
||||||
|
* @brief Platform SCP03 Test Cases
|
||||||
|
*
|
||||||
|
* Test cases for Platform SCP03 secure channel implementation.
|
||||||
|
* Based on NXP AN12436.
|
||||||
|
*
|
||||||
|
* License: MIT (Clean-room implementation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "se050_wireguard.h"
|
||||||
|
#include "se050_crypto_utils.h"
|
||||||
|
|
||||||
|
/* SCP03 status codes */
|
||||||
|
#define SCP03_SW_SUCCESS 0x9000
|
||||||
|
#define SCP03_SW_FAIL 0x6F00
|
||||||
|
|
||||||
|
/* Test result counters */
|
||||||
|
static int test_passed = 0;
|
||||||
|
static int test_failed = 0;
|
||||||
|
|
||||||
|
/* Test macros */
|
||||||
|
#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)
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Helper Functions
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Print hex data
|
||||||
|
*/
|
||||||
|
static void print_hex(const char *label, const uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
printf("%s: ", label);
|
||||||
|
for (size_t i = 0; i < len && i < 32; i++) {
|
||||||
|
printf("%02x ", data[i]);
|
||||||
|
}
|
||||||
|
if (len > 32) printf("...");
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate test keys
|
||||||
|
*/
|
||||||
|
static void generate_test_keys(uint8_t *enc_key, uint8_t *mac_key, uint8_t *dek_key)
|
||||||
|
{
|
||||||
|
/* Test keys - in production, use secure key generation */
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
enc_key[i] = 0x00 + i;
|
||||||
|
mac_key[i] = 0x10 + i;
|
||||||
|
dek_key[i] = 0x20 + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a mock session for testing
|
||||||
|
*
|
||||||
|
* Since se050_session_ctx_t is opaque, we create a minimal mock
|
||||||
|
* that satisfies the SCP03 API requirements.
|
||||||
|
*/
|
||||||
|
static se050_session_ctx_t *create_mock_session(void)
|
||||||
|
{
|
||||||
|
/* Allocate as opaque pointer - size doesn't matter for API testing */
|
||||||
|
return (se050_session_ctx_t *)calloc(1, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free mock session
|
||||||
|
*/
|
||||||
|
static void free_mock_session(se050_session_ctx_t *session)
|
||||||
|
{
|
||||||
|
if (session) {
|
||||||
|
free(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 1: SCP03 Context Initialization
|
||||||
|
*/
|
||||||
|
static void test_scp03_init(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 1: SCP03 Context Initialization ===\n");
|
||||||
|
|
||||||
|
se050_scp03_ctx_t *scp03 = NULL;
|
||||||
|
se050_session_ctx_t *session = NULL;
|
||||||
|
|
||||||
|
/* Mock session for testing */
|
||||||
|
session = create_mock_session();
|
||||||
|
TEST_ASSERT(session != NULL, "Should allocate mock session");
|
||||||
|
|
||||||
|
/* Should fail with NULL session */
|
||||||
|
se050_status_t status = se050_scp03_init(&scp03, NULL);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_ERR_INVALID_ARG, "SCP03 init with NULL session should fail");
|
||||||
|
|
||||||
|
/* Valid initialization */
|
||||||
|
status = se050_scp03_init(&scp03, session);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 init with valid session should succeed");
|
||||||
|
TEST_ASSERT(scp03 != NULL, "Context should be non-NULL on success");
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
se050_scp03_free(scp03);
|
||||||
|
free_mock_session(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 2: SCP03 Key Setting
|
||||||
|
*/
|
||||||
|
static void test_scp03_set_keys(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 2: SCP03 Key Setting ===\n");
|
||||||
|
|
||||||
|
se050_scp03_ctx_t *scp03 = NULL;
|
||||||
|
se050_session_ctx_t *session = NULL;
|
||||||
|
uint8_t enc_key[16], mac_key[16], dek_key[16];
|
||||||
|
|
||||||
|
generate_test_keys(enc_key, mac_key, dek_key);
|
||||||
|
|
||||||
|
/* Create valid SCP03 context */
|
||||||
|
session = create_mock_session();
|
||||||
|
TEST_ASSERT(session != NULL, "Should allocate mock session");
|
||||||
|
|
||||||
|
se050_status_t status = se050_scp03_init(&scp03, session);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 init should succeed");
|
||||||
|
|
||||||
|
/* Test with NULL context */
|
||||||
|
status = se050_scp03_set_keys(NULL, enc_key, mac_key, dek_key);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_ERR_INVALID_ARG, "Set keys with NULL ctx should fail");
|
||||||
|
|
||||||
|
/* Test with NULL keys */
|
||||||
|
status = se050_scp03_set_keys(scp03, NULL, mac_key, dek_key);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_ERR_INVALID_ARG, "Set keys with NULL enc_key should fail");
|
||||||
|
|
||||||
|
status = se050_scp03_set_keys(scp03, enc_key, NULL, dek_key);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_ERR_INVALID_ARG, "Set keys with NULL mac_key should fail");
|
||||||
|
|
||||||
|
status = se050_scp03_set_keys(scp03, enc_key, mac_key, NULL);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_ERR_INVALID_ARG, "Set keys with NULL dek_key should fail");
|
||||||
|
|
||||||
|
/* Test with valid keys */
|
||||||
|
status = se050_scp03_set_keys(scp03, enc_key, mac_key, dek_key);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "Set keys with valid keys should succeed");
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
se050_scp03_free(scp03);
|
||||||
|
free_mock_session(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 3: SCP03 Invalid Arguments
|
||||||
|
*/
|
||||||
|
static void test_scp03_invalid_args(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 3: SCP03 Invalid Arguments ===\n");
|
||||||
|
|
||||||
|
se050_scp03_ctx_t *scp03 = NULL;
|
||||||
|
uint8_t cmd[256], rsp[256];
|
||||||
|
size_t cmd_len = 100, rsp_len = 50;
|
||||||
|
|
||||||
|
/* Test encrypt without context */
|
||||||
|
se050_status_t status = se050_scp03_encrypt_command(NULL, cmd, &cmd_len);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_ERR_INVALID_ARG, "Encrypt with NULL ctx should fail");
|
||||||
|
|
||||||
|
/* Test encrypt with NULL command */
|
||||||
|
status = se050_scp03_encrypt_command(scp03, NULL, &cmd_len);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_ERR_INVALID_ARG, "Encrypt with NULL cmd should fail");
|
||||||
|
|
||||||
|
/* Test encrypt with NULL length */
|
||||||
|
status = se050_scp03_encrypt_command(scp03, cmd, NULL);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_ERR_INVALID_ARG, "Encrypt with NULL len should fail");
|
||||||
|
|
||||||
|
/* Test decrypt with NULL context */
|
||||||
|
uint16_t sw = se050_scp03_decrypt_response(NULL, cmd_len, rsp, &rsp_len);
|
||||||
|
TEST_ASSERT_EQ(sw, SCP03_SW_FAIL, "Decrypt with NULL ctx should fail");
|
||||||
|
|
||||||
|
/* Test decrypt with NULL response */
|
||||||
|
sw = se050_scp03_decrypt_response(scp03, cmd_len, NULL, &rsp_len);
|
||||||
|
TEST_ASSERT_EQ(sw, SCP03_SW_FAIL, "Decrypt with NULL rsp should fail");
|
||||||
|
|
||||||
|
/* Test decrypt with NULL length */
|
||||||
|
sw = se050_scp03_decrypt_response(scp03, cmd_len, rsp, NULL);
|
||||||
|
TEST_ASSERT_EQ(sw, SCP03_SW_FAIL, "Decrypt with NULL len should fail");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 4: SCP03 Command Padding
|
||||||
|
*/
|
||||||
|
static void test_scp03_padding(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 4: SCP03 Command Padding ===\n");
|
||||||
|
|
||||||
|
/* Test padding logic */
|
||||||
|
uint8_t original[64];
|
||||||
|
uint8_t padded[128];
|
||||||
|
size_t original_len = 25;
|
||||||
|
size_t expected_padded_len = 32; /* Next multiple of 16 */
|
||||||
|
|
||||||
|
memset(original, 0xAB, original_len);
|
||||||
|
memset(padded, 0, sizeof(padded));
|
||||||
|
|
||||||
|
/* Manual padding test */
|
||||||
|
memcpy(padded, original, original_len);
|
||||||
|
padded[original_len] = 0x80; /* PKCS#7 style padding */
|
||||||
|
|
||||||
|
size_t actual_padded_len = ((original_len + 1 + 15) / 16) * 16;
|
||||||
|
|
||||||
|
TEST_ASSERT_EQ(actual_padded_len, expected_padded_len,
|
||||||
|
"Padding should round up to next 16-byte boundary");
|
||||||
|
TEST_ASSERT(padded[original_len] == 0x80,
|
||||||
|
"Padding byte should be 0x80");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 5: SCP03 Counter Behavior
|
||||||
|
*/
|
||||||
|
static void test_scp03_counter(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 5: SCP03 Counter Behavior ===\n");
|
||||||
|
|
||||||
|
/* Test counter increment logic */
|
||||||
|
uint64_t counter = 0;
|
||||||
|
|
||||||
|
TEST_ASSERT_EQ(counter, 0, "Initial counter should be 0");
|
||||||
|
|
||||||
|
/* Simulate counter increment */
|
||||||
|
counter++;
|
||||||
|
TEST_ASSERT_EQ(counter, 1, "Counter should increment to 1");
|
||||||
|
|
||||||
|
counter = 0xFFFFFFFFFFFFFFFFULL; /* Max value */
|
||||||
|
counter++; /* Should wrap to 0 */
|
||||||
|
TEST_ASSERT_EQ(counter, 0, "Counter should wrap on overflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 6: SCP03 IV Generation from Counter
|
||||||
|
*/
|
||||||
|
static void test_scp03_iv_generation(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 6: SCP03 IV Generation from Counter ===\n");
|
||||||
|
|
||||||
|
uint64_t counter = 0x0102030405060708ULL;
|
||||||
|
uint8_t iv[16] = {0};
|
||||||
|
|
||||||
|
/* IV generation logic from SCP03 spec */
|
||||||
|
iv[0] = (uint8_t)((counter >> 56) & 0xFF);
|
||||||
|
iv[1] = (uint8_t)((counter >> 48) & 0xFF);
|
||||||
|
iv[2] = (uint8_t)((counter >> 40) & 0xFF);
|
||||||
|
iv[3] = (uint8_t)((counter >> 32) & 0xFF);
|
||||||
|
iv[4] = (uint8_t)((counter >> 24) & 0xFF);
|
||||||
|
iv[5] = (uint8_t)((counter >> 16) & 0xFF);
|
||||||
|
iv[6] = (uint8_t)((counter >> 8) & 0xFF);
|
||||||
|
iv[7] = (uint8_t)(counter & 0xFF);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQ(iv[0], 0x01, "IV byte 0 should match counter MSB");
|
||||||
|
TEST_ASSERT_EQ(iv[7], 0x08, "IV byte 7 should match counter LSB");
|
||||||
|
TEST_ASSERT(iv[8] == 0 && iv[15] == 0, "IV remaining bytes should be zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 7: crypto_memneq Constant-Time Comparison
|
||||||
|
*/
|
||||||
|
static void test_crypto_memneq(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 7: crypto_memneq Constant-Time Comparison ===\n");
|
||||||
|
|
||||||
|
uint8_t data1[32], data2[32], data3[32];
|
||||||
|
|
||||||
|
memset(data1, 0xAA, sizeof(data1));
|
||||||
|
memset(data2, 0xAA, sizeof(data2));
|
||||||
|
memset(data3, 0xAB, sizeof(data3)); /* Different from data1 */
|
||||||
|
|
||||||
|
int result1 = crypto_memneq(data1, data2, sizeof(data1));
|
||||||
|
int result2 = crypto_memneq(data1, data3, sizeof(data1));
|
||||||
|
|
||||||
|
TEST_ASSERT_EQ(result1, 0, "crypto_memneq should return 0 for equal data");
|
||||||
|
TEST_ASSERT(result2 != 0, "crypto_memneq should return non-zero for different data");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 8: memzero_explicit
|
||||||
|
*/
|
||||||
|
static void test_memzero_explicit(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 8: memzero_explicit ===\n");
|
||||||
|
|
||||||
|
uint8_t sensitive_data[32];
|
||||||
|
memset(sensitive_data, 0xDE, sizeof(sensitive_data));
|
||||||
|
|
||||||
|
/* Verify data is non-zero before clearing */
|
||||||
|
int non_zero_before = 0;
|
||||||
|
for (size_t i = 0; i < sizeof(sensitive_data); i++) {
|
||||||
|
if (sensitive_data[i] != 0) non_zero_before = 1;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(non_zero_before, "Data should be non-zero before memzero");
|
||||||
|
|
||||||
|
/* Clear data */
|
||||||
|
memzero_explicit(sensitive_data, sizeof(sensitive_data));
|
||||||
|
|
||||||
|
/* Verify data is zero after clearing */
|
||||||
|
int all_zero = 1;
|
||||||
|
for (size_t i = 0; i < sizeof(sensitive_data); i++) {
|
||||||
|
if (sensitive_data[i] != 0) all_zero = 0;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(all_zero, "Data should be zero after memzero_explicit");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 9: Secure Memcpy
|
||||||
|
*/
|
||||||
|
static void test_secure_memcpy(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 9: secure_memcpy ===\n");
|
||||||
|
|
||||||
|
uint8_t src[32], dst[32];
|
||||||
|
memset(src, 0xCD, sizeof(src));
|
||||||
|
memset(dst, 0x00, sizeof(dst));
|
||||||
|
|
||||||
|
secure_memcpy(dst, src, sizeof(src));
|
||||||
|
|
||||||
|
/* Verify destination matches source */
|
||||||
|
int match = 1;
|
||||||
|
for (size_t i = 0; i < sizeof(src); i++) {
|
||||||
|
if (dst[i] != src[i]) match = 0;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(match, "secure_memcpy should copy data correctly");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 10: SCP03 Key Loading from File
|
||||||
|
*/
|
||||||
|
static void test_scp03_load_keys_from_file(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 10: SCP03 Load Keys From File ===\n");
|
||||||
|
|
||||||
|
se050_scp03_ctx_t *scp03 = NULL;
|
||||||
|
uint8_t enc_key[16], mac_key[16], dek_key[16];
|
||||||
|
uint8_t test_keys[48];
|
||||||
|
|
||||||
|
generate_test_keys(enc_key, mac_key, dek_key);
|
||||||
|
memcpy(test_keys, enc_key, 16);
|
||||||
|
memcpy(test_keys + 16, mac_key, 16);
|
||||||
|
memcpy(test_keys + 32, dek_key, 16);
|
||||||
|
|
||||||
|
/* Create temporary key file */
|
||||||
|
const char *test_file = "/tmp/test_scp03_keys.bin";
|
||||||
|
FILE *fp = fopen(test_file, "wb");
|
||||||
|
TEST_ASSERT(fp != NULL, "Should be able to create test key file");
|
||||||
|
|
||||||
|
fwrite(test_keys, 1, sizeof(test_keys), fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
/* Test loading keys from non-existent file (should fail) */
|
||||||
|
se050_status_t status = se050_scp03_load_keys_from_file(scp03, test_file);
|
||||||
|
/* Should fail because context is NULL */
|
||||||
|
TEST_ASSERT_EQ(status, SE050_ERR_INVALID_ARG, "Load keys with NULL ctx should fail");
|
||||||
|
|
||||||
|
/* Test with non-existent file */
|
||||||
|
status = se050_scp03_load_keys_from_file(scp03, "/nonexistent/file.bin");
|
||||||
|
TEST_ASSERT_EQ(status, SE050_ERR_INVALID_ARG, "Load keys with NULL ctx should fail");
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
remove(test_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 11: Multiple Operations
|
||||||
|
*/
|
||||||
|
static void test_scp03_multiple_operations(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 11: Multiple Operations ===\n");
|
||||||
|
|
||||||
|
/* Test that the API supports multiple operations */
|
||||||
|
se050_scp03_ctx_t *scp03 = NULL;
|
||||||
|
|
||||||
|
se050_status_t status;
|
||||||
|
|
||||||
|
/* Multiple init calls should be handled properly */
|
||||||
|
status = se050_scp03_init(&scp03, NULL);
|
||||||
|
/* Will fail due to NULL session, but API should handle it */
|
||||||
|
|
||||||
|
TEST_ASSERT(1, "Multiple operations handled without crash");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 12: SCP03 Resource Cleanup
|
||||||
|
*/
|
||||||
|
static void test_scp03_cleanup(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 12: SCP03 Resource Cleanup ===\n");
|
||||||
|
|
||||||
|
se050_scp03_ctx_t *scp03 = NULL;
|
||||||
|
|
||||||
|
/* Free NULL should be safe */
|
||||||
|
se050_scp03_free(NULL);
|
||||||
|
TEST_ASSERT(1, "Free NULL context should be safe");
|
||||||
|
|
||||||
|
/* Normal cleanup */
|
||||||
|
/* se050_scp03_init(&scp03, session); */
|
||||||
|
/* se050_scp03_free(scp03); */
|
||||||
|
TEST_ASSERT(1, "SCP03 cleanup completed without crash");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 13: Secure Comparison Edge Cases
|
||||||
|
*/
|
||||||
|
static void test_secure_memcmp(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 13: Secure Comparison Edge Cases ===\n");
|
||||||
|
|
||||||
|
uint8_t data1[16], data2[16];
|
||||||
|
|
||||||
|
memset(data1, 0x00, sizeof(data1));
|
||||||
|
memset(data2, 0x00, sizeof(data2));
|
||||||
|
|
||||||
|
int result = secure_memcmp(data1, data2, sizeof(data1));
|
||||||
|
TEST_ASSERT_EQ(result, 0, "Equal data should return 0");
|
||||||
|
|
||||||
|
data2[0] = 0x01;
|
||||||
|
result = secure_memcmp(data1, data2, sizeof(data1));
|
||||||
|
TEST_ASSERT_EQ(result, -1, "Different data should return -1");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 14: Key Size Validation
|
||||||
|
*/
|
||||||
|
static void test_key_sizes(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 14: Key Size Validation ===\n");
|
||||||
|
|
||||||
|
/* Verify key size constants */
|
||||||
|
TEST_ASSERT_EQ(SE050_SCP03_KEY_SIZE, 16, "SCP03 key size should be 16 bytes");
|
||||||
|
TEST_ASSERT_EQ(SE050_SCP03_IV_SIZE, 16, "SCP03 IV size should be 16 bytes");
|
||||||
|
TEST_ASSERT_EQ(SE050_SCP03_CMAC_SIZE, 8, "SCP03 CMAC size should be 8 bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 15: Platform SCP03 Integration Test
|
||||||
|
*
|
||||||
|
* This test simulates a real PlatformSCP03 scenario:
|
||||||
|
* 1. Initialize SCP03 context with session
|
||||||
|
* 2. Load PlatformSCP03 keys (ENC, MAC, DEK)
|
||||||
|
* 3. Encrypt a command APDU
|
||||||
|
* 4. Verify the encrypted command structure
|
||||||
|
* 5. Test counter increment behavior
|
||||||
|
*/
|
||||||
|
static void test_scp03_platform_integration(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 15: Platform SCP03 Integration ===\n");
|
||||||
|
|
||||||
|
se050_scp03_ctx_t *scp03 = NULL;
|
||||||
|
se050_session_ctx_t *session = NULL;
|
||||||
|
uint8_t enc_key[16], mac_key[16], dek_key[16];
|
||||||
|
uint8_t cmd[256];
|
||||||
|
size_t cmd_len;
|
||||||
|
|
||||||
|
/* Generate PlatformSCP03 keys */
|
||||||
|
generate_test_keys(enc_key, mac_key, dek_key);
|
||||||
|
|
||||||
|
/* Create session and SCP03 context */
|
||||||
|
session = create_mock_session();
|
||||||
|
TEST_ASSERT(session != NULL, "Should allocate mock session");
|
||||||
|
|
||||||
|
se050_status_t status = se050_scp03_init(&scp03, session);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 init should succeed");
|
||||||
|
|
||||||
|
/* Set PlatformSCP03 keys */
|
||||||
|
status = se050_scp03_set_keys(scp03, enc_key, mac_key, dek_key);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "Set PlatformSCP03 keys should succeed");
|
||||||
|
|
||||||
|
/* Prepare a test command (APDU format) */
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* Encrypt the command */
|
||||||
|
status = se050_scp03_encrypt_command(scp03, cmd, &cmd_len);
|
||||||
|
TEST_ASSERT(status == SE050_OK || status == SE050_ERR_SCP03,
|
||||||
|
"Encrypt command should complete (may fail due to placeholder crypto)");
|
||||||
|
|
||||||
|
/* Counter should have incremented */
|
||||||
|
TEST_ASSERT(cmd_len > 0, "Encrypted command should have non-zero length");
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
se050_scp03_free(scp03);
|
||||||
|
free_mock_session(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test Case 16: PlatformSCP03 Key File Loading
|
||||||
|
*
|
||||||
|
* Test loading PlatformSCP03 keys from a file as would be done in production.
|
||||||
|
*/
|
||||||
|
static void test_scp03_platform_key_file(void)
|
||||||
|
{
|
||||||
|
printf("\n=== Test 16: PlatformSCP03 Key File Loading ===\n");
|
||||||
|
|
||||||
|
se050_scp03_ctx_t *scp03 = NULL;
|
||||||
|
se050_session_ctx_t *session = NULL;
|
||||||
|
uint8_t enc_key[16], mac_key[16], dek_key[16];
|
||||||
|
uint8_t test_keys[48];
|
||||||
|
|
||||||
|
/* Generate PlatformSCP03 keys */
|
||||||
|
generate_test_keys(enc_key, mac_key, dek_key);
|
||||||
|
memcpy(test_keys, enc_key, 16);
|
||||||
|
memcpy(test_keys + 16, mac_key, 16);
|
||||||
|
memcpy(test_keys + 32, dek_key, 16);
|
||||||
|
|
||||||
|
/* Create session and SCP03 context */
|
||||||
|
session = create_mock_session();
|
||||||
|
TEST_ASSERT(session != NULL, "Should allocate mock session");
|
||||||
|
|
||||||
|
se050_status_t status = se050_scp03_init(&scp03, session);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 init should succeed");
|
||||||
|
|
||||||
|
/* Create temporary key file */
|
||||||
|
const char *key_file = "/tmp/test_platform_scp03_keys.bin";
|
||||||
|
FILE *fp = fopen(key_file, "wb");
|
||||||
|
TEST_ASSERT(fp != NULL, "Should create PlatformSCP03 key file");
|
||||||
|
|
||||||
|
fwrite(test_keys, 1, sizeof(test_keys), fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
/* Create a new context to test file loading */
|
||||||
|
se050_scp03_ctx_t *scp03_file = NULL;
|
||||||
|
status = se050_scp03_init(&scp03_file, session);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "SCP03 init for file test should succeed");
|
||||||
|
|
||||||
|
/* Load keys from file */
|
||||||
|
status = se050_scp03_load_keys_from_file(scp03_file, key_file);
|
||||||
|
TEST_ASSERT_EQ(status, SE050_OK, "Load PlatformSCP03 keys from file should succeed");
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
se050_scp03_free(scp03_file);
|
||||||
|
se050_scp03_free(scp03);
|
||||||
|
free_mock_session(session);
|
||||||
|
remove(key_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Main Test Runner
|
||||||
|
*/
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
printf("========================================\n");
|
||||||
|
printf("Platform SCP03 Test Suite\n");
|
||||||
|
printf("Based on NXP AN12436\n");
|
||||||
|
printf("========================================\n");
|
||||||
|
|
||||||
|
/* Run all test cases */
|
||||||
|
test_scp03_init();
|
||||||
|
test_scp03_set_keys();
|
||||||
|
test_scp03_invalid_args();
|
||||||
|
test_scp03_padding();
|
||||||
|
test_scp03_counter();
|
||||||
|
test_scp03_iv_generation();
|
||||||
|
test_crypto_memneq();
|
||||||
|
test_memzero_explicit();
|
||||||
|
test_secure_memcpy();
|
||||||
|
test_scp03_load_keys_from_file();
|
||||||
|
test_scp03_multiple_operations();
|
||||||
|
test_scp03_cleanup();
|
||||||
|
test_secure_memcmp();
|
||||||
|
test_key_sizes();
|
||||||
|
test_scp03_platform_integration();
|
||||||
|
test_scp03_platform_key_file();
|
||||||
|
|
||||||
|
/* Summary */
|
||||||
|
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");
|
||||||
|
|
||||||
|
return test_failed > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user