STM32マイコンからサーバへ画像をセキュア転送する

# はじめに

普段からよく使っているSTM32マイコンを使って、
SSL/TLSライブラリを使った実験用ソースコードを製作した。
自宅に設置している家電操作用ラズパイサーバや、
そのサーバにデータ送信するクライアント端末は全て平文でデータ受け渡しをしている。

今回、wolfSSLライブラリを使った画像アップロードデモを行い、
自宅のドアフォンに移った訪問客を外出先から確認する開発に繋げたい

# 準備物
●NUCLEO-F446ZE × 1
●MM-FT232H × 1　※UART-USB変換可能なデバイスであれば何でもOK

上記２台をUART通信で接続します。接続関係は、
■ PD6（NUCLEO-F446ZE）↔ADBUS0（MM-FT232H）
■ PD5（NUCLEO-F446ZE）↔ADBUS1（MM-FT232H）
■ <font color="red">互いのGNDを接続</font> ←これを忘れると2日溶かします

筆者はArduino拡張基板を使って２台を接続しました。
Nucleo基板上のピンソケットは左右それぞれ２列ありますが、
上記端子PD5,6は左側ピンソケットの内側にあります。
拡張基板をそのまま内側に接続すると右側ピンソケットが<font color="red">１列はみ出す</font>ので、
はみ出た１列分を右側ピンソケット外側に無理やり曲げて装着します。

![IMG_3978.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519618/1e053b40-bc2e-470b-a9de-24af4bbdeae6.jpeg)


# ネットワーク構成図

概ね、ハードウェアのセッティングが完了しました。
ネットワーク構成はあくまで参考ですので、等価な接続構成になっていればOKです。

```plantuml
@startuml
!include <office/Servers/application_server>
!include <office/Servers/database_server>
!include <office/devices/switch>
nwdiag {


  network LAN {
      address = "192.168.128.x/24"
      PictureUploadServer [address = "192.168.128.191(static)", description = "<$database_server>\nPicture\nUpload\nServer"];
      OperatePC [address = "192.168.128.140(static)", description = "<&person*4.5>\n操作用PC\n(Windows)"];
  }

    network ttyUSB0 {
      PictureUploadServer [description = "<$database_server>\nPicture\nUpload\nServer"]
      NUCLEO-F446ZE [address = "MM-FT232H(USART2)"description="<$application_server>\nNUCLEO-F446ZE\n+MM-FT232H"];
  }

  network USB_Serial{
  OperatePC;
  NUCLEO-F446ZE [address = "ST-LINK(USART3)"];
  }
  
}
@enduml
```
# ソースコード

以下にサンプルプログラムから機能追加／修正内容をまとめた。

●(server-tls-uart.c)：
    ・【追加】base64エンコード／デコード関数を追加
    ・【追加】base64デコードした画像データを*.jpgファイルにする機能を追加
    ・【修正】examplesのファイル階層変更により、秘密鍵とサーバ証明書の相対パスが変わっていたので修正
    ・【修正】クライアントから受け取るデータの受信データバッファサイズを拡張
    ・【修正】ファイル名を「server-tls-uart.c」から「server-tls-uart-picture.c」に変更

●(wolfssl_example_ex.c)
    ・【追加】デモメニュー画面でのコマンド受付時間を0.1sから1sに変更
    ・【追加】実行するデモ機能を'l','m','s'に拡張し、ヘッダファイルに置いた画像データを選択

●(main.c)
    ・【追加】既存のデモ関数（wolfCryptDemo）と新規デモ関数（wolfCryptDemo_ex）をマクロ切替え

●(base64_encoder.bat)
    ・【追加】ドラッグ＆ドロップした画像ファイルをbase64エンコードして、ヘッダファイルを出力

●(default_conf.ftl)　※wolfSSLから提供されているST用ファームウェアパッケージ
    ・【追加】Hardware platformとしてSTM32F446xxを追加
    →STM32CubeMXからコード生成すると、自動で上書きする

なお、base64関数は以下サイトを引用した。
https://qiita.com/leak4mk0/items/6c7f708dd59d52e0bc5c

ベースとしたサンプルプログラム：
https://github.com/wolfSSL/wolfssl-examples/tree/master/pq/tls/stm32

## 画像ファイルをBase64エンコード
手始めにクライアントからサーバに送信する画像ファイルをBase64変換し、
マイコンが読み取れるようにヘッダファイルに格納します。
今回は自作のBase64変換バッチファイル（base64_encoder.bat）を使って、
Base64変換します。
画像ファイルをドラッグ＆ドロップすると、証明書形式でヘッダファイルが新規作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519618/4d5fe0ed-6548-44f5-b0cb-65da399eedf0.png)

このヘッダファイルに以下の修正を手作業で実施（本当はここもバッチファイルに含めたい）
１．文字列「-----BEGIN CERTIFICATE-----」を「const char picture_encode_data_large[]="」に置き換える
２．全ての改行を削除
３．行末に";を追加する

これをlarge,middle,smallの三種類で実施することで、３つの画像ファイルに対するBase64変換ヘッダファイルが完成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519618/6e761bf6-6c44-496a-8413-45f2707eafd8.png)

どんな画像かは後のお楽しみ・・・

### wolfSSLライブラリのビルド
wolfsslには大きく２つのgithubリポジトリがあります。
● https://github.com/wolfssl/wolfssl：wolfssl本体
● https://github.com/wolfSSL/wolfssl-examples：サンプルプログラムのためのソースコード

２つ目のソースコードは１つ目のソースコードを用いるため、
まずは１つ目のソースコードをビルドする必要があります。

gitクローンからビルドまでの流れ
```
git clone https://github.com/wolfSSL/wolfssl-examples.git
cd wolfssl
./autogen.sh
./configure --enable-kyber --enable-dilithium
make all
　← 本来はここでビルドしたライブラリをテストするコマンドを打つ
sudo make install ← sudo入れないとpermmission denyされる
```

# 動作確認

## サーバ側ターミナル画面

操作手順は、
１：まず先にサーバ側で、修正・機能追加したアプリケーションを実行
２：その後クライアント側で、デモコマンドを入力

その結果が以下である。

**※TCP/IP通信を行っていないので、Wireshark上でTLS1.3による通信が確認できなかった**

```ruby
[UserName]@[UserName]-CF-Y7BWHNJR:~/wolfssl-examples/pq/tls/stm32$ sudo ./server-tls-uart-picture
Waiting for client
TLS Accept handshake done
Picture Data Size: 9775 [byte] ← largeサイズの画像データを取得

[UserName]@[UserName]-CF-Y7BWHNJR:~/wolfssl-examples/pq/tls/stm32$ sudo ./server-tls-uart-picture
Waiting for client
TLS Accept handshake done
Picture Data Size: 6354 [byte] ← middleサイズの画像データを取得

[UserName]@[UserName]-CF-Y7BWHNJR:~/wolfssl-examples/pq/tls/stm32$ sudo ./server-tls-uart-picture
Waiting for client
TLS Accept handshake done
Picture Data Size: 3733 [byte] ← smallサイズの画像データを取得
```

## クライアント側ターミナル画面



```
                                MENU

        l. Run TLS 1.3 Transport Large picture over UART
        m. Run TLS 1.3 Transport Middle picture over UART
        s. Run TLS 1.3 Transport Small picture over UART
Please select one of the above options: ← l+[Enter]
Large Picture Upload Server...
TLS Connect handshake done
Sending test string
Picture data sent complete!
TLS 1.3 Client: Return code 0

                                MENU

        l. Run TLS 1.3 Transport Large picture over UART
        m. Run TLS 1.3 Transport Middle picture over UART
        s. Run TLS 1.3 Transport Small picture over UART
Please select one of the above options: ← m+[Enter]
Middle Picture Upload Server...
TLS Connect handshake done
Sending test string
Picture data sent complete!
TLS 1.3 Client: Return code 0

                                MENU

        l. Run TLS 1.3 Transport Large picture over UART
        m. Run TLS 1.3 Transport Middle picture over UART
        s. Run TLS 1.3 Transport Small picture over UART
Please select one of the above options: ← s+[Enter]
Small Picture Upload Server...
TLS Connect handshake done
Sending test string
Picture data sent complete!
TLS 1.3 Client: Return code 0
```

## 受信データの確認

サーバ側ターミナル画面にて、ディレクトリstm32内のファイル一覧を出力します。
きちんとsmallサイズからlargeサイズまで画像ファイルが作成されていることが確認できます。
※recv_data_XXXX.jpgのXXXは、画像ファイルのサイズ[byte]を表す
```ruby
[UserName]@[UserName]-CF-Y7BWHNJR:~/wolfssl-examples/pq/tls/stm32$ ls
recv_data_3733.jpg
recv_data_6354.jpg
recv_data_9775.jpg
---その他省略---
```

Linuxターミナル上ではjpgファイルを描画することはできないので、
scpコマンドを使ってWindows端末に送信する
※参照：https://qiita.com/WaatH5/items/ef976530976058aa2cb2

受信した画像データを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519618/9d5cd495-03a0-43b6-be45-45662a009acc.png)

# 今後の課題

下記に5点の課題を書く

## EthernetコネクタやWi-Fiモジュールを追加すべき
TCP/IP通信のテストとしてlocalhostを使うことは、WireSharkを使ったデバッグをすることができるので有益である。しかし、IoT端末のほとんどはゲートウェイを介した異なるネットワークへTCP/IP通信をする。
それを踏まえると、サンプルソフトウェアとして実践度合いが低いと感じてしまう

## 外部メモリに保存した秘密鍵が盗まれる
本ページ作品ではRAM上の鍵、証明書を使っているため該当しないが、
外部フラッシュや外部EEPROMに秘密鍵、証明書を格納してしまうと、
ハードウェアハッキングによってコピーデバイスを作成される恐れがある。
Winbond製フラッシュメモリではその外部フラッシュとマイコン間のI/F自体も暗号化する仕組みがあるようだ。

## セキュア転送可能な画像ファイルサイズを拡大する
本ページ作品で取り扱った画像ファイルは予めサイズが小さくなるように加工をした。
組込みシステムでは使用RAMやバッファサイズ上限が予め決められているため、
下手に大きいサイズの画像ファイルをセキュア転送しようとすると、末尾が切れらてしまう。

## TLS通信開始しようとすると、「sslレイヤーレコード長が不正」「通信バージョンが異なる」といったエラーが発生した
本ページに掲載している「サーバ側ターミナル画面」や「クライアント側ターミナル画面」の結果は、
全て連続してスムーズに転送完了したわけではない。
５回に１回ほど、上記のようなエラーが発生してしまう。
しかし、単純な10文字程度の文字列通信では１度も発生していないことから、
● データサイズが肥大化したことによる処理溢れ
● UART2通信のボーレート（115200）が遅い
と推測できる。

## UART通信による暗号化通信なため、Wiresharkから観測不可
Wiresharkのusbmon*を選択することでUARTの通信内容が確認できると考えたが、
どうしても権限エラーが解消できなかった。
たとえusbmon*のキャプチャが開始できても、
通信内容からプロトコルがTLS1.3であることを自動で認識してくれるとは思えない。
つまり、Wiresharkでパケットを確認するためには、
UART通信内容を一旦TCP/IPに変換する必要がある。

# まとめ

STM32マイコンからサーバへ画像をセキュア転送することができた。
また、wolfSSLライブラリのwrite関数、read関数は平文でデータ受け渡しすることができると確認できた。

# 感想

●wolfSSLのSSL/TLS通信ライブラリは、それ自体が暗号化／復号処理してくれるので、
平文のままwrite関数を使って送信、read関数を使って平文を取得することができる。
<font color="red">つまり、送信したいchar型の文字列と送信先IPさえ決まっていれば簡単にスタートできる。</font>

●筆者は今回２週間で本ページ作品を仕上げることができた。
元来、組込みシステムにおけるネットワーク通信（TCP/IP）に関する知識はある程度あるが、
暗号化処理の細かいアルゴリズムまでは知らなかった。
本作品の画像アップロードプログラムを用いて暗号化処理の詳しいソースコードを見てみる興味が湧いた。

●普段からWi-Fiモジュール単体またはATコマンドを使ったマイコンとWi-Fiモジュールとの通信を使って、
SmartHEMSや外出先からエアコンなどを操作する家電操作サーバを自作しているが、
無償版wolfSSLライブラリを使うことで、
今まで平文で送っていたHTMLファイルや室温情報を暗号化する術を身につけることができた。
STマイコン用のファームウェアパッケージも提供しているので、
ラズパイのみならず、STマイコンのAD変換によって得られた情報も
バンバン暗号化通信可能になる。

そして、暗号化通信でしか通信を受け付けていなかった
AWSなどの外部サービスサーバとの通信も可能になったので、
こちらもバンバン便利なIoT機器を自作していきたい。

# 参考

↓ 今回使用したサンプルプログラムの解説＆動作確認をしている
https://www.youtube.com/watch?v=OK6MKXYiVBY&t=925s

↓ wolfSSLライブラリの使い方やTLS接続、暗号化に関する解説書
https://wolfssl.jp/wolfblog/2022/03/07/tlsv13book/

https://qiita.com/kj1/items/ae434073567a6a08d972

https://qiita.com/kj1/items/3140b107fd7f0ccdf061

https://qiita.com/MHKS/items/e441dc3e9b7bcf32ea75