IoT向けセキュアエレメントをWindows11で使う

# きっかけ
IoT向けのセキュアエレメントをPCで使えるようにし、市販されているセキュリティキーの原理を学んでみたい。


# セキュリティキー概要
一般的にセキュリティキーは写真のようなUSBデバイスの形をとっている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/f47f8906-091b-3a8e-0a50-1ff3534d40fe.png)
セキュリティキーの例：Yubiko　Yubikey 5 FIPS


1. 二要素認証 (2FA) / 多要素認証 (MFA): 
セキュリティキーは、パスワードやPINといった何かを「知っている」要素と組み合わせて、ユーザーの身元を二重に確認する。これにより、パスワードが漏洩した場合でも、不正アクセスを防ぐ。

2. 暗号化: 
セキュリティキーは、秘密の情報やキーを内部で安全に保管し、外部からのアクセスを防ぐために強力な暗号化技術を使用できる。

3. 物理的な耐久性: 
多くのセキュリティキーは、水や衝撃に耐える設計となっており、物理的なダメージからも情報を保護する。

4. 広範な互換性: 
FIDO (Fast Identity Online) や U2F (Universal 2nd Factor) などの標準をサポートするセキュリティキーは、多くのオンラインサービスやアプリケーションと互換性がある。

5. プラグアンドプレイ: 
USBインターフェースを使用するセキュリティキーは、特別なドライバやソフトウェアを必要とせず、多くのデバイスで即座に使用することができる。


# IoT向けセキュアエレメント概要
一方でIoT向けセキュアエレメントは半導体チップで提供され、マイコン、MPUへ接続される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/235e120c-8cdb-e193-acd0-69fb65467343.png)
IoT向けセキュアエレメントの例：NXP SE050


1. 強固な物理的セキュリティ:
セキュアエレメントは、物理的な攻撃（例：サイドチャネル攻撃、電磁的攻撃）から保護されるように設計されている。
タンパー検出機能があり、不正なアクセスを検知した場合には自動的にデータを消去することができる。

2. 暗号化とキー管理:
セキュアエレメントは、秘密鍵や証明書などのセンシティブな情報を安全に保管する。
高度な暗号アルゴリズムをサポートし、データの暗号化やデジタル署名の生成・検証を行う。

3. 認証とアクセス制御:
デバイス同士の相互認証をサポートし、不正なデバイスからのアクセスを防ぐ。
デバイスやユーザーのアクセス権を制御し、センシティブな操作やデータへのアクセスを制限する。

4. スケーラビリティと柔軟性:
IoT環境は多様であり、異なるデバイスや通信プロトコルが存在します。セキュアエレメントは、さまざまな用途や環境に適応するための柔軟性を持つ。

5. 低消費電力:
IoTデバイスはしばしばバッテリー駆動であり、長い寿命が求められる。セキュアエレメントは、低消費電力で動作するように最適化されている。

6. 小型化:
IoTデバイスは、サイズが小さく、組み込みスペースが限られていることが多いため、セキュアエレメントも小型化されていることが求められる。


# PKCS#11
セキュリティキーで暗号化のAPIとして使われているのがPKCS#11となる。IoT向けセキュアエレメントでも共通してAWS Greengrassへの接続などで利用されるAPIである。

- 定義: PKCS#11は、暗号トークンへのアクセスを定義するAPIの標準です。この標準は、ハードウェアセキュリティモジュール（HSM）、USBセキュリティトークン、スマートカードなどの暗号トークンにアクセスするためのプラットフォーム中立のAPIを提供します。

- 主な機能:

    1. キー管理: トークン内でのキーの生成、インポート、エクスポート、ストレージ。

    2. 暗号操作: 暗号化、復号化、デジタル署名、署名の検証などの基本的な暗号操作。

    3. 認証: PINやパスワードを使用したユーザー認証。

    4. セッション管理: トークンとの通信セッションの開始と終了。

    5. セキュリティ: PKCS#11は、秘密鍵がトークンの外部に漏れることなく、トークン内で直接暗号操作を行うことを可能にします。これにより、秘密鍵が露出するリスクが軽減されます。

    6. 拡張性: PKCS#11は、さまざまな暗号アルゴリズムやトークンタイプをサポートするための拡張性があります。


# 目標

IoT向けセキュアエレメント　NXP SE050をWindows11に接続して利用できるか試す。
秘密鍵を耐タンパ性を持つセキュアエレメントで安全に秘匿できる用途を検討する。
PKCS#11でセキュアエレメントとWindows11を通信させる方針で行う。

IoT機器向けのセキュアエレメントは、ほとんどがI2C接続となっており、Windows11のような一般のPCには直接接続できない。これを解決する。

# USB-I2C方針検討

USB-I2Cの変換にはUSBの知識が必要となる。
ざっと調べたところ、

1. マイコンによるUSB-I2C変換
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/a65a91be-1c20-0815-c60e-7b96cecfbc09.png)

DigiSpark Kickstarter ATTINY85のような小型のデバイスで、[I2C-Tiny-USB](https://github.com/harbaum/I2C-Tiny-USB/tree/master/digispark)のようなファームウェアを書き込むことで、I2C変換ができるようになる。
Windows11のドライバーは、直接提供されておらず、Windows11側がドライバーに正規の電子署名を求めるため、「ドライバー署名の強制を無効にする」モードへ切り替えるか、[Zadig](https://zadig.akeo.ie/)のようなドライバーインストールを行う必要がある。
セキュリティを考慮して、「ドライバー署名の強制を無効にする」モードへ切り替えることはできないし、セキュアエレメントとPCの間にファームウェアを必要とするマイコンが介在するのもシンプルではない。Zadigを用いる方法でも、libusb,WinUSBなどのドライバーの区別と実装が困難そうで断念した。


2. 専用のUSB-I2C変換を用いる
Microchip MCP2221A, FTDI FT232Hなどの専用チップを用いるパターン。
FTDI FT232Hには、[Windows11のドライバー](https://ftdichip.com/drivers/d2xx-drivers/)も存在するようだ。
より調べていくと、[FTDI FT260](https://ftdichip.com/products/ft260q/)はUSB HIDクラス（キーボード、マウスなどのHuman Interface Device扱い）のUSB-I2C変換機能を持っているとのことだ。
同じチップを使った開発ボード　[MIKROE USB to I2C Click](https://www.mikroe.com/usb-to-i2c-click)を入手し、Windows11マシンに接続した。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/96f2684c-bc07-1e15-db90-74fd4565cb94.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/864a3295-2be7-3ba0-c0c0-3d0961e9e573.png)

ドライバーなしで認識している。

また、実装面の確認を行う。
FT260の[Application Note](https://ftdichip.com/wp-content/uploads/2020/08/AN_395_User_Guide_for_LibFT260.pdf)でアクセスするための関数を確認する。

* FT260_I2CMaster_Init
* FT260_I2CMaster_Reset
* FT260_I2CMaster_Write
* FT260_I2CMaster_Read
* FT260_I2CMaster_GetStatus

上記のような関数が確認でき、実装できそうなめどが立った。


# 環境
Windows 11 PC
Visual Studio 2022
FTDI FT260ボード（上述の[MIKROE USB to I2C Click](https://www.mikroe.com/usb-to-i2c-click)） 
NXP SE050 C1をFTDI FT260へ接続。（市販品ではMIKROE Plug&Trust clickが利用可能）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/84d53028-7c65-5b39-8c3f-cb9ff74eacf4.png)


# ポーティング
以下のような方針でポーティングを進めた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/f7e1537d-1de1-3282-1c3f-a0a025fd7f46.png)

1. NXP SE050のFT260向けHALの作成
NXPが提供するSE050用のソフトウェアはPlug and Trustという。
[NXPオフィシャル](https://www.nxp.jp/products/security-and-authentication/authentication/edgelock-se050-plug-and-trust-secure-element-family-enhanced-iot-security-with-high-flexibility:SE050)からダウンロードするフルパッケージと、GitHubに配置される[限定パッケージ](https://github.com/NXP/plug-and-trust)、組み込み用の[nanoパッケージ](https://github.com/NXPPlugNTrust/nano-package)がある。
今回、PKCS#11実装が含まれるNXPオフィシャルからダウンロードするフルパッケージを利用する。

Windows向けのHALがあるように見えるが、これは、NXP社のマイコンを経由して通信するVCOMというHALになる。
これは使えないので、新たにコードを書く必要がある。
一方、FT260は[オフィシャルサイト](https://www.ftdichip.com/old2020/Products/ICs/FT260.html)より、LibFT260のヘッダー（.h）ファイルと、DLL,LIBをダウンロードしておく。

[NXP SE050のPorting Guideline](/https://www.nxp.com/docs/en/application-note/AN12448.pdf)を確認し、i2c_a7.cを加工していく。
基本的には以下の実装を行う。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/cbffe29a-ee60-4ca7-1761-7f2857eb4a1d.png)

2. Windows 11で動作するようSE050 PKCS#11ミドルウェアの修正
PKCS#11実装が含まれるNXPオフィシャルからダウンロードするフルパッケージから、ビルドに必要なヘッダー、ソースをコンパイルする設定を行っていく。以下、主な修正点
- NXPオフィシャルからダウンロードするフルパッケージの、PKCS#11コードのうち、C_GetMechanismListのサイズ計算の修正が必要だった。
- 排他処理（Mutex）もWindows向けに書き換え。
- DLLとしてビルドするため、PKCS#11として提供する関数へ、_declspec(dllexport)を適用させる。

こうして、LibFT260.dllに依存する、DLLを完成させた。
合わせて、自己署名証明書を作成するアプリも同様に作成した。

# 動作例
まず、PKCS11-toolというコマンドラインを使って操作ができるかどうか試した。
以下の64ビット版をインストールしていく。

- [OpenSC for windows](https://github.com/OpenSC/OpenSC/wiki)
- [OpenSSL 3](https://slproweb.com/products/Win32OpenSSL.html) ライトパッケージではない、フル版をインストールした。

環境変数のPATHへ、'C:\Program Files\OpenSC Project\OpenSC\tools'を追加しておく。

まず、NXPが書き込んでいる証明書を読みだしてみる。証明書は既定のObjectIDで書き込まれており、[NXPサイトで確認](https://www.nxp.jp/docs/en/application-note/AN12436.pdf)できる。
PowerShellでは、pkcs11-toolsからopensslへ証明書データをパイプで渡す操作がうまくいかないため、cmd /cで実行する。[このあたりを参考](https://blog.shibata.tech/entry/2015/06/26/001944)にした。

```powershell
PS C:\Users\km> pkcs11-tool.exe --module <your path>se050key.dll --list-slots
Available slots:
Slot 0 (0x1):
  token label        : SSS_PKCS11
  token manufacturer : NXP
  token model        :
  token flags        : rng, token initialized
  hardware version   : 3.1
  firmware version   : 4.3
  serial num         :
  pin min/max        : 0/10

# read cert from Default Connectivity Key
PS C:\Users\km> cmd /c "pkcs11-tool.exe --module <your path>se050key.dll --id 010000F0 --read-object --type cert --slot 1 | openssl x509 -inform DER"
-----BEGIN CERTIFICATE-----
MIIB0DCCAXegAwIBAgIUBABQAWhzzrtzuLQEfo8KlGaAAAAwCgYIKoZIzj0EAwIw
VjEXMBUGA1UECwwOUGx1ZyBhbmQgVHJ1c3QxDDAKBgNVBAoMA05YUDEtMCsGA1UE
...
CgYIKoZIzj0EAwIDRwAwRAIgX9lOj5B6/UP+PhNnYAGy9WxLMjFxhnFEBdj0Uhpc
cDcCIDvYe29cY3LiFoq3AfUmxjMviE/vam+oCWxE0UuFUGsm
-----END CERTIFICATE-----

```

ECC256鍵ペア作成、署名と検証

```powershell
# EC keypair generation and signing/verifying
PS C:\Users\km> pkcs11-tool.exe --module <your path>se050key.dll --keypairgen --id 05000000 --key-type EC:secp256r1
Using slot 0 with a present token (0x1)
Key pair generated:
Private Key Object; EC
  label:      sss:05000000
  ID:         05000000
  Usage:      sign, derive
  Access:     sensitive, always sensitive
  Allowed mechanisms: ECDSA,ECDSA-SHA1
warning: PKCS11 function C_GetAttributeValue(UNIQUE_ID) failed: rv = CKR_ATTRIBUTE_SENSITIVE (0x11)

Public Key Object; EC  EC_POINT 256 bits
  EC_POINT:   044104d1f163da5dc0e7a9aeb7e7dfc86e8b2ffdee8ca1f6a9b8ea6b611ab85b19cf79ee1e42f907fb8d3036daa68bc8377cd2d8c5b4583c28d4535a37407ace02958e
  EC_PARAMS:  06082a8648ce3d030107
  label:      sss:05000000
  ID:         05000000
  Usage:      derive
  Access:     none
warning: PKCS11 function C_GetAttributeValue(UNIQUE_ID) failed: rv = CKR_ATTRIBUTE_SENSITIVE (0x11)

PS C:\Users\km> echo 123456789abcdefghijklmnopqrstu > .\testhash.dat # 32byte test data

PS C:\Users\km> pkcs11-tool.exe --module <your path>se050key.dll --id 05000000 --sign --mechanism ECDSA -i .\testhash.dat -o test.sig

PS C:\Users\km> pkcs11-tool.exe --module <your path>se050key.dll --id 05000000 --verify --mechanism ECDSA -i .\testhash.dat --signature-file test.sig
Using slot 0 with a present token (0x1)
Using signature algorithm ECDSA
Signature is valid
```

AES鍵作成と暗号化、復号

```powershell
PS C:\Users\km> pkcs11-tool.exe --module <your path>se050key.dll --keygen --key-type AES:256 --label "sss:05000002"
Using slot 0 with a present token (0x1)
Key generated:
Secret Key Object; AESwarning: PKCS11 function C_GetAttributeValue(VALUE_LEN) failed: rv = CKR_BUFFER_TOO_SMALL (0x150)

PKCS11:WARN :Not allowed to readout Symmetric key value
warning: PKCS11 function C_GetAttributeValue(VALUE) failed: rv = CKR_ATTRIBUTE_SENSITIVE (0x11)

  label:      sss:05000002
  ID:         05000002
  Usage:      encrypt, decrypt
  Access:     sensitive, always sensitive
PKCS11:WARN :Attribute required : 0x00000004

warning: PKCS11 function C_GetAttributeValue(UNIQUE_ID) failed: rv = CKR_ATTRIBUTE_SENSITIVE (0x11)

PS C:\Users\km> echo test > test.txt

PS C:\Users\km> cat .\test.txt
test

PS C:\Users\km> pkcs11-tool.exe --module <your path>se050key.dll --id 05000002 --encrypt --mechanism AES-CBC --input-file test.txt --output-file test.sec
Using slot 0 with a present token (0x1)
Using encrypt algorithm AES-CBC

PS C:\Users\km> cat .\test.sec
<暗号化された内容>

PS C:\Users\km> pkcs11-tool.exe --module <your path>se050key.dll --id 05000002 --decrypt --mechanism AES-CBC --input-file test.sec --output-file test.res
Using slot 0 with a present token (0x1)
Using decrypt algorithm AES-CBC

PS C:\Users\km> cat .\test.res
test
```

そのほか、[PKCS11Admin](https://www.pkcs11admin.net/)での認識、自己署名証明書を作成した後、[PuTTY CAC](https://github.com/NoMoreFood/putty-cac/releases)でのサーバー接続も成功した。

# まとめ
当初の目的だった、セキュリティキーに近い実装を、Windows 11にてドライバ不要で、共通のPKCS#11APIを利用可能な形にできた。
FT260とSE050だけで動作するので、別途基盤を起こせば、小型のセキュリティキー化することは可能かもしれない。
FIDO (Fast Identity Online) や U2F (Universal 2nd Factor) などはマイコンが必要になる可能性がある。

今回作成したコードは下記に配置した。
[GitHub/kmwebnet/se050-windows-pkcs11-lib](https://github.com/kmwebnet/se050-windows-pkcs11-lib)
[GitHub/kmwebnet/se050-windows-provisioning-app](https://github.com/kmwebnet/se050-windows-provisioning-app)