元の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 );
}
