mbedTLS 3.1.0 TLS 1.3 simple sample

元のssl_client2がTLS12, 13, DTLSが混じって分りにくかったので、簡単なサンプル。セッションの保存は行わない。
Root CAは組込みでファイルシステムが無い場合があるので、PEMとしてdefineして使う。とりあえず、event driven&timer制御での非同期性はオフにしたシンプルな感じで。configでMBEDTLS_SSL_PROTO_TLS1_3を有効にしたり、TLS 1.3周りの設定をする必要あり。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "mbedtls/platform.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/hmac_drbg.h"
#include "mbedtls/x509.h"
#include "mbedtls/error.h"
#include "mbedtls/debug.h"
#include "mbedtls/timing.h"
#include "mbedtls/base64.h"

#define BUFFER_SIZE   20000
#define SERVER_NAME    "www.google.com"
#define SERVER_PORT    "443"

#define GTSR1_CA_PEM                                              \
"-----BEGIN CERTIFICATE-----\r\n"  \
"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\r\n"  \
"bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c\r\n"  \
"-----END CERTIFICATE-----"
const unsigned char gtsr1_pem[] = GTSR1_CA_PEM;

typedef struct {
    mbedtls_ssl_context *ssl;
    mbedtls_net_context *net;
} io_ctx_t;

typedef struct {
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context drbg;
} rng_context_t;

void debug( void *ctx, int level, const char *file, int line, const char *str ) {
    const char *p, *basename;
    for( p = basename = file; *p != '\0'; p++ )
        if( *p == '/' || *p == '\\' )
            basename = p + 1;

    mbedtls_fprintf( (FILE *) ctx, "%s:%04d: |%d| %s",
                     basename, line, level, str );
    fflush( (FILE *) ctx  );
}

int rng_get( void *p_rng, unsigned char *output, size_t output_len ) {
    rng_context_t *rng = p_rng;
    return( mbedtls_ctr_drbg_random( &rng->drbg, output, output_len ) );
}

int send_cb( void *ctx, unsigned char const *buf, size_t len ) {
    io_ctx_t *io_ctx = (io_ctx_t*) ctx;
    return( mbedtls_net_send( io_ctx->net, buf, len ) );
}

int recv_cb( void *ctx, unsigned char *buf, size_t len ) {
    io_ctx_t *io_ctx = (io_ctx_t*) ctx;
    size_t recv_len;
    int ret;

    ret = mbedtls_net_recv( io_ctx->net, buf, len );
    if( ret < 0 )
        return( ret );
    recv_len = (size_t) ret;

    return( (int) recv_len );
}

int main() {
    int ret = 0, len, written, frags;
    mbedtls_net_context server_fd;
    io_ctx_t io_ctx;
    unsigned char buf[BUFFER_SIZE + 1];
    const char *pers = "ssl_client";

    rng_context_t rng;
    mbedtls_ssl_context ssl;
    mbedtls_ssl_config conf;
    uint32_t flags;
    mbedtls_x509_crt cacert;

    /*
     * Make sure memory references are valid.
     */
    mbedtls_net_init( &server_fd );
    mbedtls_ssl_init( &ssl );
    mbedtls_ssl_config_init( &conf );
    mbedtls_ctr_drbg_init( &rng.drbg );
    mbedtls_entropy_init( &rng.entropy );
    mbedtls_x509_crt_init( &cacert );

    mbedtls_debug_set_threshold( 3 );

    /*
     * 0. Initialize the RNG and the session data
     */
    mbedtls_printf( "\n  . Seeding the random number generator..." );
    fflush( stdout );
    ret = mbedtls_ctr_drbg_seed( &rng.drbg, mbedtls_entropy_func, &rng.entropy,
                                     (const unsigned char *) pers,
                                     strlen( pers ) );
    if( ret != 0 )
        mbedtls_exit( ret );
    mbedtls_printf( " ok\n" );

    /*
     * 1. Load the trusted CA
     */
    mbedtls_printf( "  . Loading the CA root certificate ..." );
    fflush( stdout );

    ret = mbedtls_x509_crt_parse( &cacert, gtsr1_pem, sizeof(gtsr1_pem) );
    if( ret < 0 ) {
        mbedtls_printf( " failed\n  !  mbedtls_x509_crt_parse returned -0x%x\n\n",
                        (unsigned int) -ret );
        mbedtls_exit( ret );
    }

    /*
     * 2. Setup stuff
     */
    mbedtls_printf( "  . Setting up the SSL/TLS structure..." );
    fflush( stdout );

    if( ( ret = mbedtls_ssl_config_defaults( &conf,
                    MBEDTLS_SSL_IS_CLIENT,
                    MBEDTLS_SSL_TRANSPORT_STREAM,
                    MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 ) {
        mbedtls_printf( " failed\n  ! mbedtls_ssl_config_defaults returned -0x%x\n\n", (unsigned int) -ret );
        mbedtls_exit( ret );
    }

    if( ( ret = mbedtls_ssl_conf_max_frag_len( &conf, MBEDTLS_SSL_MAX_FRAG_LEN_NONE ) ) != 0 ) {
        mbedtls_printf( " failed\n  ! mbedtls_ssl_conf_max_frag_len returned %d\n\n",
                        ret );
        mbedtls_exit( ret );
    }

    mbedtls_ssl_conf_rng( &conf, rng_get, &rng );
    mbedtls_ssl_conf_dbg( &conf, debug, stdout );

    mbedtls_ssl_conf_read_timeout( &conf, 0 );
    mbedtls_ssl_conf_session_tickets( &conf, MBEDTLS_SSL_SESSION_TICKETS_ENABLED );
    mbedtls_ssl_conf_tls13_key_exchange_modes( &conf, MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_ALL );
    mbedtls_ssl_conf_renegotiation( &conf, MBEDTLS_SSL_RENEGOTIATION_DISABLED );
    mbedtls_ssl_conf_ca_chain( &conf, &cacert, NULL );

    mbedtls_ssl_conf_min_version( &conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_4 );
    mbedtls_ssl_conf_max_version( &conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_4 );

    if( ( ret = mbedtls_ssl_setup( &ssl, &conf ) ) != 0 ) {
        mbedtls_printf( " failed\n  ! mbedtls_ssl_setup returned -0x%x\n\n",
                        (unsigned int) -ret );
        mbedtls_exit( ret );
    }

    if( ( ret = mbedtls_ssl_set_hostname( &ssl, SERVER_NAME ) ) != 0 ) {
        mbedtls_printf( " failed\n  ! mbedtls_ssl_set_hostname returned %d\n\n", ret );
        mbedtls_exit( ret );
    }

    io_ctx.ssl = &ssl;
    io_ctx.net = &server_fd;
    mbedtls_ssl_set_bio( &ssl, &io_ctx, send_cb, recv_cb, NULL);

    mbedtls_printf( " ok\n" );

    /*
     * 3. Start the connection
     */
    if( ( ret = mbedtls_net_connect( &server_fd,
                       SERVER_NAME, SERVER_PORT, MBEDTLS_NET_PROTO_TCP) ) != 0 ) {
        mbedtls_printf( " failed\n  ! mbedtls_net_connect returned -0x%x\n\n", (unsigned int) -ret );
        mbedtls_exit( ret );
    }

    ret = mbedtls_net_set_block( &server_fd );
    if ( ret != 0 ) {
        mbedtls_printf( " failed\n  ! net_set_(non)block() returned -0x%x\n\n", (unsigned int) -ret );
        mbedtls_exit( ret );
    }
    mbedtls_printf( " ok\n" );

    /*
     * 4. Handshake
     */
    mbedtls_printf( "  . Performing the SSL/TLS handshake..." );
    fflush( stdout );

    while( ( ret = mbedtls_ssl_handshake( &ssl ) ) != 0 ) {
        if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
            ret != MBEDTLS_ERR_SSL_WANT_WRITE &&
            ret != MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS ) {
            mbedtls_printf( " failed\n  ! mbedtls_ssl_handshake returned -0x%x\n", (unsigned int) -ret );
            mbedtls_exit( ret );
        }
    }

    mbedtls_printf( " ok\n    [ Protocol is %s ]\n    [ Ciphersuite is %s ]\n",
                    mbedtls_ssl_get_version( &ssl ),
                    mbedtls_ssl_get_ciphersuite( &ssl ) );
    /*
     * 5. Verify the server certificate
     */
    mbedtls_printf( "  . Verifying peer X.509 certificate..." );
    if( ( flags = mbedtls_ssl_get_verify_result( &ssl ) ) != 0 )
        mbedtls_printf( " server sertificate failed\n" );
    else
        mbedtls_printf( " ok\n" );
    
    /*
     * 6. Write the GET request
     */
    len = mbedtls_snprintf( (char *) buf, sizeof( buf ) - 1, "GET / HTTP/1.0\r\nExtra-header: \r\n\r\n" );
    written = 0;
    frags = 0;

    do {
        while( ( ret = mbedtls_ssl_write( &ssl, buf + written,
                                            len - written ) ) < 0 ) {
            if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
                ret != MBEDTLS_ERR_SSL_WANT_WRITE &&
                ret != MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS ) {
                mbedtls_printf( " failed\n  ! mbedtls_ssl_write returned -0x%x\n\n",
                                (unsigned int) -ret );
                mbedtls_exit( ret );
            }
        }
        frags++;
        written += ret;
    } while( written < len );

    buf[written] = '\0';
    mbedtls_printf( " %d bytes written in %d fragments\n\n%s\n", written, frags, (char *) buf );

    /*
     * 7. Read the HTTP response
     */
    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 <= 0 ) {
            if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
                mbedtls_printf( " connection was closed gracefully\n" );
                ret = 0;
                break;
            } else {
                mbedtls_printf( " mbedtls_ssl_read returned -0x%x\n",
                                (unsigned int) -ret );
                mbedtls_exit( ret );
            }
        }

        len = ret;
        buf[len] = '\0';
        mbedtls_printf( " %d bytes read\n\n%s", len, (char *) buf );

        if( ret > 0 && buf[len-1] == '\n' ) {
            ret = 0;
            break;
        }
    } while( 1 );

    do ret = mbedtls_ssl_close_notify( &ssl );
    while( ret == MBEDTLS_ERR_SSL_WANT_WRITE );

    mbedtls_net_free( &server_fd );
    mbedtls_ssl_free( &ssl );
    mbedtls_ssl_config_free( &conf );
    mbedtls_x509_crt_free( &cacert );
    mbedtls_ctr_drbg_free( &rng.drbg );
    mbedtls_entropy_free( &rng.entropy );

    mbedtls_exit( ret );
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください