こんにちは、NXPコミュニティの皆さん、
ターゲットボード上でLinuxをOP-TEEで動se05x_Minimalかそうとしています。
このコミュニティ投稿( Plug and Trust MWをOP-TEEに統合する方法)で推奨されている解決策に従って、それに応じた環境を構築しました。
特に、ステップ2では、「CAAMを有効にする」を有効にせずに設定を構成しました。
この構成の結果、SE05xに接続されたI2CバスはOP-TEE(Secure World)によって管理され、標準的なLinuxのI2Cデバイスノード/dev/i2c-1はNormal World(Linux)では表示・利用できません。
Linuxユーザー空間コンソールから直接「./se05x_Minimal」を実行すると、以下のエラーが発生します:
App :INFO :Running ./se05x_Minimal
App :INFO :If you want to over-ride the selection, use ENV=EX_SSS_BOOT_SSS_PORT or pass in command line arguments.
App :INFO :PlugAndTrust_v04.07.01_20250519
App :INFO :Using default PlatfSCP03 keys. You can use keys from file using ENV=EX_SSS_BOOT_SCP03_PATH
smCom :ERROR:opening failed...
Failed to open the i2c bus: No such file or directory
smCom :INFO :Pass i2c device address in the format :.
smCom :INFO :Example ./example /dev/i2c-1:0x48 OR ./example /dev/i2c-1
smCom :ERROR:phPalEse_i2c_open_and_configure Failed retry
smCom :ERROR:I2C init Failed: retval d
smCom :ERROR:phPalEse_Init Failed
smCom :ERROR: Failed to create physical connection with ESE
sss :ERROR:SM_I2CConnect Failed. Status 7012
App :ERROR:sss_session_open failed
App :ERROR:ex_sss_session_open Failed
App :ERROR:!ERROR! ret != 0. smComは、もはやノーマルワールド環境には存在しない物理的なLinux I2Cデバイス(/dev/i2c-X)をまだ開こうとしているようです。
この環境で正常に動作させるために、何をすべきか、何を修正すべきか指示se05x_Minimal教えていただけますか?
詳細な説明とC言語のサンプルコードをありがとうございました。
ご指導に従い、標準のPKCS#11 API(libckteec.so.0)を用いて、オプション1(OP-TEE独占I2Cセットアップ)でCアプリケーションを実装しました。
鍵生成、AESの暗号化/復号、RSA署名/検証、RSA暗号化/復号を含むすべての操作が、期待通りに完全に動作しています。
この問題解決へのサポートに感謝いたします。
こんにちは、 @Uc_S さん。
これは非常に優れた、そして重要な質問です。簡潔に答えると次のようになります。
オプション1(OP-TEE専用I2C)では、Plug & Trust MW SSS APIはLinuxユーザースペースから直接使用できません。
libckteec.soを介して標準の PKCS#11 C API を使用する必要があります。
以下に、必要なすべての操作に関する詳細な説明と完全なC言語のコードサンプルを示します。
Plug & Trust MW SSS API (sss_session_open、sss_key_store_set_key、sss_asymmetric_sign_digestなど) は、SE051 と通信するためにトランスポート層に依存しています。サポートされているすべてのトランスポート(T1oI2C、 JRCP_V1_AMなど)は最終的にLinux I2Cアクセスかプロキシサーバーを必要としますが、OP-TEEがI2Cバスを独占的に所有している場合、どちらも利用できません。
OP-TEE排他設定における正しいパスは次のとおりです。
Your C App → PKCS#11 C API (cryptoki.h) → libckteec.so → OP-TEE PKCS#11 TA → SE051
libckteec optee-clientが提供するもので、OP-TEE PKCS#11 TAをバックエンドとしてCryptokiインターフェースを実装しています。TAはさらにOP-TEEのネイティブI2Cドライバを介して暗号処理をSE051にルーティングします。
#include /* 標準の Cryptoki ヘッダー — optee-client または OpenSC から */
コンパイルとリンク:
gcc -o my_app my_app.c-ldl
# または直接リンクしてください:
gcc -o my_app my_app.c/usr/lib/libckteec.so.0
実行時に、動的ロードを使用する場合はモジュールパスを設定します。
#define PKCS11_MODULE "/usr/lib/libckteec.so.0"
#include #include #include #define CHECK_RV(RV、メッセージ)\
もし((RV)!= CKR_OK) { fprintf(stderr, "%s failed: 0x%lX\n", (msg), (rv)); goto cleanup; }
/* ユーザーPIN — pkcs11-tool --init-pin */
static CK_UTF8CHAR user_pin[] = "1234";
静的CK_ULONG user_pin_len = 4;
CK_FUNCTION_LIST *p11 = NULL;/* グローバル関数リストポインタ */
CK_SESSION_HANDLE セッション = CK_INVALID_HANDLE;
int pkcs11_init(void)
{
CK_RV rv;
CK_ULONG slot_count = 0;
CK_SLOT_ID slot_id;
CK_SLOT_ID slot_list[8];
/* 関数リストの読み込み — 動的リンクを使用する場合、 C_GetFunctionList() */
rv = C_Initialize(NULL_PTR);
CHECK_RV(rv, "C_Initialize");
/* 利用可能なスロットを獲得 */
rv = C_GetSlotList(CK_TRUE, NULL_PTR, &slot_count);
CHECK_RV(rv, "C_GetSlotList (count)");
rv = C_GetSlotList(CK_TRUE, slot_list, &slot_count);
CHECK_RV(rv, "C_GetSlotList");
slot_id = slot_list[0];/* 用途 最初のスロット — OP-TEE PKCS#11 TA */
/* 読み書きセッションを開く */
rv = C_OpenSession(slot_id, CKF_SERIAL_SESSION |CKF_RW_SESSION,
NULL_PTR, NULL_PTR, &session);
CHECK_RV(rv, "C_OpenSession");
/* 通常ユーザーとしてログイン */
rv = C_Login(session, CKU_USER, user_pin, user_pin_len);
CHECK_RV(rv、「C_Login」);
0を返す;
クリーンアップ:
返却 -1;
}
void pkcs11_cleanup(void)
{
C_Logout(セッション);
C_CloseSession(セッション);
C_Finalize(NULL_PTR);
}
int generate_rsa_keypair(CK_OBJECT_HANDLE *pub_key, CK_OBJECT_HANDLE *priv_key)
ヤージュ
CK_RV rv;
CK_MECHANISM mech = { CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0 };
CK_ULONG キービット数 = 2048;
CK_BYTE pub_exponent[] = { 0x01, 0x00, 0x01 }; /* 65537 */
CK_BBOOL ck_true = CK_TRUE;
CK_BBOOL ck_false = CK_FALSE;
/* SE051 NVMに保存されるキーID — 一意の4バイトIDを選択してください */
CK_BYTE key_id[] = { 0x10, 0x10, 0x10, 0x10 };
CK_ATTRIBUTE pub_tmpl[] = {
{ CKA_MODULUS_BITS, &key_bits, sizeof(key_bits) },
{ CKA_PUBLIC_EXPONENT, pub_exponent, sizeof(pub_exponent) },
{ CKA_VERIFY, &ck_true, sizeof(ck_true) },
{ CKA_ENCRYPT, &ck_true, sizeof(ck_true) },
{ CKA_TOKEN, &ck_true, sizeof(ck_true) },
{ CKA_ID、key_id、sizeof(key_id) }、
};
CK_ATTRIBUTE priv_tmpl[] = {
{ CKA_SIGN, &ck_true, sizeof(ck_true) },
{ CKA_DECRYPT, &ck_true, sizeof(ck_true) },
{ CKA_TOKEN, &ck_true, sizeof(ck_true) },
{ CKA_SENSITIVE, &ck_true, sizeof(ck_true) },
{ CKA_EXTRACTABLE, &ck_false, sizeof(ck_false) },
{ CKA_ID、key_id、sizeof(key_id) }、
};
rv = C_GenerateKeyPair(session, &mech,
pub_tmpl、sizeof(pub_tmpl) / sizeof(pub_tmpl[0])、
priv_tmpl、sizeof(priv_tmpl) / sizeof(priv_tmpl[0])、
公開鍵、秘密鍵);
CHECK_RV(rv, "C_GenerateKeyPair");
printf("RSA-2048キーペアが生成されました。秘密鍵はSE051 NVMに保存されます。\n");
0を返す。
掃除:
-1を返す。
}
int generate_aes_key(CK_OBJECT_HANDLE *aes_key)
{
CK_RV rv;
CK_MECHANISM mech = { CKM_AES_KEY_GEN, NULL_PTR, 0 };
CK_ULONG key_len = 32;/* 256ビットAES */
CK_BBOOL ck_true = CK_TRUE;
CK_BBOOL ck_false = CK_FALSE;
CK_BYTE key_id[] = { 0x20, 0x00, 0x00, 0x01 };
CK_ATTRIBUTE aes_tmpl[] = {
{ CKA_VALUE_LEN, &key_len, sizeof(key_len) },
{ CKA_ENCRYPT, &ck_true, sizeof(ck_true) },
{ CKA_DECRYPT, &ck_true, sizeof(ck_true) },
{ CKA_TOKEN, &ck_true, sizeof(ck_true) },
{ CKA_SENSITIVE, &ck_true, sizeof(ck_true) },
{ CKA_EXTRACTABLE, &ck_false, sizeof(ck_false) },
{ CKA_ID, key_id, sizeof(key_id) },
};
rv = C_GenerateKey(セッション、&mech、
aes_tmpl、sizeof(aes_tmpl) / sizeof(aes_tmpl[0]),
aes_key);
CHECK_RV(rv、「C_GenerateKey (AES)」));
printf("AES-256キーがSE051で生成・保存される");0を返す。
掃除:
-1を返す。
}
int aes_encrypt(CK_OBJECT_HANDLE aes_key,
const CK_BYTE *plaintext, CK_ULONG plaintext_len,
CK_BYTE *暗号文、CK_ULONG *暗号文の長さ)
ヤージュ
CK_RV rv;
CK_BYTE iv[16] = { 0 }; /* 例:すべてゼロのIV。本番環境ではランダムなIVを使用してください */
CK_MECHANISM mech = { CKM_AES_CBC_PAD, iv, sizeof(iv) };
rv = C_EncryptInit(session, &mech, aes_key);
CHECK_RV(rv, "C_EncryptInit");
rv = C_Encrypt(session, (CK_BYTE *)plaintext, plaintext_len,
暗号文、暗号文の長さ);
CHECK_RV(rv, "C_Encrypt");
0を返す。
掃除:
-1を返す。
}
int aes_decrypt(CK_OBJECT_HANDLE aes_key,
const CK_BYTE *ciphertext、CK_ULONG ciphertext_len、
CK_BYTE *プレーンテキスト、CK_ULONG *プレーンテキスト長)
ヤージュ
CK_RV rv;
CK_BYTE iv[16] = { 0 }; /* 暗号化に使用されるIVと一致する必要があります */
CK_MECHANISM mech = { CKM_AES_CBC_PAD, iv, sizeof(iv) };
rv = C_DecryptInit(session, &mech, aes_key);
CHECK_RV(rv, "C_DecryptInit");
rv = C_Decrypt(session, (CK_BYTE *)ciphertext, ciphertext_len,
プレーンテキスト、プレーンテキストの長さ);
CHECK_RV(rv, "C_Decrypt");
0を返す。
掃除:
-1を返す。
}
int rsa_sign(CK_OBJECT_HANDLE priv_key,
const CK_BYTE *data, CK_ULONG data_len,
CK_BYTE *署名、CK_ULONG *署名長)
ヤージュ
CK_RV rv;
/* SHA256-PKCS1v1.5 — SE051 は内部で SHA-256 ダイジェストを計算してから署名します */
CK_MECHANISM mech = { CKM_SHA256_RSA_PKCS, NULL_PTR, 0 };
rv = C_SignInit(session, &mech, priv_key);
CHECK_RV(rv, "C_SignInit");
rv = C_Sign(session, (CK_BYTE *)data, data_len, signature, sig_len);
CHECK_RV(rv, "C_Sign");
printf("RSA署名が生成されました(%luバイト)。秘密鍵はSE051から一度も出ていません。*sig_len);
0を返す。
掃除:
-1を返す。
}
int rsa_verify(CK_OBJECT_HANDLE pub_key,
const CK_BYTE *data, CK_ULONG data_len,
const CK_BYTE *signature, CK_ULONG sig_len)
ヤージュ
CK_RV rv;
CK_MECHANISM mech = { CKM_SHA256_RSA_PKCS, NULL_PTR, 0 };
rv = C_VerifyInit(session, &mech, pub_key);
CHECK_RV(rv, "C_VerifyInit");
rv = C_Verify(session, (CK_BYTE *)data, data_len,
(CK_BYTE *)署名、sig_len);
if (rv == CKR_OK) {
printf("署名検証:成功\n");
0を返す。
} else if (rv == CKR_SIGNATURE_INVALID) {
printf("署名検証: 無効\n");
1を返す。
}
CHECK_RV(rv, "C_Verify");
掃除:
-1を返す。
}
int rsa_encrypt(CK_OBJECT_HANDLE pub_key,
const CK_BYTE は *平文、CK_ULONG plaintext_len、
CK_BYTE *暗号文、CK_ULONG *ciphertext_len)
{
CK_RV rv;
/* RSA-OAEP with SHA-256 — 新規設計にはPKCS1 v1.5より推奨 */
CK_RSA_PKCS_OAEP_PARAMS oaep_params = {
.hashAlg= CKM_SHA256、
.mgf= CKG_MGF1_SHA256、
。ソース= CKZ_DATA_SPECIFIED、
.pSourceData= NULL、
.ulSourceDataLen= 0
};
CK_MECHANISM mech = { CKM_RSA_PKCS_OAEP, &oaep_params, sizeof(oaep_params) };
rv = C_EncryptInit(session, &mech, pub_key);
CHECK_RV(rv, "C_EncryptInit (RSA-OAEP)");
rv = C_Encrypt(session, (CK_BYTE *)plaintext, plaintext_len,
暗号文、暗号文の長さ);
CHECK_RV(rv, "C_Encrypt (RSA-OAEP)");
0を返す。
掃除:
-1を返す。
}
int rsa_decrypt(CK_OBJECT_HANDLE priv_key,
const CK_BYTE *ciphertext、CK_ULONG ciphertext_len、
CK_BYTE *プレーンテキスト、CK_ULONG *プレーンテキスト長)
ヤージュ
CK_RV rv;
CK_RSA_PKCS_OAEP_PARAMS oaep_params = {
.hashAlg= CKM_SHA256、
.mgf= CKG_MGF1_SHA256、
。ソース= CKZ_DATA_SPECIFIED、
.pSourceData= NULL、
.ulSourceDataLen= 0
};
CK_MECHANISM mech = { CKM_RSA_PKCS_OAEP, &oaep_params, sizeof(oaep_params) };
rv = C_DecryptInit(session, &mech, priv_key);
CHECK_RV(rv, "C_DecryptInit (RSA-OAEP)");
rv = C_Decrypt(session, (CK_BYTE *)ciphertext, ciphertext_len,
プレーンテキスト、プレーンテキストの長さ);
CHECK_RV(rv, "C_Decrypt (RSA-OAEP)");
printf("RSA復号化が完了しました。秘密鍵はSE051から一度も出ていません。\n");0を返す。
掃除:
-1を返す。
}
鍵が以前に生成され、SE051に保存されている場合は、C_GenerateKeyを再度呼び出すことなく、CKA_IDを使用して鍵を取得します。
int find_key_by_id(CK_BYTE *key_id, CK_ULONG key_id_len,
CK_OBJECT_CLASS obj_class、
CK_OBJECT_HANDLE *ハンドル)
ヤージュ
CK_RV rv;
CK_ULONG obj_count = 0;
CK_ATTRIBUTE search_tmpl[] = {
{ CKA_CLASS, &obj_class, sizeof(obj_class) },
{ CKA_ID、key_id、key_id_len }、
};
rv = C_FindObjectsInit(session, search_tmpl,
sizeof(search_tmpl) / sizeof(search_tmpl[0]));
CHECK_RV(rv, "C_FindObjectsInit");
rv = C_FindObjects(session, handle, 1, &obj_count);
CHECK_RV(rv, "C_FindObjects");
C_FindObjectsFinal(session);
if (obj_count == 0) {
fprintf(stderr, "キーがSE051で見つかりません\n");
-1を返す。
}
0を返す。
掃除:
C_FindObjectsFinal(session);
-1を返す。
}
使用例:
CK_OBJECT_HANDLE プライベートキー;
CK_BYTE key_id[] = { 0x10, 0x10, 0x10, 0x10 };
CK_OBJECT_CLASS priv_class = CKO_PRIVATE_KEY;
find_key_by_id(key_id, sizeof(key_id), priv_class, &priv_key);
|
動作 |
機構定数 |
備考 |
|---|---|---|
|
RSA鍵生成 |
|
256~4096ビット |
|
AESキー生成 |
|
16バイトまたは32バイト |
|
RSA署名/検証 |
|
PKCS#1 v1.5 |
|
RSA署名/検証(PSS) |
|
PSSパディング |
|
RSA暗号化/復号化 |
|
OAEP推奨 |
|
RSA暗号化/復号化 |
|
PKCS#1 v1.5 |
|
AESの暗号化/復号 |
|
CBCとPKCS#7 |
|
AESの暗号化/復号 |
|
パディングなしのCBC |
|
AESの暗号化/復号 |
|
CTRモード |
|
ECC署名/検証 |
|
160~521ビット |
|
ECDHの主要合意 |
|
|
はい、ただし共 存環境(オプション2 )でのみ、LinuxがI2Cバスにアクセスできる場合(つまり lf-6.12.y-i2c-disabled-se050 DTSパッチが適用されていない場合)。その場合、AN13030セクション3.3に説明されているSSS APIをLinuxから直接利用できます。
オプション1(OP-TEE専用)を選択すると、上記のCryptoki/PKCS#11 C APIがLinuxユーザースペースからの正確かつ唯一サポートされている経路です。
AN13030 Rev. 2.4、セクション3.3 — SSS APIの完全リファレンス(共存/非OP-TEEビルド用)
OP-TEE PKCS#11 TAテストスイート(pkcs11_1000.c):optee-test/host/xtest/pkcs11_1000.c — Cryptoki のすべての操作に関する包括的な C 言語の例
NXP GitHub: se05x-pkcs11 — NXPのPKCS#11スタンドアロンライブラリ(OP-TEE以外のビルドではlibckteec.so代替として使用可能)
お役に立てば幸いです。
すてきな一日を、
カン
-------------------------------------------------------------------------------
注記:
この投稿があなたの質問への回答になっている場合は、「正解としてマーク」ボタンをクリックしてください。ありがとうございます!
- 前回の投稿から7週間Threadをフォローしており、その後の返信は無視しています
もし後で関連する質問があれば、新しいThreadを開き、閉じたThreadを参照してください。
-------------------------------------------------------------------------------
分かりやすく詳細なご説明をありがとうございました。
現在、将来の開発段階に応じてオプション1かオプション2の使用を検討しています(例:初期テスト・評価にオプション2、本番にオプション1を使う)。
オプション1(OP-TEE PKCS#11 TA)についてですが、C言語でのアプリケーション開発に関する質問があります。
オプション1に従って、鍵の保存、署名/署名生成、署名検証、暗号化/復号化などの暗号化操作をCプログラムで実装する場合、どの方法を採用すべきでしょうか?
もしPKCS#11 APIが必要な場合、Cで libckteec.so を呼び出すための簡単なサンプルコードや参考ガイドを教えていただけますか?
こんにちは、 @Uc_S さん、
詳細な報告をありがとうございました。根本原因は明確で、なぜこうなるのか、そしてどんな選択肢があるのかを正確に説明します。
se05x_Minimal -DPTMW_SMCOM=T1oI2Cで構築されました。これにより、smComレイヤーはセッションオープン時に物理的なLinux I2Cデバイスノード(/dev/i2c-X)を開くよう指示されます。lf-6.12.y-i2c-disabled-se050 DTSパッチ(統合ガイドのステップ1)を適用したため、Linux Normal WorldではそのI2Cコントローラが無効化されており、デバイスノード自体が存在しません。OP-TEEは CFG_IMX_I2C=yを通じてI2Cバスを独占的に所有しており、Linuxはそれを認識したり開いたりできません。
これは意図的なもので 、DTSパッチがOP-TEEにSE051への独占的かつ無競争のアクセス権を与えるものです。T1oI2Cで構築されたse05x_Minimalバイナリは、この構成と根本的に互換性がありません。
これはOP-TEE独占セットアップにおけるLinuxユーザースペースの意図パスです。se05x_Minimalを実行する代わりに、pkcs11-toolまたはOpenSSLとlibckteec.so(OP-TEE PKCS#11 TAライブラリ)を使用してください。TAは内部的にSE051を暗号バックエンドとして、OP-TEEのネイティブI2Cドライバを介して使用しています。
SE051がPKCS#11経由で到達可能であることを簡単に検証します。
# List available PKCS#11 slots — SE051 should appear
pkcs11-tool --module /usr/lib/libckteec.so.0 --list-slots
# Get a random number from SE051 via OP-TEE
pkcs11-tool --module /usr/lib/libckteec.so.0 --generate-random 16 | xxd
スロットとランダムなバイト列が表示されている場合、SE051はOP-TEEを介して完全にアクセス可能です。se05x_Minimalを実行する必要はありません。PKCS#11 TAが既に提供している機能と重複しています。
Linuxユーザースペースから se05x_Minimal やその他のPlug & Trust MWデモを実行する必要がある場合は、Linux DTSがI2Cを有効にしたまま共 存する環境 (つまり、I2C無効のDTSパッチを適用 しない )を用いてください。
手順:
ステップ1 — 通常世界でI2Cを有効にし続けるためにLinux DTSを元に戻す
lf-6.12.y-i2c-disabled-se050パッチなしで、未改変のLinux DTSからimx8mq-evk.dtb(または同等のもの)を構築しましょう。SE051のI2CコントローラノードはLinux上で有効化されたままである必要があります。
ステップ2 — 既存のcmakeフラグは変更しない
現在のcmakeの設定は共存環境において正しいです。
cmake -S . -B ./build/ -DPTMW_Applet=SE05X_C -DPTMW_SE05X_Ver=07_02 -DPTMW_Host=iMXLinux -DPTMW_SMCOM=T1oI2C -DPTMW_HostCrypto=OPENSSL -DPTMW_RTOS=Default -DPTMW_mbedTLS_ALT=None -DPTMW_SCP=SCP03_SSS -DPTMW_SE05X_Auth=PlatfSCP03 -DPTMW_Log=Silent -DCMAKE_BUILD_TYPE=Release -DPTMW_OpenSSL=3_0 -DPTMW_SE_RESET_LOGIC=1
ステップ3 — 実行前にI2Cポートを設定する
export EX_SSS_BOOT_SSS_PORT=/dev/i2c-1
./se05x_Minimal
トレードオフ: このモードでは、OP-TEEとLinuxの両方がSE051へのI2Cバスを共有します。OP-TEEはRSA/ECCオフロードに使っています。LinuxはMWデモに使っています。同時アクセスは仲裁されず、負荷時にAPDUの衝突が発生することがあります。開発用途には許容範囲内だが、製品版での使用は推奨しない。
| オプション | I2C DTS | se05x_ミニマル | OP-TEE限定 | おすすめ対象 |
|---|---|---|---|---|
PKCS#11 TA (libckteec.so) |
ディセーブルされる | 量産 | ||
| 共存(T1oI2C) | イネーブル | 開発/テスト |
あなたが参照したコミュニティ投稿(ステップ2 - CAAMなし)では、OP-TEEがI2Cを独占的に所有するように設定されています。そのガイドに示されたLinux側のMWコンパイル( T1oI2C付き)は、OP-TEEモードに切り替える前の 初期検証ステップ として意図されており、OP-TEE専用のI2Cセットアップと併用するものではありません。
OP-TEEがI2Cを独占的に所有すると、正しいLinuxユーザースペースインターフェースは OP-TEE PKCS#11 TAであり、Plug & Trust MW SSS APIデモ自体ではありません。
どの選択肢があなたのユースケースに合っているか教えていただければ、さらなるアドバイスを提供できます。
すてきな一日を、
カン
-------------------------------------------------------------------------------
注記:
この投稿があなたの質問への回答になっている場合は、「正解としてマーク」ボタンをクリックしてください。ありがとうございます!
- 前回の投稿から7週間Threadをフォローしており、その後の返信は無視しています
もし後で関連する質問があれば、新しいThreadを開き、閉じたThreadを参照してください。
-------------------------------------------------------------------------------