ライブラリ構造
wolfHSMは、組込みシステム向けに安全で効率的なハードウェアセキュリティモジュール(HSM)APIを提供するために設計した、モジュラーで拡張可能なライブラリです。 このライブラリは、特定のアプリケーションの要件を満たすために簡単に設定・組み合わせることが可能な一連の機能コンポーネントを中心に構築されています。 この章では、コンポーネントアーキテクチャ、通信レイヤー、不揮発性メモリ(NVM)、鍵管理、暗号化操作、ハードウェアセキュリティモジュール(HSM)サポートなど、wolfHSMの主要な機能コンポーネントの概要を説明します。
目次
- コンポーネントアーキテクチャ
- 通信
- 主要コンポーネント
- クライアント/サーバーAPI
- 通信レイヤー
- 不揮発性メモリ
- 不揮発性メモリのメタデータ
- 不揮発性メモリのアーキテクチャ
- 不揮発性メモリのバックエンド
- 鍵管理
- 暗号処理
- ハードウェア暗号サポート
コンポーネントアーキテクチャ
wolfHSMの各コンポーネントは、共通の初期化、設定、コンテキストストレージアーキテクチャを持つように設計しています。 これは、異なるハードウェアプラットフォームやビルド環境に簡単に移植できるようにするためです。
ハードウェアの詳細は、void*
として参照される型付けされていないコンテキスト構造体にコールバック関数を関連付けることで、論理操作から抽象化しています。
コンポーネントの初期化
wolfHSMコンポーネントの、一般的な設定と初期化の例を示します。
#include "wolfhsm/component.h" /* wolfHSM コンポーネントの抽象APIリファレンス */
#include "port/vendor/mycomponent.h" /* プラットフォーム固有の設定とコンテキスト構造体の定義、
* およびコールバック関数の宣言 */
/* mycomponent用の関数コールバックのルックアップテーブルを提供。型は
wolfhsm/component.hで提供される抽象型であることに注意 */
whComponentCb my_cb[1] = {MY_COMPONENT_CB};
/* 固定設定データ。関連データはinit()の実行中に構造体から
* コピーされることに注意 */
const myComponentConfig my_config = {
.my_number = 3,
.my_string = "This is a string",
}
/* myComponentの動的状態の静的割り当て */
myComponentContext my_context[1] = {0};
/* プラットフォーム固有のコールバックを使用したコンポーネントの初期化 */
const whComponentConfig comp_config[1] = {
.cb = my_cb,
.context = my_context,
.config = my_config
};
whComponentContext comp_context[1] = {0};
int rc = wh_Component_Init(comp_context, comp_config);
rc = wh_Component_DoSomething(comp_context, 1, 2, 3);
rc = wh_Component_CleanUp(comp_context);
通信
wolfHSMの通信レイヤーは、クライアントとサーバー間において信頼性の高い双方向のパケットベースの通信を提供するように設計しています。 このレイヤーは基盤となるトランスポートメカニズムを抽象化し、柔軟性とモジュール性を実現します。 クライアントとサーバーの両方でリクエストとレスポンスの機能を分割していることで、メッセージ受信の同期的なポーリングや、割り込み/イベントサポートに基づく非同期処理を可能にしています。
主要コンポーネント
- クライアント/サーバーAPI:クライアントとサーバー間の通信のためのメインインターフェース。これらはユーザーアプリケーションによって直接使用されるAPIです。
- 通信レイヤー:クライアントとサーバー間で交換されるメッセージのフォーマットと構造を定義し、基盤となるトランスポートレイヤーの実装への抽象インターフェースを提供。
- トランスポートレイヤー:基盤となるトランスポートの具体的な実装。クライアントとサーバー間でデータが実際にどのように転送されるかを定義します。
クライアント/サーバーAPI
高レベルのクライアントとサーバーAPI(wolfhsm/wh_client.h
とwolfhsm/wh_server.h
で定義)は、通信のための主要なインターフェースです。
これらの関数は、呼び出し元から低レベルの通信の詳細を抽象化し、論理操作のためのシンプルな分割トランザクションインターフェースを提供します。
以下に、クライアントAPIを使用してサーバーにエコーリクエストを送信する場合の実装例を示します。
/* エコーリクエストを送信 */
wh_Client_EchoRequest(&clientCtx, sendLen, &sendBuffer);
/* 任意で他の処理を実行 */
/* サーバーのレスポンスをポーリング */
while (WH_ERROR_NOTREADY == wh_Client_EchoResponse(client, &recv_len, recv_buffer));
通信レイヤー
通信レイヤーは、より高レベルのクライアントとサーバーのAPIによって呼び出され、下位レベルのトランスポートとの間でデータを送受信するためのメッセージング構造と制御ロジックをカプセル化します。 下位レベルのトランスポートと対話するための抽象インターフェース関数を提供する通信クライアントと通信サーバーの抽象化を実現しています。 通信レイヤーAPIは、リクエストとレスポンスの送受信機能で構成されており、これらのリクエストとレスポンスは高レベルの操作ではなくメッセージに関連します。
各クライアントは、一度に1つのリクエストのみをサーバーに送信できます。 サーバーはクライアントの分離を確実にするため、一度に1つのリクエストを処理します。
メッセージ
メッセージは、可変長のペイロードを持つヘッダーで構成されます。 ヘッダーはシーケンスIDとリクエストまたはレスポンスの種類を示します。併せて、補助フラグやセッション情報を提供するための追加フィールドも提供します。
/* wolfhsm/wh_comm.h */
typedef struct {
uint16_t magic;
uint16_t kind;
uint16_t seq;
uint16_t size;
} whCommHeader;
メッセージは、サーバーが望ましい機能を実行するために必要なリクエストデータと、機能の実行結果をクライアントに返すためのレスポンスをカプセル化するために使用されます。 メッセージタイプは、機能を実行するコンポーネントに基づいてグループ化され、列挙された機能のどれが実行されているかを一意に識別します。 互換性(エンディアンとバージョン)を確保するためにメッセージにはMagicフィールドが含まれており、ペイロード内で渡されるデータをネイティブ処理用にデマーシャリングするために必要な操作を示す既知の値が使用されます。 各機能コンポーネントには、ネイティブ値と「on-the-wire」メッセージフォーマット間の変換を行うリモート実装があります。 サーバーは、レスポンスフォーマットがリクエストフォーマットと一致することを保証します。
メッセージ内でデータコンテンツを渡すことに加えて、特定のメッセージタイプは共有またはマップされたメモリポインタの受け渡しもサポートしています。
これは、特にサーバーコンポーネントがDMA方式でデータに直接アクセスできる可能性のある、パフォーマンスクリティカルな操作のためのものです。
整数ポインタサイズ(IPS)とsize_t
の違いを避けるため、可能な場合はすべてのポインタとサイズをuint64_t
として送信する必要があります。
メッセージは、ヘッダーのMagicフィールドを使用して「on-the-wire」フォーマットでエンコードされ、構造体メンバーの指定されたエンディアンと通信ヘッダーのバージョン(現在は0x01)が示されます。 リクエストメッセージを処理するサーバーコンポーネントは、提供された値をネイティブフォーマットに変換し、タスクを実行し、結果をリクエストのフォーマットに再エンコードします。 クライアントでは、リクエストフォーマットと一致しないメッセージを処理する必要はありません。 エンコードされたメッセージは、Magicフィールドで指定されたエンディアンで、かつネイティブ構造体と同じサイズとレイアウトを想定しています。
以下に、クライアント通信レイヤーがリクエストを送信する際の実装例を示します。
uint16_t req_magic = wh_COMM_MAGIC_NATIVE;
uint16_t req_type = 123;
uint16_t request_id;
char* req_data = "RequestData";
rc = wh_CommClient_SendRequest(context, req_magic, req_type, &request_id,
sizeof(req_data), req_data);
/* 他のタスクを実行 */
uint16_t resp_magic, resp_type, resp_id, resp_size;
char response_data[20];
while((rc = wh_CommClient_RecvResponse(context,&resp_magic, &resp_type, &resp_id,
&resp_size, resp_data)) == WH_ERROR_NOTREADY) {
/* 他のタスクを実行 or yield */
}
メッセージレイヤーに渡されるトランスポートエラーは致命的であると想定されます。 クライアント/サーバーはコンテキストをクリーンアップする必要があることに注意してください。
トランスポート
トランスポートは、ライブラリがリクエストまたはレスポンスとして処理するために、可変サイズ(最大MTUまで)の完全なパケット(バイト列)をメッセージレイヤーに提供します。
whTransportClientCb
で定義された抽象インターフェースを実装し、データの送受信が必要な時にcommClient/commServerによって直接呼び出されます。
whTransportClientCb
インターフェースを実装するカスタムトランスポートモジュールは、サーバーとクライアントに登録できます。
その後、標準的なサーバーとクライアントのリクエスト/レスポンス機能を介して自動的に使用されます。
メモリバッファトランスポートモジュールとPOSIX TCPソケットトランスポートの実装例は、wolfHSMでサポートしているトランスポートの中で見ることができます。
サポートしているトランスポート
wolfHSMは、以下2つの組み込みトランスポートを付属しています。
- メモリバッファトランスポート(
wh_transport_mem.c
) - POSIX TCPソケットトランスポート(
port/posix_transport_tcp.c
)
メモリトランスポートは、ほとんどの組込みwolfHSMポートのデフォルトトランスポートであり、wolfHSMコアライブラリの一部です。 クライアントとサーバー間の共有メモリブロックを使用して、トランスポートコールバックの具体的な実装を提供します。 共有メモリトランスポートメカニズムは、2つのメモリブロックを割り当てることで動作します。 1つは受信リクエスト用、もう1つは送信レスポンス用です。クライアントは受信メモリブロックにリクエストを書き込み、送信メモリブロックからレスポンスを読み取ります。 サーバーは受信メモリブロックからリクエストを読み取り、送信メモリブロックにレスポンスを書き込みます。 各ブロックには、使用準備が整ったときに消費者に通知する制御とステータスフラグが含まれています。 このメカニズムは、システムコールやネットワーク通信の必要性を回避するため、高速かつ効率的になるように設計されています。
POSIX TCPトランスポートは、wolfHSM POSIXポートの一部です。 クライアントとサーバー間のデータのトランスポート媒体としてTCPソケットを使用します。 ソケットはIPv4のみで、ノンブロッキングです。
不揮発性メモリ
wolfHSMにおける不揮発性メモリ(NVM)は、メタデータとデータブロックを持つ永続的なオブジェクトを管理するために使用されます。 NVMライブラリは、成功を返す前にトランザクションが完全にコミットされることを保証する、信頼性の高い原子的な操作を確保します。 主要な操作には、オブジェクトの追加、一覧表示、読み取り、破棄、および関連するメタデータの取得が含まれます。
NVMの主要な機能として、以下が含まれます。
- アクセス可能なNVM内の可変サイズデータとメタデータ(ID、ラベル、長さ、アクセス、フラグ)を関連付けるためのAPI
- ステータスフラグを持つ2つの消去可能なパーティションを使用して、常に復元可能
- オブジェクトは次のエントリを使用して追加され、空き領域にプログラムされる
- 重複したIDは許可されるが、最新のものだけが読み取り可能
- オブジェクトは、リストされたオブジェクトを除いて、非アクティブなパーティションに全領域をコピーすることで破棄される
- 復旧時に後のオブジェクトを識別するため、内部エポックカウンタを使用
不揮発性メモリのメタデータ
不揮発性メモリ(NVM)メタデータは、NVMに保存されたオブジェクトを管理および記述するために使用されます。 このメタデータは、識別子、アクセス権限、フラグ、その他の属性など、各オブジェクトに関する重要な情報を提供します。 メタデータにより、NVM内のオブジェクトを確実に管理、アクセス、操作することができます。
/* NVMオブジェクトのユーザー指定メタデータ */
typedef struct {
whNvmId id; / 一意の識別子 /
whNvmAccess access; / アクセス権限 /
whNvmFlags flags; / 追加フラグ /
whNvmSize len; / データの長さ(バイト単位) /
uint8_t label[WOLFHSM_NVM_LABEL_LEN]; / ラベル */
} whNvmMetadata;
- ID (
whNvmId id
):NVMオブジェクトの一意の識別子です。このIDはNVM内の特定のオブジェクトを参照しアクセスするために使用されます。オブジェクトの読み取り、書き込み、削除などの操作を可能にします。 - アクセス (
whNvmAccess access
):オブジェクトのアクセス権限を定義します。このフィールドは、誰がどのような条件でオブジェクトにアクセスできるかを指定します。セキュリティポリシーを実施し、権限のあるエンティティのみがオブジェクトと対話できることを保証します。 - フラグ (
whNvmFlags flags
):追加情報を提供したりオブジェクトの動作を変更したりする追加フラグです。フラグは、オブジェクトが読み取り専用か、一時的か、その他の特定のプロパティを持つかなど、特別な属性や状態でオブジェクトをマークするために使用できます。 - 長さ (
whNvmSize len
):オブジェクトに関連付けられたデータの長さ(バイト単位)です。 - ラベル (
uint8_t label[]
):オブジェクトの人間が読める形式のラベルまたは名前です。
不揮発性メモリのアーキテクチャ
wolfHSMサーバーは、不揮発性メモリ(NVM)操作を処理するために汎用コンポーネントアーキテクチャアプローチに従います。 設定は汎用部分と特定部分に分かれており、柔軟性とカスタマイズ性を提供します。
-
汎用設定 (wh_nvm.h): このヘッダーファイルは、NVM操作の汎用インターフェースを定義します。
nvm_Read
、nvm_Write
、nvm_Erase
、nvm_Init
などのNVM操作用の関数ポインタが含まれています。これらの関数ポインタはwhNvmConfig
構造体の一部であり、実際のNVM実装を抽象NVMインターフェースにバインドするために使用されます。 -
特定設定 (wh_nvm_flash.c, wh_nvm_flash.h): これらのファイルは、フラッシュメモリ用のNVMインターフェースの具体的な実装を提供します。ここで定義される関数は、汎用インターフェースで定義された関数シグネチャに準拠しており、NVM操作の実際の実装として使用できます。
whServerContext
構造体にはwhNvmConfig
メンバーが含まれています。これはNVM操作をサーバーコンテキストにバインドするために使用され、サーバーが設定されたNVMインターフェースを使用してNVM操作を実行できるようにします。
サーバーでNVMを初期化するには、以下の手順が必要です。
whNvmConfig
構造体を割り当て、初期化し、特定のNVMバックエンド(例:wh_nvm_flash.c
から)へのバインディングを提供whServerConfig
構造体を割り当て、初期化し、そのnvmConfig
メンバーを手順1で初期化したwhNvmConfig
構造体に設定whServerContext
構造体を割り当てwh_Server_Init()
を呼び出して、whServerConfig
構造体でサーバーを初期化
これにより、サーバーは指定されたバッキングストアで設定されたNVM操作を使用できるようになり、whNvmConfig
構造体で異なる実装を提供することで簡単に入れ替えることができます。
不揮発性メモリのバックエンド
現在、wolfHSMがサポートしているNVMバックエンドプロバイダーは、NVMフラッシュモジュール(wh_nvm_flash.c
)のみです。
このモジュールは、NVMインターフェース関数(wh_nvm.h
)の具体的な実装を提供し、NVMデータストアをフラッシュメモリデバイスにマッピングします。
低レベルのフラッシュドライバーはデバイス固有であり、それ自体が汎用コンポーネント(wh_flash.h
)として指定され、ターゲットハードウェアに応じて入れ替えることができます。
鍵管理
wolfHSMライブラリは、不揮発性メモリからの鍵の保存、読み込み、エクスポート、高速アクセスのためにRAMに頻繁に使用される鍵のキャッシング、およびハードウェア専用デバイス鍵との対話など、包括的な鍵管理機能を提供します。 鍵は、対応するアクセス保護とともに、他のNVMオブジェクトと並んで不揮発性メモリに保存されます。 wolfHSMは、特定のコンシューマーが使用するために鍵が選択されたとき、必要な暗号化ハードウェアに鍵を自動的に読み込みます。 鍵管理APIの詳細については、クライアントライブラリとAPIドキュメントのセクションをご参照ください。
暗号処理
wolfHSMの特徴的な機能の1つは、クライアントアプリケーションがwolfCrypt APIを直接使用できるようにしながら、基盤となる暗号化操作を実際にHSMコア上で実行することです。 これは以下のメリットをもたらします。
- クライアントアプリケーションは、HSM間でデータを双方向に受け渡すために必要な複雑な通信トランザクションを設定する必要がありません。これにより、劇的にシンプルになります。
- wolfCrypt呼び出しのパラメータを1つ変更するだけで、ローカルとリモートのHSM実装を簡単に切り替えることができます。実装の最大限の柔軟性と開発の容易さを実現します。クライアントアプリケーションの開発は、HSMコアがオンラインになる前でも、wolfCryptのローカルインスタンスでプロトタイプを作成できます。
- wolfHSM APIは、シンプルかつ安定していて、豊富なドキュメントがあり、あらゆる環境でその動作が検証されています。
wolfCrypt API呼び出しをwolfHSMサーバーに簡単にリダイレクトできる機能は、wolfCryptの「暗号コールバック」(cryptocbとも呼ばれる)に基づいています。
wolfHSMクライアントは、暗号コールバックとしてリモートプロシージャコール(RPC)ロジックを実装することで、wolfCrypt API呼び出しをwolfHSMサーバーにリダイレクトできます。
wolfCryptの暗号コールバックフレームワークにより、ユーザーは選択した暗号化アルゴリズムのデフォルト実装を上書きし、実行時に独自のカスタム実装を提供できます。
wolfHSMクライアントライブラリは、wolfCryptに暗号コールバックを登録し、各wolfCrypt暗号化API関数を安全な環境で実行されるHSMサーバーへのリモートプロシージャコールに変換します。
暗号コールバックは、ほとんどのwolfCrypt API呼び出しで受け入れられるデバイスID(devId
)パラメータに基づいて選択されます。
wolfHSMは、wolfHSMサーバー暗号化デバイスを表すWOLFHSM_DEV_ID
値を定義しており、これを任意のwolfCrypt関数のdevId
パラメータとして渡すことができます。
devId
パラメータをサポートするwolfCrypt APIにはWOLFHSM_DEV_ID
を渡すことができます。
これがサポートされている場合、暗号化操作は自動的にwolfHSMサーバーによって実行されます。
ハードウェア暗号サポート
多くのHSMデバイスは、いくつかのアルゴリズムをターゲットとしたハードウェアアクセラレーション機能も備えています。 wolfHSMサーバーは、HSMサーバーサイドの暗号化処理をデバイスハードウェアにオフロードすることもサポートしています。 HSMデバイスが対応している場合、wolfHSMサーバーはユーザーからの特別な入力を必要とせずに、これを自動的に行うように設定できます。 デバイス固有のハードウェアアクセラレーション機能については、そのデバイスのwolfHSMポートのドキュメントをご覧ください。
AUTOSAR SHE
(本章は後日提供予定です。)