セキュアなIoTデバイス通信をやってみた(Azure IoT 接続編‐DPSプロビジョニング)

#はじめに#
2019年5月に[IoTLT](https://iotlt.connpass.com/)にてLTをさせていただきました。
その内容のフォローを記入しながら自分なりに整理していきたいと思います。
F
発表内容：
[スタートアップIoTデバイスのセキュリティを考える](https://www.slideshare.net/KentaroMitsuyasu1/iot-144602008)

#前回までのあらすじ#
[セキュアなIoTデバイス通信をやってみた(MbedTLS移植編-MQTTS)](https://qiita.com/kmitsu76/items/8573ee925ffd72b027e0)でMQTTSへの移植を行い、別で用意したNode-REDへデータを送信できました。

今回の記事に関連するソースコードは下記にて公開中。
[kmwebnet/ECC608-AzureDPS-Provision](https://github.com/kmwebnet/ECC608-AzureDPS-Provision)

#Azure接続のシナリオ#

Azure IoTへの接続は、Shared-Access-Signature(SAS)トークンとX509認証が対応しており、さらにプロトコルでMQTT,AMQP,HTTPSに対応するとのこと。

Azure IoTへのデバイス登録は下記の3種類があり、事前のデバイスID登録が必須
対象キーはAzure IoT Hub上で生成しデバイスへ登録、X509自己署名済み接続はデバイス証明書の拇印(フィンガープリント)を登録する必要があり、数台のデバイス登録であれば問題にならないが、登録デバイスの数が多くなると、非常に手間がかかる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/53f42950-f095-6136-08c6-f62a4fc41b8d.png)

[IoT Hub へのアクセスの制御](https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-devguide-security)

その手間を省くためにAzure IoTへ証明書を登録しデバイスを登録する手続きを自動化する、DPSサービスというものがある。

マイクロソフトのサイト、[Azure IoT Hub Device Provisioning Service を使用したデバイスのプロビジョニング](https://docs.microsoft.com/ja-jp/azure/iot-dps/about-iot-dps)を整理すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/08cc4fca-42ff-d8a8-38e3-af79d8bf1a2f.png)

今回はDPSサービスを使ってATECC608Aセキュアエレメント内に保存されたデバイス証明書を自動で登録してみたい。

今回もコードからポーティングのポイントを整理します。

#Azure DPSにつなぐためのポーティング#
ESP32開発環境ESP-IDFにはAzure接続用のライブラリがある。
[espressif/esp-azure](https://github.com/espressif/esp-azure)が該当するが、その内部では[GitHub-azure-iot-sdk-c](https://github.com/Azure/azure-iot-sdk-c)をサブモジュールとして持っている。

上記ソースコード内、lib配下へesp-azureを配置する。
大きなサイズのライブラリ群が配置され、その中の
[How to Port the Azure IoT C SDK to Other Platforms](https://github.com/Azure/azure-c-shared-utility/blob/master/devdoc/porting_guide.md)を参照する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/2c6f1c63-a724-8422-78b4-5d3463e0df54.png)
上記図から想定される、ポーティングのポイントは、
最低、platform,Tickcounter,Agenttime,TLSIO,Sleepを実装しなくてはならないということになる。

■主なポーティングポイント
esp-azureを確認すると、[port/src](https://github.com/espressif/esp-azure/tree/master/port/src)内に、
agenttime_esp.c
platform_esp.c
tlsio_esp_tls.c
上記をベースにカスタマイズができそう。
まず、agenttime_esp.c、platform_esp.cに関しては、時間同期のsntp.hのインクルードパスの調整が必要。
tlsio_esp_tls.cはESP-IDF内のOpensslの簡易ライブラリを使うものらしく、ここをMbedTLSを使うように置き換える必要がある。

[azure-c-shared-utility/adapters/](https://github.com/Azure/azure-c-shared-utility/tree/f0642196af85aeb4f2717d9cc11176290f321fb8/adapters)にポーティングの参考になる各プラットフォーム向けのコードがある。
この中のMbedOSプラットフォーム向けのtlsio_mbedtls.cを持ってきてtlsio_esp_tls.cと置き換える。

ThreadAPI_Sleep(HANDSHAKE_WAIT_INTERVAL_MS);という関数はESP-IDFにないので、
vTaskDelay(HANDSHAKE_WAIT_INTERVAL_MS/portTICK_PERIOD_MS);に置き換えてFreeRTOSをインクルードしておく。

上記図のSocketIOというものがないので作る必要がある。
[azure-c-shared-utility/adapters/](https://github.com/Azure/azure-c-shared-utility/tree/f0642196af85aeb4f2717d9cc11176290f321fb8/adapters)からsocketio_berkley.cを持ってきて、UNIXドメインソケット関連のコードを消し、ESP32が使うTCPスタックのlwipをインクルードして作成した。

上記のように多段構造で、ハンドルが多用されているので、わかりにくい。
Prov_Device_LL_DoWork　→　MQTT Dowork →　on_underlying_io_bytes_received　→　on_io_recv →　xio_dowork →　tlsio_dowork →　Socketio_dowork
処理の流れは上記のようになるようだ。

■成功した際の結果

DPSへの接続がうまくいくと、認証が行われ、今度は接続先をAzure IoT Hubへ変えて再度TLS1.2ハンドシェイクが始まる。そこから通信を数度行い疎通が取れるとデバイスが登録される流れのようだ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/5e6d39b5-df65-1d62-b162-0a61eb8c0f71.png)

シリアルコンソールでは以下の通りとなり、通信に成功したAZure IoT Hub URLと
デバイスID（ATECC608Aのシリアル番号）をATECC608Aの8番スロットへ書き込んでいる。

```
ssl_tls.c:2674: ssl->f_send() returned 0 (-0x0000)
ssl_tls.c:2674: ssl->f_send() returned 69 (-0xffffffbb)
ssl_tls.c:2701: <= flush output
ssl_tls.c:3100: <= write record
ssl_tls.c:7577: <= write
ssl_tls.c:7761: => free
ssl_tls.c:7826: <= free
Registering testcorp.azure-devices.net ...
Azure IoT URL provisioning complete.
device sn provisioning complete.
```

上記により、この後のAzure IoT Hub接続の際に、WifiのSSID,password以外のデバイスへ設定が必要な情報をすべてデバイス内へ格納することができた。