0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

mbedTLSのコーディング

Posted at

こちらの記事の続きになります。
上記のリンク先の記事では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以上は受信データサイズ。それ以外のデータはエラー値
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?