mbedTLSの概要とコンフィギュレーション

下記の記事にて、SSL/TLSに関してまとめ、NUCLEOF429Ziを使用した場合のmbedTLSのハンズオンとしての記事も作成しました。

- [暗号方式とSSL/TLS](https://qiita.com/tom_S/items/2a994f353ef5e15554d1 )
- [mbed-TLSのNUCLEOF429Ziでのハンズオン](https://qiita.com/tom_S/items/ffcbccd9393d3e7e156f )

ただ、mbedTLSの設定方法に関してはあまり触れていなかったので、本記事でこのあたりを説明しようと思います。
長くなってしまったので、初期化時のソースコードと通信する際のソースコードに関しては別の記事にて書く予定です。

# mbedTLSとは

mbedTLSとは、組み込み開発で使用できるように作られた軽量型のSSL/TLSライブラリです。ソフトウェアの構成として図を示すと、mbedTLSは下記の場所に位置します。

![ソフトウェア構成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/0a268a2b-ea81-43bf-081e-30f4504204c2.png)


mbedTLSは、EthernetなどのデバイスドライバとTCPIPのプロトコルスタックによる通信ができることが前提となっております。使用するハードウェアに暗号プロセッサや乱数発生器等が搭載されている場合は、TCPIPのプロトコルスタックを介さずにこれらのデバイスドライバにアクセスします。
また、TCPIPのプロトコルスタックは[LwIP(Lightweight IP)](https://savannah.nongnu.org/projects/lwip/ )やuIPといったオープンソースが使われる事が多いようです。

## ライセンス

[公式サイト](https://mbed-tls.readthedocs.io/en/latest/kb/licensing/how-is-mbedtls-protected/ )で「 Apache 2.0 OR GPL 2.0-or-later license」と記されており、デュアルライセンスとなっています。つまり、Apache 2.0とするかGPL2.0以降とするかをユーザーが選択可能になっています。

## 入手方法

[GitHubのリポジトリ](https://github.com/Mbed-TLS/mbedtls )があるので、ここから入手します。
また、マイコンメーカによっては評価ボードのサンプル用にポーティングされたものがあったりするので、使えそうなサンプルがないかを探してみるのもいいかもしれません。

# mbedTLSのコンフィギュレーション

mbedTLSにはコンフィギュレーション用のヘッダファイルや、他のミドルウェアとやり取りするためのラッパーファイルが用意されているので、これらを修正して使用します。

## コンフィギュレーション用のヘッダファイルの作成

mbedTLS自体の設定を行うコンフィギュレーションファイルを設定します。
mbedTLSのver3.0以降は「mbedtld_config.h」、ver3.0より前のバージョンの場合は「config.h」というファイルが用意されています。リポジトリの下記の場所にありますので、これを編集していきます。

- [mbedtld_config.h](https://github.com/Mbed-TLS/mbedtls/blob/v3.6.0/include/mbedtls/mbedtls_config.h )
- [config.h](https://github.com/Mbed-TLS/mbedtls/blob/v2.28.8/include/mbedtls/config.h )

上記のリンクは、例としてver3.6.0とver2.28.8のものを示しました。必要に応じて使用するバージョンのものを参考にしてください。

## 乱数発生器の使用設定

乱数発生器による乱数取得を行う場合は、「mbedtld_config.h」もしくは「config.h」に下記の定義をするようにしてください。(デフォルトではコメント化されているようなので定義するよう修正してください)

```
#define MBEDTLS_ENTROPY_HARDWARE_ALT
```

このマクロを定義することにより、乱数生成の際にはユーザーが定義した「mbedtls_hardware_poll()」を呼び出すようになります。この関数の仕様は下記のようになります。関数の内部で乱数生成を行い、引数の「output」に格納するように実装してください。

```C
int mbedtls_hardware_poll( void *data, unsigned char *output, size_t len, size_t *olen );
```

|  項目1  |    項目2    | 説明 |
| ------------- |  -------------------- | -------------------- |
| 機能 |              | 乱数生成を行う際に呼ばれる関数 |
| 引数 | data  |  |
|     | output      | 生成した乱数を格納する配列のポインタ |
|     | len | outputの指す配列に格納したい要素数 |
|     | *olen | 実際にoutputに格納した配列の要素数 |
| 返り値 |    | 0が正常値。それ以外はエラーとして判断 |

また、この関数では生成した乱数をunsigned char型の配列に格納するため、「*olen」に設定する値もunsigned char型の配列に格納した数を設定する必要があります。


## その他のコンフィグファイルの設定

「mbedtld_config.h」もしくは「config.h」で設定するマクロの一部をここに示します。

| マクロ名  |  値     | 説明 |
| ------------- |  -------------------- | -------------------- |
| MBEDTLS_HAVE_TIME_DATE | なし  | time.hの日付取得関数を使用する際に定義 |
| MBEDTLS_HAVE_TIME | なし  | time.hの時刻取得関数を使用する際に定義 |
| MBEDTLS_CIPHER_MODE～ | なし  | 「～」に入る暗号方式を使用する際に定義 |
| MBEDTLS_CIPHER_PADDING_～ | なし  | 「～」に入る暗号方式で\nパディングモードを使用する際に定義 |
| MBEDTLS_REMOVE_～ | なし  | 「～」に入る暗号方式を無効化する際に定義 |
| MBEDTLS_ECP_～_ENABLED | なし  | 「～」に入る楕円曲線暗号(ECC)のモジュールを有効化する際に定義。公開鍵暗号で使用 |
| MBEDTLS_ECDSA_DETERMINISTIC | なし  | RFC 6979にて示す、秘密鍵とメッセージから乱数生成し、デジタル署名を検証するECDSA(決定論的署名)を有効化する際に定義 |
| MBEDTLS_KEY_EXCHANGE_～_ENABLED | なし  | 「～」に入る鍵交換時の暗号方式を有効化する際に定義 |
| MBEDTLS_ERROR_STRERROR_DUMMY  | なし  |  ダミーのエラー関数を有効化する際に定義 |
| MBEDTLS_GENPRIME | なし  | 素数生成コードを有効化する際に定義。MBEDTLS_BIGNUM_Cも定義して使用する  |
| MBEDTLS_FS_IO  | なし  | ファイルシステムを有効化する際に定義 |
| MBEDTLS_PK_RSA_ALT_SUPPORT  | なし  |  HSMなどの外部セキュリティモジュールから生成したRSAの鍵を有効化する際に定義 |
| MBEDTLS_PKCS1_～ | なし  | 「～」に入るバージョン番号のPKCS(公開鍵暗号標準)を使用する際に定義 |
| MBEDTLS_SSL_ALL_ALERT_MESSAGES | なし  | エラー発生時にRFCに則ったエラーメッセージ送信をするに定義。無効化することで攻撃者に情報を与えないため、定義したほうが絶対に良いわけではない |
| MBEDTLS_SSL_ENCRYPT_THEN_MAC | なし  | RFC7366のEncrypt-then-MACのサポートを有効にする際に定義。Encrypt-then-MACは共通鍵暗号とメッセージ認証コードを組み合わせる際の方式を示す |
| MBEDTLS_SSL_EXTENDED_MASTER_SECRET | なし  | RFC7627に示されているハンドシェイク中の攻撃に対応するための処理を行う際に定義 |
| MBEDTLS_SSL_FALLBACK_SCSV | なし  | SSLへのダウングレード攻撃(POODLE)への対応を行う際に定義 |
| MBEDTLS_SSL_RENEGOTIATION | なし  | TLS際ネゴシエーションに対応する際に定義。鍵のリフレッシュ時に使用される |
| MBEDTLS_SSL_MAX_FRAGMENT_LENGTH | なし  | RFC6066の最大フラグメントサイズ設定に対応する際に定義 |
| MBEDTLS_SSL_PROTO_TLS～ | なし  | 「～」に入るバージョンのTLSに対応する際に定義 |
| MBEDTLS_SSL_PROTO_DTLS | なし  | DTLSに対応する際に定義 |
| MBEDTLS_SSL_ALPN | なし  | RFC7301で示されている、ハンドシェイクにアプリケーション層のやり取りを追加する際に定義 |
| MBEDTLS_SSL_DTLS_～ | なし  | 「～」に示す機能をDTLSでサポートする際に定義 |
| MBEDTLS_SSL_SESSION_TICKETS | なし  | RFC5077に示すセッションチケット機能を使用する際に定義 |
| MBEDTLS_SSL_SERVER_NAME_INDICATION | なし  | RFC6066に示す、一つのIPアドレスに複数のドメイン名の指定を可能にする際に定義 |
| MBEDTLS_VERSION_FEATURES | なし  | コンパイル時に有効化した機能を確認できるようにする際に定義 |
| MBEDTLS_X509_RSASSA_PSS_SUPPORT | なし  | X.509の証明書の検証と解析機能を使用する際に定義 |
| MBEDTLS_～_C | なし  | 「～」に入る機能をモジュール全体としてサポートする際に定義。使用しないモジュールをコメント化することによってファームウェアサイズやメモリの使用量を減らす |
| MBEDTLS_NO_UDBL_DIVISION | なし  | 整数の除算結果の正確性を上げる機能を使用する際に定義。有効化することで除算に使うメモリが増えるので注意 |
| MBEDTLS_AES_ROM_TABLES | なし  | 事前にROMに保存されたAES用のテーブルを使用する際に定義 |
| MBEDTLS_NO_PLATFORM_ENTROPY | なし  | 乱数生成時にデフォルトで用意されているモジュールを呼び出さない際に定義。ハードの乱数生成器を使用する際は定義した状態とする |
| MBEDTLS_ECP_MAX_BITS | ~512  | デフォルトでは521ビットの楕円曲線をサポートするため、512が設定されている。この値よりも小さくしてもよいが、メモリ使用量に与える影響は少ない |
| MBEDTLS_MPI_MAX_SIZE  | ~1024  | 整数を表現するのに使用する最大バイト数。デフォルトは1024だが、必要に応じて小さくしても問題ない |
| MBEDTLS_ECP_WINDOW_SIZE  | 2~6  | 楕円曲線の計算で使用するテーブルサイズ。増やすことでメモリ使用量も上がる |

割愛しましたが、「mbedtld_config.h」もしくは「config.h」には他にも設定可能な項目があるので、興味がありましたら下記を参照してください。

https://mbed-tls.readthedocs.io/projects/api/en/development/api/file/mbedtls__config_8h/ 

## TCPIPのプロトコルスタックとのコンフィギュレーション

mbedTLSは、BSDソケットAPIを前提としたネットワークの抽象レイヤがあります。
図に示すと下記のような構成となっています。

![mbedTLS構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/7532486e-22f4-b053-ea1e-c5fd6f7074e0.png)


暗号化・復号化したデータの受け渡し先のAPIの定義のみが決まっている状態です。使用するプロトコルスタックが違う可能性があるので、関数の実装はユーザーが行います。

ソースコードは「net_sockets.c」に定義します。

### 「net_sockets.c」に実装が必要な関数

ユーザーが「net_socket.c」に実装する必要があるのは下記の関数です。下記の関数を、使用するTCPIPのプロトコルスタックのAPI等を用いて実装する必要があります。

```C
void mbedtls_net_init(mbedtls_net_context *ctx)
```
|  項目1  |    項目2    | 説明 |
| ------------- |  -------------------- | -------------------- |
| 実装する機能 |              | TCP/IPのプロトコルスタックとソケット通信の初期化 |
| 引数 | ctx  | ソケットのファイルディスクリプタを保持しておくための構造体 |

```C
int mbedtls_net_connect(mbedtls_net_context *ctx, const char *host, const char *port, int proto)
```
|  項目1  |    項目2    | 説明 |
| ------------- |  -------------------- | -------------------- |
| 実装する機能 |              | 引数で指定したhostとportのソケットに接続する。クライアント用関数 |
| 引数 | ctx  | ソケットのファイルディスクリプタを保持している構造体 |
|  | host  | 接続相手のソケットのホスト名 |
|  | port | 接続相手のソケットのポート番号 |
|  | proto | 使用するプロトコル。MBEDTLS_NET_PROTO_TCPかMBEDTLS_NET_PROTO_UDPを入れる |
| 返り値 |  | 正常終了時は0。エラー発生時はMBEDTLS_ERR_NET_SOCKET_FAILED, MBEDTLS_ERR_NET_UNKNOWN_HOST, MBEDTLS_ERR_NET_CONNECT_FAILEDのどれかを返すようにする
 |

```C
int mbedtls_net_bind(mbedtls_net_context *ctx, const char *bind_ip, const char *port, int proto)
```
|  項目1  |    項目2    | 説明 |
| ------------- |  -------------------- | -------------------- |
| 実装する機能 |              | 引数で指定したIPアドレスとportをソケットに紐づける。TCPの通信をする場合はソケットをリッスン状態にしておく。サーバー用関数 |
| 引数 | ctx  | ソケットのファイルディスクリプタを保持している構造体 |
|  | bind_ip | ソケットに紐づけるIPアドレス |
|  | port | ソケットに紐づけるポート番号 |
|  | proto | 使用するプロトコル。MBEDTLS_NET_PROTO_TCPかMBEDTLS_NET_PROTO_UDPを入れる |
| 返り値 |  | 正常終了時は0。エラー発生時はMBEDTLS_ERR_NET_SOCKET_FAILED, MBEDTLS_ERR_NET_BIND_FAILED, MBEDTLS_ERR_NET_LISTEN_FAILEDのどれかを返すようにする
 |

```C
int mbedtls_net_accept(mbedtls_net_context *bind_ctx, mbedtls_net_context *client_ctx, void *client_ip, size_t buf_size, size_t *cip_len)
```
|  項目1  |    項目2    | 説明 |
| ------------- |  -------------------- | -------------------- |
| 実装する機能 |              | 接続要求を許可する。サーバー用関数 |
| 引数 | bind_ctx | 接続を受け付けるソケット情報。ユーザが指定 |
|  | client_ctx | 接続要求を送ってきたソケット情報。関数内で設定される |
|  | client_ip | 接続要求を送ってきたソケットのIPアドレスを格納する配列のポインタ |
|  | buf_size | client_ipが指すバッファのサイズ。ユーザが指定 |
|  | cip_len | client_ipに書き込まれているIPアドレスのデータサイズ。関数内で設定される |
| 返り値 |  | 正常終了時は0。エラー発生時MBEDTLS_ERR_NET_ACCEPT_FAILED, MBEDTLS_ERR_NET_BUFFER_TOO_SMALL, MBEDTLS_ERR_SSL_WANT_READのどれかを返すようにする
 |


```C
int mbedtls_net_set_block(mbedtls_net_context *ctx)
```

|  項目1  |    項目2    | 説明 |
| ------------- |  -------------------- | -------------------- |
| 実装する機能 |              | ソケットをブロッキングモードに設定する。(指定されたデータが送受信完了するまで、ライブラリの送受信関数から返ってこないようにする) |
| 引数 | ctx  | ソケットのファイルディスクリプタを保持している構造体 |
| 返り値 |  | 正常終了時は0。エラー発生時は0以外をかえすようにする |


```C
int mbedtls_net_set_nonblock(mbedtls_net_context *ctx)
```


|  項目1  |    項目2    | 説明 |
| ------------- |  -------------------- | -------------------- |
| 実装する機能 |              | ソケットをノンブロッキングモードに設定する。
(指定されたデータの送受信完了を待たずに、ライブラリの送受信関数が終了する) |
| 引数 | ctx  | ソケットのファイルディスクリプタを保持している構造体 |
| 返り値 |  | 正常終了時は0。エラー発生時は0以外をかえすようにする |


```C
void mbedtls_net_usleep(unsigned long usec)
```

|  項目1  |    項目2    | 説明 |
| ------------- |  -------------------- | -------------------- |
| 実装する機能 |              | usecで指定した時間(単位はms)、呼び出し先のスレッドをスリープする。 |
| 引数 | usec | 待つ時間。ms単位で指定 |

```C
int mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len)
```

|  項目1  |    項目2    | 説明 |
| ------------- |  -------------------- | -------------------- |
| 実装する機能 |              | データを受信する |
| 引数 | ctx  | ソケットのファイルディスクリプタを保持している構造体 |
|  | buf | 受信したデータを入れるバッファのポインタ |
|  | len | 受信できるデータのサイズ |
| 返り値 |  | ブロッキングモードでは受信したbyte数を返す。ノンブロッキングモードではMBEDTLS_ERR_SSL_WANT_READを正常値として返す。何らかのエラー時はどちらのモードでも負の値のエラーコードを返す。 |

```C
int mbedtls_net_send(void *ctx, const unsigned char *buf, size_t len)
```

|  項目1  |    項目2    | 説明 |
| ------------- |  -------------------- | -------------------- |
| 実装する機能 |              | データを送信する |
| 引数 | ctx  | ソケットのファイルディスクリプタを保持している構造体 |
|  | buf | 送信するデータを入れたバッファのポインタ |
|  | len | 送信データのサイズ |
| 返り値 |  | ブロッキングモードでは送信したbyte数を返す。ノンブロッキングモードでMBEDTLS_ERR_SSL_WANT_WRITEを正常値として返す。何らかのエラー時はどちらのモードでも負の値のエラーコードを返す。 |

```C
int mbedtls_net_recv_timeout(void *ctx, unsigned char *buf, size_t len, uint32_t timeout)
```

|  項目1  |    項目2    | 説明 |
| ------------- |  -------------------- | -------------------- |
| 実装する機能 |              | データを受信する。timeoutで指定した時間受信データがなければ受信街を終了してリターンする |
| 引数 | ctx  | ソケットのファイルディスクリプタを保持している構造体 |
|  | buf | 受信したデータを入れるバッファのポインタ |
|  | len | 受信できるデータのサイズ |
|  | timeout | タイムアウト時間(ms) |
| 返り値 |  | タイムアウト時はMBEDTLS_ERR_SSL_TIMEOUTを返す。主にブロッキングモードのソケットで使用 |

```C
void mbedtls_net_free(mbedtls_net_context *ctx)
```

|  項目1  |    項目2    | 説明 |
| ------------- |  -------------------- | -------------------- |
| 実装する機能 |              | 引数で指定したソケットを解放する |
| 引数 | ctx  | ソケットのファイルディスクリプタを保持している構造体 |

```C
void mbedtls_net_close(mbedtls_net_context *ctx)
```

|  項目1  |    項目2    | 説明 |
| ------------- |  -------------------- | -------------------- |
| 実装する機能 |              | 引数で指定したソケットをクローズする |
| 引数 | ctx  | ソケットのファイルディスクリプタを保持している構造体 |

```C
int mbedtls_net_poll(mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout)
```

|  項目1  |    項目2    | 説明 |
| ------------- |  -------------------- | -------------------- |
| 実装する機能 |              | 第一引数のctxが示すコンテクストに送受信が可能なソケットがあるかを調べる。BSDのソケットAPIのselect()に該当する機能の使用が推奨 |
| 引数 | ctx  | ソケットのファイルディスクリプタを保持している構造体 |
|  | rw | |
|  | timeout | タイムアウト時間(ms) |
| 返り値 |  | 正常終了時もしくはタイムアウト時は、読み出し可能ならMBEDTLS_NET_POLL_READを、書き込み可能ならMBEDTLS_NET_POLL_WRITEを返す。どちらも可能ならこの二つの論理和を返す。エラー発生時は負の値をかえすようにする |


また、net_socket.cの実装は、LwIPなどよく使われるオープンソースの場合は実装例があることも多いです。評価ボードのサンプルプロジェクトとしてSSL/TLSのサンプルがある場合はそれを参考にしながら実装してもいいかもしれません。

## ビルド

MakeやCMakeを使用してのビルド方法に関して下記の公式サイトで記されています。
https://mbed-tls.readthedocs.io/en/latest/getting_started/building/

このあたりの詳細は追って調査後に追記しようかと思います。


# 参考資料
https://mbed-tls.readthedocs.io/en/latest/kb/how-to/mbedtls-tutorial/#the-stack