こちらの記事の続きになります。
上記のリンク先の記事ではmbedTLSの概要と設定に関して説明したので、
本記事では初期化、ハンドシェイク、通信におけるコーディングと呼び出すAPIの説明をいたします。
初期化のソースコード
mbedTLSの初期化時に呼び出すAPIを下記にまとめました。
void mbedTLSInit(void)
{
int len;
mbedtls_net_context server_fd;
const uint8_t *pers = (uint8_t *)("ssl_client");
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_x509_crt cacert;
/* ここでLwIP初期化。DHCPのIPアドレス取得まで行う */
mbedtls_ssl_init(&ssl);
mbedtls_ssl_config_init(&conf);
mbedtls_x509_crt_init(&cacert);
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_entropy_init(&entropy);
len = strlen((char *)pers);
if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *) pers, len) != 0)
{
return; /* エラー */
}
/* 証明書の初期化(自身の鍵・証明書設定) */
/* mbedtls_test_cas_pem、mbedtls_test_cas_pem_lenはcert.hにて定義されている */
if (mbedtls_x509_crt_parse(&cacert, (const unsigned char *) mbedtls_test_cas_pem, mbedtls_test_cas_pem_len) < 0)
{
return; /* エラー */
}
}
使用しているAPIを順に説明していきます。
void mbedtls_ssl_init( mbedtls_ssl_context *ssl )
項目1 |
項目2 |
説明 |
機能 |
|
引数で指定した構造体をmemsetにより全てのパラメータを0で初期化 |
引数 |
ssl |
mbedTLS管理用構造体。実体はユーザ側で持つ |
void mbedtls_ssl_config_init( mbedtls_ssl_config *conf )
項目1 |
項目2 |
説明 |
機能 |
|
引数で指定した構造体をmemsetにより全てのパラメータを0で初期化 |
引数 |
conf |
mbedTLS設定用構造体。実体はユーザ側で持つ |
void mbedtls_x509_crt_init( mbedtls_x509_crt *crt )
項目1 |
項目2 |
説明 |
機能 |
|
引数で指定した構造体をmemsetにより全てのパラメータを0で初期化 |
引数 |
crt |
mbedTLSの証明書管理用構造体。実体はユーザ側で持つ |
void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx )
項目1 |
項目2 |
説明 |
機能 |
|
引数で指定した構造体をmemsetにより全てのパラメータを0で初期化 |
引数 |
ctx |
mbedTLSのCTR_DRBG(AESとカウンタによる擬似乱数生成用モジュール)管理用の構造体。実体はユーザ側で持つ |
void mbedtls_entropy_init( mbedtls_entropy_context *ctx )
項目1 |
項目2 |
説明 |
機能 |
|
引数で指定した構造体をmemsetで初期化。乱数生成時に呼ばれるコールバックの設定 |
引数 |
ctx |
mbedTLSのエントロピー(乱数の予測不可能性を担保するもの)管理用の構造体。実体はユーザ側で持つ |
int mbedtls_ctr_drbg_seed( mbedtls_ctr_drbg_context *ctx,
int (*f_entropy)(void *, unsigned char *, size_t),
void *p_entropy,
const unsigned char *custom,
size_t len )
項目1 |
項目2 |
説明 |
機能 |
|
シードの初期化とエントロピー(乱数)取得用関数の登録。再シード取得用のパラメータ設定 |
引数 |
ctx |
mbedTLSのCTR_DRBG(AESとカウンタによる擬似乱数生成用モジュール)管理用の構造体。実体はユーザ側で持つ |
|
f_entropy |
ctxのメンバに登録する、エントロピー(乱数)取得用のコールバック |
|
p_entropy |
f_entropyを呼び出す際に引数として指定するポインタ |
|
custom |
シードの値を格納した配列。シードの再設定時に使われる |
|
len |
customの示す先の配列サイズ |
返り値 |
|
正常値は0。それ以外の値はエラー |
int mbedtls_x509_crt_parse(mbedtls_x509_crt* chain,
const unsigned char* buf,
size_t buflen)
項目1 |
項目2 |
説明 |
機能 |
|
証明書をPEM形式かDER形式にパースして取得する。 |
引数 |
chain |
パースする証明書 |
|
buf |
パースして取得した、PEM形式か、DER形式の証明書 |
|
buflen |
bufの示す先の配列サイズ |
返り値 |
|
正常値は0。それ以外の値はエラー |
ハンドシェイク
SSL/TLSのハンドシェイクに関するソースコードについて解説します。初期化にて設定した「mbedtls_ssl_config」、「mbedtls_ctr_drbg_context」、「mbedtls_ssl_context」、「mbedtls_x509_crt」を使用するので、これらの変数は静的な変数と定義する、グローバルな変数として保持しておくなどの対応が必要です。
void mbedTLS_handShake(mbedtls_ssl_config conf, mbedtls_ctr_drbg_context ctr_drbg, mbedtls_ssl_context ssl,
mbedtls_x509_crt cacert)
{
mbedtls_net_context server_fd;
/* 通信相手と接続(暗号通信はしない)。net_socket.cでユーザーが実装する関数 */
if (mbedtls_net_connect(&server_fd, SERVER_NAME, SERVER_PORT, MBEDTLS_NET_PROTO_TCP) != 0)
{
return; /* エラー */
}
/* クライアントとしてmbedTLSを設定 */
if (mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0)
{
return; /* エラー */
}
/* 通信モードをconfに設定。MBEDTLS_SSL_VERIFY_OPTIONALなので、エラー内容を「mbedtls_ssl_get_verify_result」で確認する必要がある */
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
/* mbedtls_x509_crt_parseで設定した証明書・鍵をconfに設定 */
mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
/* 乱数生成にCTR-DRBG用の関数「mbedtls_ctr_drbg_random」を呼び出すようにconfに設定 */
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
/* confに設定したパラメータを反映 */
if (mbedtls_ssl_setup(&ssl, &conf) != 0)
{
return; /* エラー */
}
/* HOST名指定 */
if (mbedtls_ssl_set_hostname(&ssl, "localhost") != 0)
{
return; /* エラー */
}
/* データの送受信に使う関数を「mbedtls_net_send」「mbedtls_net_recv」にて設定。server_fdは送受信関数呼び出し時に引数として指定される */
mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
/* TLSの4WAYハンドシェイクを実施 */
while ((ret = mbedtls_ssl_handshake(&ssl)) != 0)
{
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
{
return; /* エラー */
}
}
/* 4WAYハンドシェイクの結果を確認 */
if (mbedtls_ssl_get_verify_result( &ssl ) != 0)
{
return; /* エラー */
}
}
使用しているAPIを順に説明していきます。
int mbedtls_ssl_config_defaults(mbedtls_ssl_config* conf, int endpoint, int transport,
int preset)
項目1 |
項目2 |
説明 |
機能 |
|
デフォルトのmbedTLSの設定を反映する |
引数 |
conf |
デフォルトの設定を格納する配列のポインタ |
|
endpoint |
クライアントを示す値「MBEDTLS_SSL_IS_CLIENT」かサーバを示す値 「MBEDTLS_SSL_IS_SERVER」を設定する |
|
transport |
TLS通信を示す値「MBEDTLS_SSL_TRANSPORT_STREAM」か、DTLS通信を示す値「MBEDTLS_SSL_TRANSPORT_DATAGRAM」を設定する |
|
preset |
現状使用されていない。「MBEDTLS_SSL_PRESET_DEFAULT」を指定すれば問題ない |
返り値 |
|
正常値は0。それ以外はエラー |
void mbedtls_ssl_conf_authmode(mbedtls_ssl_config* conf, int authmode)
項目1 |
項目2 |
説明 |
機能 |
|
証明書の認証方法を設定 |
引数 |
conf |
コンフィギュレーションを格納する配列のポインタ。既に設定されているものを渡す |
|
authmode |
MBEDTLS_SSL_VERIFY_OPTIONAL、MBEDTLS_SSL_VERIFY_REQUIREDのどちらかを設定する。MBEDTLS_SSL_VERIFY_OPTIONALの場合は認証処理のエラー値を「mbedtls_ssl_get_verify_result」によって取得する。 |
void mbedtls_ssl_conf_ca_chain(mbedtls_ssl_config *conf, mbedtls_x509_crt *ca_chain, mbedtls_x509_crl *ca_crl)
項目1 |
項目2 |
説明 |
機能 |
|
証明書を設定 |
引数 |
conf |
コンフィギュレーションを格納する配列のポインタ。関数内で証明書の設定をこの配列に設定される |
|
ca_chain |
証明書チェーン(ルート証明書から、サーバ証明書、クライアント証明書の連なり) |
|
ca_crt |
証明書失効リスト。指定する必要がなければNULLを入れる |
void mbedtls_ssl_conf_rng(mbedtls_ssl_config *conf, int (*f_rng)(void*, unsigned char*, size_t), void *p_rng)
項目1 |
項目2 |
説明 |
機能 |
|
乱数生成時に呼ばれる関数を設定する |
引数 |
conf |
コンフィギュレーションを格納する配列のポインタ。f_rng, p_rngを格納する |
|
f_rng |
乱数生成時に呼ばれる関数ポインタ |
|
p_rng |
f_rngが呼び出される際に引数として指定されるポインタ |
int mbedtls_ssl_setup(mbedtls_ssl_context *ssl, const mbedtls_ssl_config *conf)
項目1 |
項目2 |
説明 |
機能 |
|
コンテキスト(第一引数の「ssl」)にコンフィギュレーション情報を設定する |
引数 |
ssl |
設定を反映させるmbedTLSのコンテキスト情報 |
|
conf |
コンフィギュレーションを格納する配列のポインタ |
返り値 |
|
正常値は0。メモリ取得エラーが起きた際はMBEDTLS_ERR_SSL_ALLOC_FAILEDが返る |
int mbedtls_ssl_set_hostname(mbedtls_ssl_context *ssl, const char *hostname)
項目1 |
項目2 |
説明 |
機能 |
|
コンテキスト(第一引数の「ssl」)にhost名を設定する |
引数 |
ssl |
host名を設定するmbedTLSのコンテキスト情報 |
|
hostname |
host名を示す文字列 |
返り値 |
|
正常値は0。メモリ取得エラーが起きた際はMBEDTLS_ERR_SSL_ALLOC_FAILEDが返る |
void mbedtls_ssl_set_bio(mbedtls_ssl_context *ssl,
void *p_bio,
mbedtls_ssl_send_t *f_send,
mbedtls_ssl_recv_t *f_recv,
mbedtls_ssl_recv_timeout_t *f_recv_timeout)
項目1 |
項目2 |
説明 |
機能 |
|
コンテキスト(第一引数の「ssl」)にhost名を設定する |
引数 |
ssl |
mbedTLSのコンテキスト情報 |
|
p_bio |
f_send, f_recv, f_recv_timeout を呼び出す際に引数で指定するポインタ |
|
f_send |
データ送信時に呼び出される関数 |
|
f_recv |
データ受信時に呼び出される関数(タイムアウト無し) |
|
f_recv_timeout |
データ受信時に呼び出される関数(タイムアウトあり) |
int mbedtls_ssl_handshake(mbedtls_ssl_context *ssl)
項目1 |
項目2 |
説明 |
機能 |
|
TLSのハンドシェイクを実施する |
引数 |
ssl |
設定を反映させるmbedTLSのコンテキスト情報 |
返り値 |
|
正常値は0。それ以外の値はエラー |
uint32_t mbedtls_ssl_get_verify_result(const mbedtls_ssl_context *ssl)
項目1 |
項目2 |
説明 |
機能 |
|
TLSのハンドシェイクの実施結果を取得する |
引数 |
ssl |
設定を反映させるmbedTLSのコンテキスト情報 |
返り値 |
|
正常値は0。それ以外の値はエラー |
通信
送受信の際のコードについて解説します。
HTTPのGETリクエストを送る例を下記に示します。データの通信に関しては、初期化とハンドシェイクが正常終了した際に使われていたmbedtls_ssl_context型の変数が必要になります。
#define HTTP_GET_REQUEST "GET / HTTP/1.0\r\n\r\n"
int mbedTLS_readWrite(mbedtls_ssl_context* ssl)
{
int len;
uint8_t buf[1024];
int ret;
snprintf((char *)buf, sizeof(buf) / sizeof(char), HTTP_GET_REQUEST);
len = strlen((char *) buf);
while ((ret = mbedtls_ssl_write(ssl, buf, len)) <= 0)
{
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
{
return ret; /* error */
}
}
do
{
len = sizeof(buf) - 1;
memset(buf, 0, sizeof(buf));
ret = mbedtls_ssl_read(ssl, buf, len);
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
{
continue;
}
if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)
{
break;
}
if (ret < 0)
{
break;
}
if (ret == 0)
{
break;
}
}
while(1);
return ret;
}
使用しているAPIを順に説明していきます。
int mbedtls_ssl_write(mbedtls_ssl_context *ssl, const unsigned char *buf, size_t len)
項目1 |
項目2 |
説明 |
機能 |
|
データの送信を行う |
引数 |
ssl |
mbedTLSのコンテキスト情報 |
|
buf |
送信データ |
|
len |
送信データサイズ |
返り値 |
|
0以上は送信データサイズ。それ以外のデータはエラー値 |
int mbedtls_ssl_read(mbedtls_ssl_context *ssl, unsigned char *buf, size_t len)
項目1 |
項目2 |
説明 |
機能 |
|
データの受信を行う |
引数 |
ssl |
mbedTLSのコンテキスト情報 |
|
buf |
受信データ |
|
len |
受信データサイズ |
返り値 |
|
0以上は受信データサイズ。それ以外のデータはエラー値 |